diff --git a/Px.Utils.UnitTests/ModelBuilderTests/ValueParserUtilitiesTests/StringToDimensionTypeTests.cs b/Px.Utils.UnitTests/ModelBuilderTests/ValueParserUtilitiesTests/StringToDimensionTypeTests.cs index c059b6d..85b97de 100644 --- a/Px.Utils.UnitTests/ModelBuilderTests/ValueParserUtilitiesTests/StringToDimensionTypeTests.cs +++ b/Px.Utils.UnitTests/ModelBuilderTests/ValueParserUtilitiesTests/StringToDimensionTypeTests.cs @@ -1,7 +1,9 @@ -using Px.Utils.ModelBuilders; +using Px.Utils.ModelBuilders; using Px.Utils.Models.Metadata.Enums; +using Px.Utils.PxFile; +using System.Globalization; -namespace ModelBuilderTests.ValueParserUtilitiesTests +namespace Px.Utils.UnitTests.ModelBuilderTests.ValueParserUtilitiesTests { [TestClass] public class StringToDimensionTypeTests @@ -16,14 +18,27 @@ public void TimeTest() } [TestMethod] - public void ContentTest() + public void ContentsTest() { - string input = "Content"; + string input = "Contents"; DimensionType expected = DimensionType.Content; DimensionType actual = ValueParserUtilities.StringToDimensionType(input); Assert.AreEqual(expected, actual); } + [TestMethod] + [DataRow("RANKING", DimensionType.Ordinal)] + [DataRow("region", DimensionType.Geographical)] + public void CustomAliasTest(string alias, DimensionType expected) + { + string input = alias.ToUpper(CultureInfo.InvariantCulture); // Testing case-insensitivity + PxFileConfiguration conf = PxFileConfiguration.Default; + conf.Tokens.VariableTypes.Mappings[alias] = expected; + + DimensionType actual = ValueParserUtilities.StringToDimensionType(input, conf); + Assert.AreEqual(expected, actual); + } + [TestMethod] public void DefaultValueTest() { diff --git a/Px.Utils.UnitTests/Validation/ContentValidationTests/ContentValidationTests.cs b/Px.Utils.UnitTests/Validation/ContentValidationTests/ContentValidationTests.cs index c3b3218..3fcc692 100644 --- a/Px.Utils.UnitTests/Validation/ContentValidationTests/ContentValidationTests.cs +++ b/Px.Utils.UnitTests/Validation/ContentValidationTests/ContentValidationTests.cs @@ -2,8 +2,11 @@ using Px.Utils.Validation; using Px.Utils.Validation.ContentValidation; using Px.Utils.Validation.SyntaxValidation; +using Px.Utils.PxFile; +using Px.Utils.Models.Metadata.Enums; using System.Text; using System.Reflection; +using System.Globalization; namespace Px.Utils.UnitTests.Validation.ContentValidationTests { @@ -536,6 +539,60 @@ public void ValidateValueTypesCalledWithStructuredEntryArrayWithWrongValuesRetur } } + [TestMethod] + [DataRow("ranking", DimensionType.Ordinal)] + [DataRow("REGION", DimensionType.Geographical)] + public void ValidateValueContentsCalledWithCustomDimensionTypesValueReturnsWithoutFeedback(string alias, DimensionType dimensionType) + { + // Arrange + PxFileConfiguration conf = PxFileConfiguration.Default; + conf.Tokens.VariableTypes.Mappings[alias] = dimensionType; + + ValidationStructuredEntry entry = new( + filename, + new ValidationStructuredEntryKey("VARIABLE-TYPE", "fi", "foo"), + alias.ToUpper(CultureInfo.InvariantCulture), // The value is converted to upper case to verify case insensitivity of the dimension type matching + 0, + [], + 0, + Utils.Validation.ValueType.StringValue); + + ContentValidator validator = new(filename, encoding, [entry], conf: conf); + + // Act + ValidationFeedback? result = ContentValidator.ValidateValueContents(entry, validator); + + // Assert + Assert.IsNull(result); + } + + [TestMethod] + public void ValidateValueContentsCalledWithUnknownDimensionTypeValueReturnsWithError() + { + // Arrange + PxFileConfiguration conf = PxFileConfiguration.Default; + + ValidationStructuredEntry entry = new( + filename, + new ValidationStructuredEntryKey("VARIABLE-TYPE", "fi", "foo"), + "Ranking", + 0, + [], + 0, + Utils.Validation.ValueType.StringValue); + + ContentValidator validator = new(filename, encoding, [entry], conf: conf); + + // Act + ValidationFeedback? result = ContentValidator.ValidateValueContents(entry, validator); + + // Assert + Assert.IsNotNull(result); + Assert.HasCount(1, result); + Assert.AreEqual(ValidationFeedbackRule.InvalidValueFound, result.First().Key.Rule); + Assert.AreEqual(ValidationFeedbackLevel.Error, result.First().Key.Level); + } + [TestMethod] public void ValidateValueAmountsCalledWithUnmatchingAmountOfElementsReturnsWithError() { diff --git a/Px.Utils.UnitTests/Validation/DatabaseValidation/DatabaseValidatorTests.cs b/Px.Utils.UnitTests/Validation/DatabaseValidation/DatabaseValidatorTests.cs index f9657cf..4b6e292 100644 --- a/Px.Utils.UnitTests/Validation/DatabaseValidation/DatabaseValidatorTests.cs +++ b/Px.Utils.UnitTests/Validation/DatabaseValidation/DatabaseValidatorTests.cs @@ -161,7 +161,7 @@ public async Task ValidateDatabasAsynceWithCustomFunctionsReturnsFeedback() IDatabaseValidator[] customValidators = [ new MockCustomDatabaseValidator() - ]; + ]; DatabaseValidator validator = new( "database_invalid", customPxFileValidators: customValidators, diff --git a/Px.Utils.UnitTests/Validation/Fixtures/ContentValidationFixtures.cs b/Px.Utils.UnitTests/Validation/Fixtures/ContentValidationFixtures.cs index 67d3652..eada5c9 100644 --- a/Px.Utils.UnitTests/Validation/Fixtures/ContentValidationFixtures.cs +++ b/Px.Utils.UnitTests/Validation/Fixtures/ContentValidationFixtures.cs @@ -306,7 +306,7 @@ internal static class ContentValidationFixtures private static readonly ValidationStructuredEntry variableTypeBarEntry = new(filename, new ValidationStructuredEntryKey("VARIABLE-TYPE", null, "bar"), - "Content", + "Contents", 23, [], 13, diff --git a/Px.Utils.UnitTests/Validation/Fixtures/PxFileFixtures.cs b/Px.Utils.UnitTests/Validation/Fixtures/PxFileFixtures.cs index 542a449..d28536e 100644 --- a/Px.Utils.UnitTests/Validation/Fixtures/PxFileFixtures.cs +++ b/Px.Utils.UnitTests/Validation/Fixtures/PxFileFixtures.cs @@ -1,4 +1,4 @@ -namespace Px.Utils.UnitTests.Validation.Fixtures +namespace Px.Utils.UnitTests.Validation.Fixtures { internal static class PxFileFixtures { @@ -22,7 +22,7 @@ internal static class PxFileFixtures "\r\nCODES(\"Vuosi\")=\"2015\",\"2016\",\"2017\",\"2018\",\"2019\",\"2020\",\"2021\",\"2022\",\"2023\",\"2024\";" + "\r\nCODES(\"Tiedot\")=\"code-foo-val-a\",\"code-foo-val-b\";" + "\r\nVARIABLE-TYPE(\"Vuosi\")=\"Time\";" + - "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Content\";" + + "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Contents\";" + "\r\nPRECISION(\"Tiedot\",\"foo-val-a\")=1;" + "\r\nPRECISION(\"Tiedot\",\"foo-val-b\")=1;" + "\r\nLAST-UPDATED(\"Tiedot\",\"foo-val-a\")=\"20231101 08:00\";" + @@ -64,7 +64,7 @@ internal static class PxFileFixtures "\r\nCODES(\"Vuosi\")=\"2015\",\"2016\",\"2017\",\"2018\",\"2019\",\"2020\",\"2021\",\"2022\",\"2023\",\"2024\";" + "\r\nCODES(\"Tiedot\")=\"code-foo-val-a\",\"code-foo-val-b\";" + "\r\nVARIABLE-TYPE(\"Vuosi\")=\"Time\";" + - "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Content\";" + + "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Contents\";" + "\r\nPRECISION(\"Tiedot\",\"foo-val-a\")=1;" + "\r\nPRECISION(\"Tiedot\",\"foo-val-b\")=1;" + "\r\nLAST-UPDATED(\"Tiedot\",\"foo-val-a\")=\"20231101 08:00\";" + @@ -94,7 +94,7 @@ internal static class PxFileFixtures "\r\nCODES(\"Vuosi\")=\"2015\",\"2016\",\"2017\",\"2018\",\"2019\",\"2020\",\"2021\",\"2022\",\"2023\",\"2024\";" + "\r\nCODES(\"Tiedot\")=\"code-foo-val-a\",\"code-foo-val-b\";" + "\r\nVARIABLE-TYPE(\"Vuosi\")=\"Time\";" + - "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Content\";" + + "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Contents\";" + "\r\nPRECISION(\"Tiedot\",\"foo-val-a\")=1;" + "\r\nPRECISION(\"Tiedot\",\"foo-val-b\")=1;" + "\r\nLAST-UPDATED(\"Tiedot\",\"foo-val-a\")=\"20231101 08:00\";" + @@ -135,7 +135,7 @@ internal static class PxFileFixtures "\r\nCODES(\"Vuosi\")=\"2015\",\"2016\",\"2017\",\"2018\",\"2019\",\"2020\",\"2021\",\"2022\",\"2023\",\"2024\";" + "\r\nCODES(\"Tiedot\")=\"code-foo-val-a\",\"code-foo-val-b\";" + "\r\nVARIABLE-TYPE(\"Vuosi\")=\"Time\";" + - "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Content\";" + + "\r\nVARIABLE-TYPE(\"Tiedot\")=\"Contents\";" + "\r\nPRECISION(\"Tiedot\",\"foo-val-a\")=1;" + "\r\nLAST-UPDATED(\"Tiedot\",\"foo-val-a\")=\"20231101 08:00\";" + "\r\nUNITS(\"Tiedot\",\"foo-val-a\")=\"kpl\";" + diff --git a/Px.Utils.sln b/Px.Utils.sln index 4fe9d1a..9196bd0 100644 --- a/Px.Utils.sln +++ b/Px.Utils.sln @@ -36,6 +36,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Px.Utils.TestingApp", "Px.U {D94934AF-6B41-4C66-818A-C4C33230E9DD} = {D94934AF-6B41-4C66-818A-C4C33230E9DD} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "architecture", "architecture", "{BFA8E05B-EBD8-4085-B765-EAED69DD0874}" + ProjectSection(SolutionItems) = preProject + docs\architecture.md = docs\architecture.md + docs\architecture.models.md = docs\architecture.models.md + docs\architecture.operations.md = docs\architecture.operations.md + docs\architecture.overview.md = docs\architecture.overview.md + docs\architecture.readers.md = docs\architecture.readers.md + docs\architecture.serializers.md = docs\architecture.serializers.md + docs\architecture.testing.md = docs\architecture.testing.md + docs\architecture.validation.md = docs\architecture.validation.md + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -60,6 +72,7 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {EB4119B8-8292-418A-9CD3-2905DA67B889} = {BC1C9772-9B8F-49A7-A8F3-09E3A94789ED} + {BFA8E05B-EBD8-4085-B765-EAED69DD0874} = {42DFE3AF-7507-415A-806B-99B74519BF97} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B1F81A3B-9FF5-4557-8A5A-A9519C42C2B2} diff --git a/Px.Utils/ModelBuilders/ValueParserUtilities.cs b/Px.Utils/ModelBuilders/ValueParserUtilities.cs index 33cbe84..8ff707e 100644 --- a/Px.Utils/ModelBuilders/ValueParserUtilities.cs +++ b/Px.Utils/ModelBuilders/ValueParserUtilities.cs @@ -1,6 +1,7 @@ -using Px.Utils.Models.Metadata.Enums; +using Px.Utils.Models.Metadata.Enums; using Px.Utils.Models.Metadata.ExtensionMethods; using Px.Utils.PxFile; +using System.Globalization; namespace Px.Utils.ModelBuilders { @@ -102,6 +103,7 @@ public static string GetTimeValValueRangeString(string input, PxFileConfiguratio /// /// Parses a string into a enumeration value. /// This method maps the input string to a enumeration value based on the provided or default PxFileConfiguration configuration. + /// Mapping is done using the configured variable type mappings. /// If the input string does not map to a known , the method returns . /// /// The string to parse into a enumeration value. @@ -110,20 +112,14 @@ public static string GetTimeValValueRangeString(string input, PxFileConfiguratio public static DimensionType StringToDimensionType(string input, PxFileConfiguration? conf = null) { conf ??= PxFileConfiguration.Default; - Dictionary map = new() - { - {conf.Tokens.VariableTypes.Content, DimensionType.Content}, - {conf.Tokens.VariableTypes.Time, DimensionType.Time}, - {conf.Tokens.VariableTypes.Ordinal, DimensionType.Ordinal}, - {conf.Tokens.VariableTypes.Nominal, DimensionType.Nominal}, - {conf.Tokens.VariableTypes.Geographical, DimensionType.Geographical}, - {conf.Tokens.VariableTypes.Other, DimensionType.Other}, - {conf.Tokens.VariableTypes.Unknown, DimensionType.Unknown} - }; string cleanString = input.CleanStringDelimeters(conf.Symbols.Value.StringDelimeter); - if (map.TryGetValue(cleanString, out DimensionType value)) return value; - else return DimensionType.Unknown; + if (conf.Tokens.VariableTypes.Mappings.TryGetValue(cleanString, out DimensionType value)) + { + return value; + } + + return DimensionType.Unknown; } } } diff --git a/Px.Utils/Px.Utils.csproj b/Px.Utils/Px.Utils.csproj index bd2ca8d..2e85547 100644 --- a/Px.Utils/Px.Utils.csproj +++ b/Px.Utils/Px.Utils.csproj @@ -2,7 +2,7 @@ Px.Utils - 1.4.1 + 1.5.0 net10.0 enable enable diff --git a/Px.Utils/PxFile/PxFileConfiguration.cs b/Px.Utils/PxFile/PxFileConfiguration.cs index f1cbf89..9b4f2f6 100644 --- a/Px.Utils/PxFile/PxFileConfiguration.cs +++ b/Px.Utils/PxFile/PxFileConfiguration.cs @@ -1,4 +1,4 @@ -using Px.Utils.Models.Metadata.Enums; +using Px.Utils.Models.Metadata.Enums; namespace Px.Utils.PxFile { @@ -107,28 +107,29 @@ private TimeValue() { } public static TimeValue DefaultTimeValue => new(); } + /// + /// Defines accepted tokens for parsing and validating dimension types using an expandable dictionary string to . + /// public class VariableTypeTokens { - private const string CONTENT = "Content"; - private const string TIME = "Time"; - private const string ORDINAL = "Ordinal"; - private const string NOMINAL = "Nominal"; - private const string GEOGRAPHICAL = "Geographical"; - private const string OTHER = "Other"; - private const string UNKNOWN = "Unknown"; - private const string CLASSIFICATORY = "Classificatory"; - - public string Content { get; set; } = CONTENT; - public string Time { get; set; } = TIME; - public string Ordinal { get; set; } = ORDINAL; - public string Nominal { get; set; } = NOMINAL; - public string Geographical { get; set; } = GEOGRAPHICAL; - public string Other { get; set; } = OTHER; - public string Unknown { get; set; } = UNKNOWN; - public string Classificatory { get; set; } = CLASSIFICATORY; + /// + /// Gets or sets the expandable dictionary mapping string tokens to dimension types. + /// + public Dictionary Mappings { get; set; } = new(StringComparer.OrdinalIgnoreCase) + { + { "Time", DimensionType.Time }, + { "Contents", DimensionType.Content }, + { "Geographical", DimensionType.Geographical }, + { "Ordinal", DimensionType.Ordinal }, + { "Nominal", DimensionType.Nominal }, + { "Other", DimensionType.Other }, + }; private VariableTypeTokens() { } + /// + /// Gets the default variable type tokens configuration. + /// public static VariableTypeTokens DefaultVariableTypeTokens => new(); } diff --git a/Px.Utils/Validation/ContentValidation/ContentValidator.ValidationEntryFunctions.cs b/Px.Utils/Validation/ContentValidation/ContentValidator.ValidationEntryFunctions.cs index 0de2425..5f0ab0a 100644 --- a/Px.Utils/Validation/ContentValidation/ContentValidator.ValidationEntryFunctions.cs +++ b/Px.Utils/Validation/ContentValidation/ContentValidator.ValidationEntryFunctions.cs @@ -1,4 +1,5 @@ using Px.Utils.Validation.SyntaxValidation; +using Px.Utils.Models.Metadata.Enums; using System.Globalization; namespace Px.Utils.Validation.ContentValidation @@ -317,71 +318,78 @@ public sealed partial class ContentValidator /// /// Entry in the Px file metadata. Represented by a object /// object that stores information that is gathered during the validation process - /// Key value pair containing information about the rule violation is returned if an unexpected value is detected + /// Key value pair containing information about the rule violation is returned if an unexpected value is detected from the entry public static ValidationFeedback? ValidateValueContents(ValidationStructuredEntry entry, ContentValidator validator) { - string[] allowedCharsets = ["ANSI", "Unicode"]; - - string[] dimensionTypes = [ - validator.Conf.Tokens.VariableTypes.Content, - validator.Conf.Tokens.VariableTypes.Time, - validator.Conf.Tokens.VariableTypes.Geographical, - validator.Conf.Tokens.VariableTypes.Ordinal, - validator.Conf.Tokens.VariableTypes.Nominal, - validator.Conf.Tokens.VariableTypes.Other, - validator.Conf.Tokens.VariableTypes.Unknown, - validator.Conf.Tokens.VariableTypes.Classificatory - ]; - + string[] allowedValueContents = []; + StringComparer comparer = StringComparer.Ordinal; + string keyword = entry.Key.Keyword; string value = SyntaxValidationUtilityMethods.CleanString(entry.Value, validator.Conf); - if ((entry.Key.Keyword == validator.Conf.Tokens.KeyWords.Charset && !allowedCharsets.Contains(value)) || - (entry.Key.Keyword == validator.Conf.Tokens.KeyWords.CodePage && !value.Equals(validator._encoding.BodyName, StringComparison.OrdinalIgnoreCase)) || - (entry.Key.Keyword == validator.Conf.Tokens.KeyWords.DimensionType && !dimensionTypes.Contains(value))) - { - KeyValuePair feedbackIndexes = SyntaxValidationUtilityMethods.GetLineAndCharacterIndex( - entry.KeyStartLineIndex, - entry.ValueStartIndex, - entry.LineChangeIndexes); - KeyValuePair feedback = new( - new(ValidationFeedbackLevel.Error, - ValidationFeedbackRule.InvalidValueFound), - new(validator._filename, - feedbackIndexes.Key, - feedbackIndexes.Value, - $"{entry.Key.Keyword}: {entry.Value}") - ); + if (keyword == validator.Conf.Tokens.KeyWords.Charset) + { + allowedValueContents = ["ANSI", "Unicode"]; + } + else if (keyword == validator.Conf.Tokens.KeyWords.CodePage) + { + allowedValueContents = [validator._encoding.BodyName]; + comparer = StringComparer.OrdinalIgnoreCase; + } + else if (keyword == validator.Conf.Tokens.KeyWords.DimensionType) + { + if (validator.Conf.Tokens.VariableTypes.Mappings.TryGetValue(value, out DimensionType _)) + { + return null; + } - return new(feedback); + return CreateInvalidValueFeedback(entry, validator); } - else if (entry.Key.Keyword == validator.Conf.Tokens.KeyWords.ContentVariableIdentifier) + else if (keyword == validator.Conf.Tokens.KeyWords.ContentVariableIdentifier) { string defaultLanguage = validator._defaultLanguage ?? string.Empty; string lang = entry.Key.Language ?? defaultLanguage; - if (validator._stubDimensionNames is not null && validator._stubDimensionNames.TryGetValue(lang, out string[]? stubValues) && - !Array.Exists(stubValues, d => d == value) && - (validator._headingDimensionNames is not null && validator._headingDimensionNames.TryGetValue(lang, out string[]? headingValues) && - !Array.Exists(headingValues, d => d == value))) - { - KeyValuePair feedbackIndexes = SyntaxValidationUtilityMethods.GetLineAndCharacterIndex( - entry.KeyStartLineIndex, - entry.ValueStartIndex, - entry.LineChangeIndexes); + List dimensionNames = []; - KeyValuePair feedback = new( - new(ValidationFeedbackLevel.Error, - ValidationFeedbackRule.InvalidValueFound), - new(validator._filename, - feedbackIndexes.Key, - feedbackIndexes.Value, - $"{entry.Key.Keyword}: {entry.Value}") - ); + if (validator._stubDimensionNames is not null && validator._stubDimensionNames.TryGetValue(lang, out string[]? stubValues)) + { + dimensionNames.AddRange(stubValues); + } - return new(feedback); + if (validator._headingDimensionNames is not null && validator._headingDimensionNames.TryGetValue(lang, out string[]? headingValues)) + { + dimensionNames.AddRange(headingValues); } + + allowedValueContents = [.. dimensionNames]; } - return null; + if (allowedValueContents.Length == 0 || allowedValueContents.Contains(value, comparer)) + { + return null; + } + + return CreateInvalidValueFeedback(entry, validator); + } + + private static ValidationFeedback CreateInvalidValueFeedback( + ValidationStructuredEntry entry, + ContentValidator validator) + { + KeyValuePair feedbackIndexes = SyntaxValidationUtilityMethods.GetLineAndCharacterIndex( + entry.KeyStartLineIndex, + entry.ValueStartIndex, + entry.LineChangeIndexes); + + KeyValuePair feedback = new( + new(ValidationFeedbackLevel.Error, + ValidationFeedbackRule.InvalidValueFound), + new(validator._filename, + feedbackIndexes.Key, + feedbackIndexes.Value, + $"{entry.Key.Keyword}: {entry.Value}") + ); + + return new(feedback); } /// diff --git a/docs/PXFILE_SPECIFICATION.md b/docs/PXFILE_SPECIFICATION.md index ac5ecb0..8c3d0a6 100644 --- a/docs/PXFILE_SPECIFICATION.md +++ b/docs/PXFILE_SPECIFICATION.md @@ -96,7 +96,7 @@ Whitespace characters are not significant in the value outside of ```"``` separa - Decimal separator is period ```.```. - Thousands separator is not allowed. - Whitespace characters are not allowed between the characters of the number. -- Number must be in range of ±7.9228 x 10^28. +- Number must be in range of ±7.9228 x 10^28. #### List of strings - List items are separated by a comma ```,```. @@ -281,6 +281,7 @@ More spesific encoding information. The value must be a string that matches the - Recommended that the variable type is defined for each variable defined by the STUB or HEADING. - Has a set of allowed values: ```Content```, ```Time```, ```Geographical```, ```Ordinal```, ```Nominal```, ```Other```, ```Classificatory```., ```Unknown```. - Can be defined for each language, but this is not recommended. +- Value is not case sensitive, but uppercase characters are recommended. #### TIMEVAL - See the TIMEVAL entry for the syntax and content requirements. \ No newline at end of file diff --git a/docs/architecture.models.md b/docs/architecture.models.md index 7705f8c..d83ff08 100644 --- a/docs/architecture.models.md +++ b/docs/architecture.models.md @@ -62,6 +62,8 @@ Task BuildAsync(IAsyncEnumerable> m Helpers: `MetadataEntryKeyBuilder`, `ValueParserUtilities`, `MetadataEntryKey`. +`PxFileConfiguration.TokenDefinitions.VariableTypeTokens` exposes a configurable `Dictionary` (`Mappings`) used by both parsing and validation for `VARIABLE-TYPE` values. The dictionary uses `StringComparer.OrdinalIgnoreCase`, so lookups are case-insensitive by default. Custom aliases can be added by inserting additional keys into `Mappings`. + ## Extension Methods | File | Purpose | diff --git a/docs/architecture.overview.md b/docs/architecture.overview.md index 3940ea1..152f4c2 100644 --- a/docs/architecture.overview.md +++ b/docs/architecture.overview.md @@ -27,6 +27,7 @@ No DI container — all types instantiated directly via constructors. Configurat | `Symbols.KeywordSeparator` | Keyword = value separator | `=` | | `Symbols.EntrySeparator` | Entry separator | `;` | | `Tokens.KeyWords.Data` | Data section keyword | `DATA` | +| `Tokens.VariableTypes.Mappings` | `VARIABLE-TYPE` token to `DimensionType` mapping | Case-insensitive dictionary (`StringComparer.OrdinalIgnoreCase`) | ## Key Patterns diff --git a/docs/architecture.validation.md b/docs/architecture.validation.md index 1873220..84736d0 100644 --- a/docs/architecture.validation.md +++ b/docs/architecture.validation.md @@ -34,6 +34,8 @@ Validates metadata content (required keys, language definitions, dimension consi File: `Validation/ContentValidation/ContentValidator.cs` Partial files: `ValidationEntryFunctions.cs`, `ValidationFindKeywordFunctions.cs`, `UtilityMethods.cs` +Dimension type validation uses `PxFileConfiguration.TokenDefinitions.VariableTypeTokens`. The first configured token for each dimension type is treated as the recommended primary value, while additional configured tokens are accepted as aliases and reported as warnings. + ### DataValidator Validates data section (row counts, row lengths, value types, separators).