| | | 1 | | using pva.SuperV.Engine.Exceptions; |
| | | 2 | | using pva.SuperV.Engine.FieldFormatters; |
| | | 3 | | using pva.SuperV.Engine.Processing; |
| | | 4 | | using System.Globalization; |
| | | 5 | | using System.Text; |
| | | 6 | | using System.Text.Json.Serialization; |
| | | 7 | | using System.Threading.Channels; |
| | | 8 | | |
| | | 9 | | namespace pva.SuperV.Engine |
| | | 10 | | { |
| | | 11 | | /// <summary> |
| | | 12 | | /// Definition of a field used in a <see cref="Class"/>. |
| | | 13 | | /// </summary> |
| | | 14 | | /// <typeparam name="T">Type of field</typeparam> |
| | | 15 | | /// <seealso cref="pva.SuperV.Engine.IFieldDefinition" /> |
| | | 16 | | public class FieldDefinition<T> : IFieldDefinition |
| | | 17 | | { |
| | | 18 | | /// <summary> |
| | | 19 | | /// The name of the field. Uses <see cref="Name"/> to access value. |
| | | 20 | | /// </summary> |
| | | 21 | | private string _name; |
| | | 22 | | |
| | | 23 | | /// <summary> |
| | | 24 | | /// Gets or sets the name of the field. |
| | | 25 | | /// </summary> |
| | | 26 | | /// <value> |
| | | 27 | | /// The name. |
| | | 28 | | /// </value> |
| | | 29 | | public string Name |
| | | 30 | | { |
| | 24973 | 31 | | get => _name; |
| | | 32 | | set |
| | 4634 | 33 | | { |
| | 4634 | 34 | | _name = IdentifierValidation.ValidateIdentifier("field", value); |
| | 4631 | 35 | | } |
| | | 36 | | } |
| | | 37 | | |
| | | 38 | | /// <summary> |
| | | 39 | | /// Gets or sets the type of the field. |
| | | 40 | | /// </summary> |
| | | 41 | | /// <value> |
| | | 42 | | /// The type. |
| | | 43 | | /// </value> |
| | 8756 | 44 | | public Type Type { get; init; } |
| | | 45 | | |
| | | 46 | | /// <summary> |
| | | 47 | | /// Gets or sets the formatter associated with field. |
| | | 48 | | /// </summary> |
| | | 49 | | /// <value> |
| | | 50 | | /// The formatter. |
| | | 51 | | /// </value> |
| | 6614 | 52 | | public FieldFormatter? Formatter { get; set; } |
| | | 53 | | |
| | | 54 | | /// <summary> |
| | | 55 | | /// Gets or sets the default value for fields in instances. |
| | | 56 | | /// </summary> |
| | | 57 | | /// <value> |
| | | 58 | | /// The default value. |
| | | 59 | | /// </value> |
| | 8604 | 60 | | public T? DefaultValue { get; set; } |
| | | 61 | | |
| | | 62 | | /// <summary> |
| | | 63 | | /// Gets or sets the name of the topic to trigger script(s) when the value changes. |
| | | 64 | | /// </summary> |
| | | 65 | | /// <value> |
| | | 66 | | /// The name of the topic. |
| | | 67 | | /// </value> |
| | 10924 | 68 | | public string? TopicName { get; set; } |
| | | 69 | | |
| | | 70 | | /// <summary> |
| | | 71 | | /// Gets or sets the field value changed event channel. |
| | | 72 | | /// </summary> |
| | | 73 | | /// <value> |
| | | 74 | | /// The field value changed event. |
| | | 75 | | /// </value> |
| | | 76 | | [JsonIgnore] |
| | 2327 | 77 | | public Channel<FieldValueChangedEvent>? FieldValueChangedEventChannel { get; set; } |
| | | 78 | | |
| | | 79 | | /// <summary> |
| | | 80 | | /// Gets or sets the value post change processings. |
| | | 81 | | /// </summary> |
| | | 82 | | /// <value> |
| | | 83 | | /// The value post change processings. |
| | | 84 | | /// </value> |
| | 28374 | 85 | | public List<IFieldValueProcessing> ValuePostChangeProcessings { get; set; } = []; |
| | | 86 | | |
| | | 87 | | /// <summary> |
| | | 88 | | /// Initializes a new instance of the <see cref="FieldDefinition{T}"/> class with the initial field value set to |
| | | 89 | | /// </summary> |
| | | 90 | | /// <param name="name">The name of the field.</param> |
| | 109 | 91 | | public FieldDefinition(string name) : this(name, default, "") |
| | 109 | 92 | | { |
| | 109 | 93 | | } |
| | | 94 | | |
| | | 95 | | /// <summary> |
| | | 96 | | /// Initializes a new instance of the <see cref="FieldDefinition{T}"/> class. |
| | | 97 | | /// </summary> |
| | | 98 | | /// <param name="name">The name of the field.</param> |
| | | 99 | | /// <param name="defaultValue">The default initial value for instances field.</param> |
| | | 100 | | /// <param name="topicName">Name of topic to publish to when field value changes.Can be null if no publishing is |
| | | 101 | | #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider ad |
| | 4634 | 102 | | public FieldDefinition(string name, T? defaultValue, string? topicName = "") |
| | | 103 | | #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider ad |
| | 4634 | 104 | | { |
| | 4634 | 105 | | Name = name; |
| | 4631 | 106 | | DefaultValue = defaultValue ?? default; |
| | 4631 | 107 | | TopicName = topicName?.Trim(); |
| | 4631 | 108 | | Type = typeof(T); |
| | 4631 | 109 | | } |
| | | 110 | | |
| | | 111 | | /// <summary> |
| | | 112 | | /// Gets the C# code to create field for an <see cref="Instance"/> of a <see cref="Class"/>. |
| | | 113 | | /// </summary> |
| | | 114 | | /// <returns>C# code for field.</returns> |
| | | 115 | | public string GetCode() |
| | 2635 | 116 | | { |
| | 2635 | 117 | | StringBuilder codeBuilder = new(); |
| | 2635 | 118 | | codeBuilder.AppendLine( |
| | 2635 | 119 | | $"public Field<{typeof(T)}> {Name} {{ get; set; }} = new ({GetCodeValueForNew(DefaultValue)});"); |
| | 2635 | 120 | | return codeBuilder.ToString(); |
| | 2635 | 121 | | } |
| | | 122 | | |
| | | 123 | | private static string? GetCodeValueForNew(T? defaultValue) |
| | 2635 | 124 | | { |
| | 2635 | 125 | | if (typeof(T).Equals(typeof(string))) |
| | 234 | 126 | | { |
| | 234 | 127 | | return $"\"{defaultValue}\""; |
| | | 128 | | } |
| | | 129 | | |
| | 2401 | 130 | | return defaultValue switch |
| | 2401 | 131 | | { |
| | 118 | 132 | | bool boolValue => boolValue ? "true" : "false", |
| | 118 | 133 | | DateTime dateTimeValue => $"new {typeof(T)}({dateTimeValue.Ticks.ToString(CultureInfo.InvariantCulture)} |
| | 118 | 134 | | TimeSpan timespanValue => $"new {typeof(T)}({timespanValue.Ticks.ToString(CultureInfo.InvariantCulture)} |
| | 123 | 135 | | float floatValue => $"{floatValue.ToString(CultureInfo.InvariantCulture)}F", |
| | 123 | 136 | | double doubleValue => $"{doubleValue.ToString(CultureInfo.InvariantCulture)}", |
| | 1801 | 137 | | _ => defaultValue!.ToString(), |
| | 2401 | 138 | | }; |
| | 2635 | 139 | | } |
| | | 140 | | |
| | | 141 | | /// <summary> |
| | | 142 | | /// Clones this instance. |
| | | 143 | | /// </summary> |
| | | 144 | | /// <returns>Cloned field definition</returns> |
| | | 145 | | IFieldDefinition IFieldDefinition.Clone() |
| | 836 | 146 | | => (FieldDefinition<T>)new(Name, DefaultValue) |
| | 836 | 147 | | { |
| | 836 | 148 | | Formatter = Formatter, |
| | 836 | 149 | | TopicName = TopicName, |
| | 836 | 150 | | FieldValueChangedEventChannel = FieldValueChangedEventChannel, |
| | 836 | 151 | | ValuePostChangeProcessings = [.. ValuePostChangeProcessings] |
| | 836 | 152 | | }; |
| | | 153 | | |
| | | 154 | | /// <summary> |
| | | 155 | | /// Update field from another field. |
| | | 156 | | /// </summary> |
| | | 157 | | /// <param name="fieldDefinitionUpdate">Field from which to update. Only default value and formatter are copied. |
| | | 158 | | /// <param name="fieldFormatter">Formatter</param> |
| | | 159 | | /// <exception cref="WrongFieldTypeException"></exception> |
| | | 160 | | public void Update(IFieldDefinition fieldDefinitionUpdate, FieldFormatter? fieldFormatter) |
| | 4 | 161 | | { |
| | 4 | 162 | | if (fieldDefinitionUpdate is FieldDefinition<T> typedFieldDefinitionUpdate) |
| | 2 | 163 | | { |
| | 2 | 164 | | DefaultValue = typedFieldDefinitionUpdate.DefaultValue; |
| | 2 | 165 | | Formatter = fieldFormatter; |
| | 2 | 166 | | TopicName = fieldDefinitionUpdate.TopicName; |
| | 2 | 167 | | return; |
| | | 168 | | } |
| | 2 | 169 | | throw new WrongFieldTypeException(Name, Type, fieldDefinitionUpdate.Type); |
| | 2 | 170 | | } |
| | | 171 | | } |
| | | 172 | | } |