| | | 1 | | using pva.Helpers.Extensions; |
| | | 2 | | using pva.SuperV.Engine.Exceptions; |
| | | 3 | | using pva.SuperV.Engine.FieldFormatters; |
| | | 4 | | using pva.SuperV.Engine.Processing; |
| | | 5 | | using System.Text; |
| | | 6 | | using System.Text.Json.Serialization; |
| | | 7 | | |
| | | 8 | | namespace pva.SuperV.Engine |
| | | 9 | | { |
| | | 10 | | /// <summary>Dynamic class of a <see cref="Project"/>. It contains dynamic fields on which processing can be defined |
| | | 11 | | public class Class |
| | | 12 | | { |
| | | 13 | | /// <summary> |
| | | 14 | | /// Name of class. Access done through <see cref="Name"/>. |
| | | 15 | | /// </summary> |
| | 642 | 16 | | private string _name = string.Empty; |
| | | 17 | | |
| | | 18 | | /// <summary>Gets or sets the name of the class.</summary> |
| | | 19 | | public string Name |
| | | 20 | | { |
| | 4869 | 21 | | get { return _name; } |
| | | 22 | | set |
| | 642 | 23 | | { |
| | 642 | 24 | | IdentifierValidation.ValidateIdentifier("class", value); |
| | 639 | 25 | | _name = value; |
| | 639 | 26 | | } |
| | | 27 | | } |
| | | 28 | | |
| | | 29 | | /// <summary> |
| | | 30 | | /// Gets or sets the base class. |
| | | 31 | | /// </summary> |
| | | 32 | | /// <value> |
| | | 33 | | /// The base class. |
| | | 34 | | /// </value> |
| | | 35 | | [JsonIgnore] |
| | 1295 | 36 | | public Class? BaseClass { get; set; } |
| | | 37 | | |
| | | 38 | | /// <summary> |
| | | 39 | | /// Gets or sets the name of the base class. |
| | | 40 | | /// </summary> |
| | | 41 | | /// <value> |
| | | 42 | | /// The name of the base class. |
| | | 43 | | /// </value> |
| | 819 | 44 | | public string? BaseClassName { get; set; } |
| | | 45 | | |
| | | 46 | | /// <summary>Gets the fields defining the class.</summary> |
| | 12073 | 47 | | public Dictionary<string, IFieldDefinition> FieldDefinitions { get; set; } = new(StringComparer.OrdinalIgnoreCas |
| | | 48 | | |
| | | 49 | | /// <summary> |
| | | 50 | | /// Initializes a new instance of the <see cref="Class"/> class. Used by JSON deserialization. |
| | | 51 | | /// </summary> |
| | 16 | 52 | | public Class() |
| | 16 | 53 | | { |
| | 16 | 54 | | } |
| | | 55 | | |
| | | 56 | | /// <summary> |
| | | 57 | | /// Initializes a new instance of the <see cref="Class"/> class. |
| | | 58 | | /// </summary> |
| | | 59 | | /// <param name="className">Name of the class.</param> |
| | 370 | 60 | | public Class(string className) : this(className, null) |
| | 367 | 61 | | { |
| | 367 | 62 | | } |
| | | 63 | | |
| | | 64 | | /// <summary> |
| | | 65 | | /// Initializes a new instance of the <see cref="Class"/> class with inheritance from a base class. |
| | | 66 | | /// </summary> |
| | | 67 | | /// <param name="className">Name of the class.</param> |
| | | 68 | | /// <param name="baseClass">Base class.</param> |
| | 626 | 69 | | public Class(string className, Class? baseClass) |
| | 626 | 70 | | { |
| | 626 | 71 | | this.Name = className; |
| | 623 | 72 | | this.BaseClass = baseClass; |
| | 623 | 73 | | this.BaseClassName = baseClass?.Name; |
| | 623 | 74 | | } |
| | | 75 | | |
| | | 76 | | /// <summary> |
| | | 77 | | /// Adds a field definition to the class. |
| | | 78 | | /// </summary> |
| | | 79 | | /// <typeparam name="T"></typeparam> |
| | | 80 | | /// <param name="field">The field.</param> |
| | | 81 | | /// <returns>The field once it has been added.</returns> |
| | | 82 | | public IFieldDefinition AddField(IFieldDefinition field) |
| | 2663 | 83 | | { |
| | 2663 | 84 | | return AddField(field, null); |
| | 2662 | 85 | | } |
| | | 86 | | |
| | | 87 | | /// <summary> |
| | | 88 | | /// Adds a field with a field formatter. |
| | | 89 | | /// </summary> |
| | | 90 | | /// <typeparam name="T"></typeparam> |
| | | 91 | | /// <param name="field">The <see cref="FieldDefinition{T}"/> to be added.</param> |
| | | 92 | | /// <param name="formatter">The formatter to be used when using ToString()."/>.</param> |
| | | 93 | | /// <returns></returns> |
| | | 94 | | /// <exception cref="pva.SuperV.Engine.Exceptions.EntityAlreadyExistException"></exception> |
| | | 95 | | public IFieldDefinition AddField(IFieldDefinition field, FieldFormatter? formatter) |
| | 2986 | 96 | | { |
| | 2986 | 97 | | if (FieldDefinitions.ContainsKey(field.Name)) |
| | 1 | 98 | | { |
| | 1 | 99 | | throw new EntityAlreadyExistException("Field", field.Name); |
| | | 100 | | } |
| | 2985 | 101 | | field.Formatter = formatter; |
| | 2985 | 102 | | FieldDefinitions.Add(field.Name, field); |
| | 2985 | 103 | | return field; |
| | 2985 | 104 | | } |
| | | 105 | | |
| | | 106 | | public IFieldDefinition UpdateField(string fieldName, IFieldDefinition fieldDefinition, FieldFormatter? fieldFor |
| | 4 | 107 | | { |
| | 4 | 108 | | if (FieldDefinitions.TryGetValue(fieldName, out IFieldDefinition? fieldToUpdate)) |
| | 4 | 109 | | { |
| | 4 | 110 | | fieldToUpdate.Update(fieldDefinition, fieldFormatter); |
| | 2 | 111 | | return fieldToUpdate; |
| | | 112 | | } |
| | 0 | 113 | | throw new UnknownEntityException("Field", fieldName); |
| | 2 | 114 | | } |
| | | 115 | | |
| | | 116 | | /// <summary> |
| | | 117 | | /// Gets a field. |
| | | 118 | | /// </summary> |
| | | 119 | | /// <param name="fieldName">Name of the field to be retrieved.</param> |
| | | 120 | | /// <returns>The field</returns> |
| | | 121 | | /// <exception cref="pva.SuperV.Engine.Exceptions.UnknownEntityException"></exception> |
| | | 122 | | public IFieldDefinition GetField(string fieldName) |
| | 3646 | 123 | | { |
| | 3646 | 124 | | if (FieldDefinitions.TryGetValue(fieldName, out IFieldDefinition? fieldDefinition)) |
| | 3645 | 125 | | { |
| | 3645 | 126 | | return fieldDefinition; |
| | | 127 | | } |
| | 1 | 128 | | if (BaseClass is not null) |
| | 0 | 129 | | { |
| | 0 | 130 | | return BaseClass.GetField(fieldName); |
| | | 131 | | } |
| | 1 | 132 | | throw new UnknownEntityException("Field", fieldName); |
| | 3645 | 133 | | } |
| | | 134 | | |
| | | 135 | | /// <summary> |
| | | 136 | | /// Gets a field of a specific type. |
| | | 137 | | /// </summary> |
| | | 138 | | /// <typeparam name="T"></typeparam> |
| | | 139 | | /// <param name="fieldName">Name of the field to be retrieved.</param> |
| | | 140 | | /// <returns>The field</returns> |
| | | 141 | | /// <exception cref="pva.SuperV.Engine.Exceptions.WrongFieldTypeException"></exception> |
| | | 142 | | /// <exception cref="pva.SuperV.Engine.Exceptions.UnknownEntityException"></exception> |
| | | 143 | | public FieldDefinition<T>? GetField<T>(string fieldName) |
| | 1224 | 144 | | { |
| | 1224 | 145 | | IFieldDefinition fieldDefinition = GetField(fieldName); |
| | 1223 | 146 | | if (fieldDefinition.Type == typeof(T)) |
| | 1223 | 147 | | { |
| | 1223 | 148 | | return fieldDefinition as FieldDefinition<T>; |
| | | 149 | | } |
| | 0 | 150 | | throw new WrongFieldTypeException(fieldName, typeof(T), fieldDefinition.Type); |
| | 1223 | 151 | | } |
| | | 152 | | |
| | | 153 | | /// <summary> |
| | | 154 | | /// Removes a field. |
| | | 155 | | /// </summary> |
| | | 156 | | /// <param name="fieldName">Name of the field to be removed.</param> |
| | | 157 | | public void RemoveField(string fieldName) |
| | 3 | 158 | | { |
| | 3 | 159 | | VerifyFieldNotUsedInProcessings(fieldName); |
| | 2 | 160 | | FieldDefinitions.Remove(fieldName); |
| | 2 | 161 | | } |
| | | 162 | | |
| | | 163 | | internal void AddFieldChangePostProcessing(string fieldName, IFieldValueProcessing fieldValueProcessing) |
| | 440 | 164 | | { |
| | 440 | 165 | | IFieldDefinition? fieldDefinition = GetField(fieldName); |
| | 440 | 166 | | fieldDefinition!.ValuePostChangeProcessings.Add(fieldValueProcessing!); |
| | 440 | 167 | | } |
| | | 168 | | |
| | | 169 | | internal void UpdateFieldChangePostProcessing(string fieldName, string processingName, IFieldValueProcessing fie |
| | 1 | 170 | | { |
| | 1 | 171 | | IFieldDefinition? fieldDefinition = GetField(fieldName); |
| | 1 | 172 | | IFieldValueProcessing? fieldValueProcessingToUpdate = fieldDefinition?.ValuePostChangeProcessings |
| | 4 | 173 | | .FirstOrDefault(valueProcessing => valueProcessing.Name == processingName); |
| | 1 | 174 | | if (fieldValueProcessingToUpdate != null) |
| | 1 | 175 | | { |
| | 1 | 176 | | fieldDefinition!.ValuePostChangeProcessings.Remove(fieldValueProcessingToUpdate); |
| | 1 | 177 | | fieldDefinition!.ValuePostChangeProcessings.Add(fieldProcessing); |
| | 1 | 178 | | return; |
| | | 179 | | } |
| | 0 | 180 | | throw new UnknownEntityException("Field processing", processingName); |
| | 1 | 181 | | } |
| | | 182 | | |
| | | 183 | | internal void RemoveFieldChangePostProcessing(string fieldName, string processingName) |
| | 1 | 184 | | { |
| | 1 | 185 | | if (FieldDefinitions.TryGetValue(fieldName, out IFieldDefinition? fieldDefinition)) |
| | 1 | 186 | | { |
| | 1 | 187 | | IFieldValueProcessing? processing = fieldDefinition.ValuePostChangeProcessings |
| | 2 | 188 | | .FirstOrDefault(fieldProcessing => fieldProcessing.Name.Equals(processingName)); |
| | 1 | 189 | | if (processing is not null) |
| | 1 | 190 | | { |
| | 1 | 191 | | fieldDefinition.ValuePostChangeProcessings.Remove(processing); |
| | 1 | 192 | | return; |
| | | 193 | | } |
| | 0 | 194 | | throw new UnknownEntityException("Field processing", processingName); |
| | | 195 | | } |
| | 0 | 196 | | throw new UnknownEntityException("Field", fieldName); |
| | 1 | 197 | | } |
| | | 198 | | |
| | | 199 | | private void VerifyFieldNotUsedInProcessings(string fieldName) |
| | 3 | 200 | | { |
| | 3 | 201 | | List<String> fieldsUsedInProcessing = [.. FieldDefinitions.Values |
| | 5 | 202 | | .Where(field => !field.Name.Equals(fieldName) && field.ValuePostChangeProcessings.Any(valueProcessin |
| | 4 | 203 | | .Select(field => field.Name)]; |
| | 3 | 204 | | if (fieldsUsedInProcessing.Count > 0) |
| | 1 | 205 | | { |
| | 1 | 206 | | throw new EntityInUseException("Field", fieldName, Name, fieldsUsedInProcessing); |
| | | 207 | | } |
| | 2 | 208 | | } |
| | | 209 | | |
| | | 210 | | /// <summary> |
| | | 211 | | /// Gets the C# code for the class. |
| | | 212 | | /// </summary> |
| | | 213 | | /// <returns>C# code of class</returns> |
| | | 214 | | internal string GetCode() |
| | 339 | 215 | | { |
| | 339 | 216 | | StringBuilder codeBuilder = new(); |
| | 339 | 217 | | StringBuilder ctorBuilder = new($"public {Name}() {{"); |
| | 339 | 218 | | string baseClass = BaseClass is null ? "Instance" : BaseClass.Name; |
| | 339 | 219 | | codeBuilder.AppendLine($"public class {Name} : {baseClass} {{"); |
| | 339 | 220 | | FieldDefinitions |
| | 339 | 221 | | .ForEach((k, v) => |
| | 2192 | 222 | | { |
| | 2192 | 223 | | codeBuilder.AppendLine(v.GetCode()); |
| | 2192 | 224 | | ctorBuilder.AppendFormat("Fields.Add(\"{0}\", {0});{0}.Instance = this;", k).AppendLine(); |
| | 2531 | 225 | | }); |
| | 339 | 226 | | ctorBuilder.AppendLine("}"); |
| | 339 | 227 | | codeBuilder.AppendLine(ctorBuilder.ToString()); |
| | 339 | 228 | | codeBuilder.AppendLine("}"); |
| | 339 | 229 | | return codeBuilder.ToString(); |
| | 339 | 230 | | } |
| | | 231 | | |
| | | 232 | | /// <summary> |
| | | 233 | | /// Clones this instance. |
| | | 234 | | /// </summary> |
| | | 235 | | /// <returns>A new <see cref="Class"/> clone of class instance.</returns> |
| | | 236 | | internal Class Clone() |
| | 108 | 237 | | { |
| | 108 | 238 | | var clazz = new Class(Name, BaseClass); |
| | 108 | 239 | | FieldDefinitions |
| | 828 | 240 | | .ForEach((k, v) => clazz.FieldDefinitions.Add(k, v.Clone())); |
| | 108 | 241 | | return clazz; |
| | 108 | 242 | | } |
| | | 243 | | } |
| | | 244 | | } |