| | 1 | | using System.Collections; |
| | 2 | | using System.Diagnostics.CodeAnalysis; |
| | 3 | | using System.Reflection; |
| | 4 | | using System.Runtime.CompilerServices; |
| | 5 | | using System.Text.Json.Serialization; |
| | 6 | | using Spdx3.Exceptions; |
| | 7 | | using Spdx3.Serialization; |
| | 8 | | using Spdx3.Utility; |
| | 9 | |
|
| | 10 | | namespace Spdx3.Model; |
| | 11 | |
|
| | 12 | | /// <summary> |
| | 13 | | /// This is a base class for like EVERYTHING in an SPDX document, whether it's an "Element" or some other "Non-Eleme |
| | 14 | | /// class. |
| | 15 | | /// See https://spdx.github.io/spdx-spec/v3.0.1/annexes/rdf-model/ |
| | 16 | | /// </summary> |
| | 17 | | public abstract class BaseModelClass |
| | 18 | | { |
| | 19 | | [JsonPropertyName("type")] |
| | 20 | | [JsonConverter(typeof(SpdxModelConverterFactory))] |
| 2856 | 21 | | public required string Type { get; set; } |
| | 22 | |
|
| | 23 | | [JsonPropertyName("spdxId")] |
| | 24 | | [JsonConverter(typeof(SpdxModelConverterFactory))] |
| 4398 | 25 | | public required Uri SpdxId { get; set; } |
| | 26 | |
|
| | 27 | | // A back-link to the catalog that was used to created this item and now contains it |
| 2497 | 28 | | public Catalog Catalog { get; internal set; } |
| | 29 | |
|
| | 30 | | // protected internal no-parm constructor required for deserialization |
| 912 | 31 | | protected internal BaseModelClass() |
| | 32 | | { |
| 912 | 33 | | } |
| | 34 | |
|
| | 35 | | [SetsRequiredMembers] |
| 1050 | 36 | | protected BaseModelClass(Catalog catalog) |
| | 37 | | { |
| 1050 | 38 | | Catalog = catalog; |
| 1050 | 39 | | Type = Naming.SpdxTypeForClass(GetType()); |
| 1050 | 40 | | SpdxId = catalog.NewId(GetType()); |
| 1050 | 41 | | catalog.Items[SpdxId] = this; |
| 1050 | 42 | | } |
| | 43 | |
|
| | 44 | |
|
| | 45 | | public virtual void Validate() |
| | 46 | | { |
| 191 | 47 | | ValidateRequiredProperty(nameof(SpdxId)); |
| 157 | 48 | | ValidateRequiredProperty(nameof(Type)); |
| 154 | 49 | | } |
| | 50 | |
|
| | 51 | | protected void ValidateRequiredProperty(string propertyName) |
| | 52 | | { |
| 14244 | 53 | | var props = GetType().GetProperties().Where(p => p.Name == propertyName).ToList(); |
| 759 | 54 | | PropertyInfo? propertyInfo = null; |
| | 55 | |
|
| 759 | 56 | | if (props.Count > 1) |
| | 57 | | { |
| 9 | 58 | | propertyInfo = props.Single(p => p.GetCustomAttribute<RequiredMemberAttribute>() != null); |
| | 59 | | } |
| | 60 | | else |
| | 61 | | { |
| 756 | 62 | | propertyInfo = props.FirstOrDefault(); |
| | 63 | | } |
| | 64 | |
|
| 759 | 65 | | if (propertyInfo == null) |
| | 66 | | { |
| 1 | 67 | | throw new Spdx3ValidationException(this, $"'{propertyName}'", "No such property exists"); |
| | 68 | | } |
| | 69 | |
|
| 758 | 70 | | var propVal = propertyInfo.GetValue(this); |
| | 71 | |
|
| 758 | 72 | | switch (propVal) |
| | 73 | | { |
| | 74 | | case null: |
| 42 | 75 | | throw new Spdx3ValidationException(this, propertyName, "Field is required"); |
| 316 | 76 | | case string when propVal.ToString() == string.Empty: |
| 8 | 77 | | throw new Spdx3ValidationException(this, propertyName, "String field is empty"); |
| | 78 | | case IList { Count: 0 }: |
| 1 | 79 | | throw new Spdx3ValidationException(this, propertyName, "List is empty"); |
| | 80 | | } |
| 704 | 81 | | } |
| | 82 | | } |