diff --git a/docs/CodeDoc/Atc/Atc.Helpers.md b/docs/CodeDoc/Atc/Atc.Helpers.md
index 0fed788a..106f9d9b 100644
--- a/docs/CodeDoc/Atc/Atc.Helpers.md
+++ b/docs/CodeDoc/Atc/Atc.Helpers.md
@@ -1993,6 +1993,28 @@ PrimitiveTypeHelper.
+## StackTraceHelper
+Provides utility methods for working with stack traces.
+
+>```csharp
+>public static class StackTraceHelper
+>```
+
+### Static Methods
+
+#### ContainsConstructorWithinFrameCount
+>```csharp
+>bool ContainsConstructorWithinFrameCount(int drillDownFrameMax)
+>```
+>Summary: Determines whether any of the stack frames within the specified frame count contains a constructor.
+>
+>Parameters:
+> `drillDownFrameMax` - The maximum number of frames to inspect in the stack trace.
+>
+>Returns: `true` if a constructor is found within the specified number of frames; otherwise, `false`.
+
+
+
## TaskHelper
TaskHelper.
@@ -2101,4 +2123,53 @@ ThreadHelper.
>
>Parameters:
> `exemptProcessorCount` - The exempt processor count.
+
+
+
+## VersionHelper
+Provides utility methods for comparing and handling version strings and Version objects.
+
+>```csharp
+>public static class VersionHelper
+>```
+
+### Static Methods
+
+#### IsDefault
+>```csharp
+>bool IsDefault(Version sourceVersion, Version destinationVersion)
+>```
+>Summary: Determines whether both source and destination versions are the default version ("1.0.0.0").
+>
+>Parameters:
+> `sourceVersion` - The source version as a Version object.
+> `destinationVersion` - The destination version as a Version object.
+>
+>Returns: True if both versions are "1.0.0.0"; otherwise, false.
+#### IsSourceNewerThanDestination
+>```csharp
+>bool IsSourceNewerThanDestination(string sourceVersion, string destinationVersion)
+>```
+>Summary: Compares two version strings to determine if the source version is newer than the destination version.
+>
+>Parameters:
+> `sourceVersion` - The source version as a string.
+> `destinationVersion` - The destination version as a string.
+>
+>Returns: True if the source version is newer than the destination version; otherwise, false.
+>
+>Remarks: If either version string is null, or if they are equal, the method returns false. If the version strings cannot be parsed into System.Version, a lexical comparison is performed.
+#### IsSourceNewerThanDestination
+>```csharp
+>bool IsSourceNewerThanDestination(Version sourceVersion, Version destinationVersion)
+>```
+>Summary: Compares two version strings to determine if the source version is newer than the destination version.
+>
+>Parameters:
+> `sourceVersion` - The source version as a string.
+> `destinationVersion` - The destination version as a string.
+>
+>Returns: True if the source version is newer than the destination version; otherwise, false.
+>
+>Remarks: If either version string is null, or if they are equal, the method returns false. If the version strings cannot be parsed into System.Version, a lexical comparison is performed.
Generated by MarkdownCodeDoc version 1.2
diff --git a/docs/CodeDoc/Atc/Atc.md b/docs/CodeDoc/Atc/Atc.md
index 353ce576..f210288f 100644
--- a/docs/CodeDoc/Atc/Atc.md
+++ b/docs/CodeDoc/Atc/Atc.md
@@ -32,6 +32,25 @@ Flag-Enumeration: AddressType.
+
+
+## ArrowDirectionType
+
+>```csharp
+>public enum ArrowDirectionType
+>```
+
+
+| Value | Name | Description | Summary |
+| --- | --- | --- | --- |
+| 0 | None | None | Default None. |
+| 1 | Left | Left | Left. |
+| 2 | Up | Up | Up. |
+| 4 | Right | Right | Right. |
+| 8 | Down | Down | Down. |
+
+
+
## ArticleNumberType
diff --git a/docs/CodeDoc/Atc/Index.md b/docs/CodeDoc/Atc/Index.md
index eb3fc356..4955f68f 100644
--- a/docs/CodeDoc/Atc/Index.md
+++ b/docs/CodeDoc/Atc/Index.md
@@ -7,6 +7,7 @@
## [Atc](Atc.md)
- [AddressType](Atc.md#addresstype)
+- [ArrowDirectionType](Atc.md#arrowdirectiontype)
- [ArticleNumberType](Atc.md#articlenumbertype)
- [AtcAssemblyTypeInitializer](Atc.md#atcassemblytypeinitializer)
- [BooleanOperatorType](Atc.md#booleanoperatortype)
@@ -99,8 +100,10 @@
- [ReflectionHelper](Atc.Helpers.md#reflectionhelper)
- [RegionInfoHelper](Atc.Helpers.md#regioninfohelper)
- [SimpleTypeHelper](Atc.Helpers.md#simpletypehelper)
+- [StackTraceHelper](Atc.Helpers.md#stacktracehelper)
- [TaskHelper](Atc.Helpers.md#taskhelper)
- [ThreadHelper](Atc.Helpers.md#threadhelper)
+- [VersionHelper](Atc.Helpers.md#versionhelper)
## [Atc.Math](Atc.Math.md)
@@ -191,6 +194,7 @@
- [DesignTimeUseOnlyException](System.md#designtimeuseonlyexception)
- [DoubleExtensions](System.md#doubleextensions)
- [EntityStoreException](System.md#entitystoreexception)
+- [EnumAtcExtensions](System.md#enumatcextensions)
- [EnumExtensions](System.md#enumextensions)
- [ExceptionExtensions](System.md#exceptionextensions)
- [IntegerExtensions](System.md#integerextensions)
@@ -255,6 +259,10 @@
- [ClaimsPrincipalExtensions](System.Security.Claims.md#claimsprincipalextensions)
+## [System.Security.Cryptography.X509Certificates](System.Security.Cryptography.X509Certificates.md)
+
+- [X509Certificate2Extensions](System.Security.Cryptography.X509Certificates.md#x509certificate2extensions)
+
## [System.Text](System.Text.md)
- [StringBuilderExtensions](System.Text.md#stringbuilderextensions)
diff --git a/docs/CodeDoc/Atc/IndexExtended.md b/docs/CodeDoc/Atc/IndexExtended.md
index 471374b4..c6d6ab1b 100644
--- a/docs/CodeDoc/Atc/IndexExtended.md
+++ b/docs/CodeDoc/Atc/IndexExtended.md
@@ -7,6 +7,7 @@
## [Atc](Atc.md)
- [AddressType](Atc.md#addresstype)
+- [ArrowDirectionType](Atc.md#arrowdirectiontype)
- [ArticleNumberType](Atc.md#articlenumbertype)
- [AtcAssemblyTypeInitializer](Atc.md#atcassemblytypeinitializer)
- [BooleanOperatorType](Atc.md#booleanoperatortype)
@@ -4665,6 +4666,9 @@
- GetBeautifyTypeName(Type type)
- GetBeautifyTypeNameByRef(Type type)
- IsSimpleType(string value, StringComparison comparison = Ordinal)
+- [StackTraceHelper](Atc.Helpers.md#stacktracehelper)
+ - Static Methods
+ - ContainsConstructorWithinFrameCount(int drillDownFrameMax)
- [TaskHelper](Atc.Helpers.md#taskhelper)
- Static Methods
- Execute(Func<CancellationToken, Task<TResult>> taskToRun, TimeSpan timeout, CancellationToken cancellationToken = null)
@@ -4680,6 +4684,11 @@
- ProcessorCount
- Static Methods
- GetParallelOptions(int exemptProcessorCount = 2)
+- [VersionHelper](Atc.Helpers.md#versionhelper)
+ - Static Methods
+ - IsDefault(Version sourceVersion, Version destinationVersion)
+ - IsSourceNewerThanDestination(Version sourceVersion, Version destinationVersion)
+ - IsSourceNewerThanDestination(string sourceVersion, string destinationVersion)
## [Atc.Math](Atc.Math.md)
@@ -5027,6 +5036,17 @@
- RoundOff2(this double value)
- RoundOffPercent(this double percent)
- [EntityStoreException](System.md#entitystoreexception)
+- [EnumAtcExtensions](System.md#enumatcextensions)
+ - Static Methods
+ - Opposite(this ArrowDirectionType arrowDirectionType)
+ - Opposite(this CardinalDirectionType cardinalDirectionType)
+ - Opposite(this ForwardReverseType forwardReverseType)
+ - Opposite(this InsertRemoveType insertRemoveType)
+ - Opposite(this LeftRightType leftRightType)
+ - Opposite(this OnOffType onOffType)
+ - Opposite(this SortDirectionType sortDirectionType)
+ - Opposite(this UpDownType yesNoType)
+ - Opposite(this YesNoType yesNoType)
- [EnumExtensions](System.md#enumextensions)
- Static Methods
- GetDescription(this Enum enumeration, bool useLocalizedIfPossible = True)
@@ -5089,6 +5109,7 @@
- GetStringFormatParameterNumericCount(this string value)
- GetStringFormatParameterTemplatePlaceholders(this string value)
- GetTemplateKeys(this string value, TemplatePatternType templatePatternType = HardBrackets, bool includeTemplatePattern = False)
+ - GetUniqueTemplateKeysWithOccurrence(this string value, TemplatePatternType templatePatternType = HardBrackets, bool includeTemplatePattern = False)
- GetValueBetweenLessAndGreaterThanCharsIfExist(this string value)
- Humanize(this string value)
- IndexersOf(this string value, string pattern, bool ignoreCaseSensitive = True, bool useEndOfPatternToMatch = False)
@@ -5110,6 +5131,8 @@
- ReplaceMany(this string value, IDictionary<string, string> replacements)
- ReplaceMany(this string value, char[] chars, char replacement)
- ReplaceNewLines(this string value, string newValue)
+ - ReplaceTemplateKeyWithValue(this string value, string templateKey, string templateValue, TemplatePatternType templatePatternType = HardBrackets)
+ - ReplaceTemplateKeysWithValues(this string value, IDictionary<string, string> templateKeyValues, TemplatePatternType templatePatternType = HardBrackets)
- SetStringFormatParameterTemplatePlaceholders(this string value, Dictionary<string, string> replacements)
- ToLines(this string value)
- ToStream(this string value)
@@ -5356,6 +5379,13 @@
- Static Methods
- GetIdentity(this ClaimsPrincipal principal)
+## [System.Security.Cryptography.X509Certificates](System.Security.Cryptography.X509Certificates.md)
+
+- [X509Certificate2Extensions](System.Security.Cryptography.X509Certificates.md#x509certificate2extensions)
+ - Static Methods
+ - GetNameIdentifier(this X509Certificate2 certificate)
+ - IsValid(this X509Certificate2 certificate)
+
## [System.Text](System.Text.md)
- [StringBuilderExtensions](System.Text.md#stringbuilderextensions)
diff --git a/docs/CodeDoc/Atc/System.Security.Cryptography.X509Certificates.md b/docs/CodeDoc/Atc/System.Security.Cryptography.X509Certificates.md
new file mode 100644
index 00000000..8872494e
--- /dev/null
+++ b/docs/CodeDoc/Atc/System.Security.Cryptography.X509Certificates.md
@@ -0,0 +1,39 @@
+
+
+[References](Index.md) - [References extended](IndexExtended.md)
+
+
+# System.Security.Cryptography.X509Certificates
+
+
+
+## X509Certificate2Extensions
+Provides extension methods for the X509Certificate2 class.
+
+>```csharp
+>public static class X509Certificate2Extensions
+>```
+
+### Static Methods
+
+#### GetNameIdentifier
+>```csharp
+>string GetNameIdentifier(this X509Certificate2 certificate)
+>```
+>Summary: Gets the name identifier of the certificate. This can be the FriendlyName, or a substring of the SubjectName.
+>
+>Parameters:
+> `certificate` - The X509Certificate2 object.
+>
+>Returns: The FriendlyName of the certificate if not null or empty; otherwise, a substring of the SubjectName starting from "CN=" if it exists, or the full SubjectName if "CN=" is not found.
+#### IsValid
+>```csharp
+>bool IsValid(this X509Certificate2 certificate)
+>```
+>Summary: Checks if the certificate is valid. A certificate is considered valid if it is not archived, the current date is within the certificate's validity period, and it has a non-empty name identifier.
+>
+>Parameters:
+> `certificate` - The X509Certificate2 object.
+>
+>Returns: True if the certificate is valid; otherwise, false.
+
Generated by MarkdownCodeDoc version 1.2
diff --git a/docs/CodeDoc/Atc/System.md b/docs/CodeDoc/Atc/System.md
index 0c988aef..43d52069 100644
--- a/docs/CodeDoc/Atc/System.md
+++ b/docs/CodeDoc/Atc/System.md
@@ -1078,6 +1078,108 @@ The exception that is thrown when an entity is not stored.
>```
+
+
+## EnumAtcExtensions
+Provides extension methods for atc enum types.
+
+>```csharp
+>public static class EnumAtcExtensions
+>```
+
+### Static Methods
+
+#### Opposite
+>```csharp
+>ArrowDirectionType Opposite(this ArrowDirectionType arrowDirectionType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>CardinalDirectionType Opposite(this CardinalDirectionType cardinalDirectionType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>ForwardReverseType Opposite(this ForwardReverseType forwardReverseType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>InsertRemoveType Opposite(this InsertRemoveType insertRemoveType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>LeftRightType Opposite(this LeftRightType leftRightType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>OnOffType Opposite(this OnOffType onOffType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>SortDirectionType Opposite(this SortDirectionType sortDirectionType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>UpDownType Opposite(this UpDownType yesNoType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+#### Opposite
+>```csharp
+>YesNoType Opposite(this YesNoType yesNoType)
+>```
+>Summary: Gets the opposite direction of the specified ArrowDirectionType.
+>
+>Parameters:
+> `arrowDirectionType` - The ArrowDirectionType to find the opposite for.
+>
+>Returns: The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+
## EnumExtensions
@@ -1672,6 +1774,18 @@ Extensions for the string class.
> `includeTemplatePattern` - Indicates whether to include the template pattern in the returned keys.
>
>Returns: A list of extracted template keys. If no keys are found, an empty list is returned.
+#### GetUniqueTemplateKeysWithOccurrence
+>```csharp
+>IDictionary GetUniqueTemplateKeysWithOccurrence(this string value, TemplatePatternType templatePatternType = HardBrackets, bool includeTemplatePattern = False)
+>```
+>Summary: Gets unique template keys from the input string and counts their occurrences.
+>
+>Parameters:
+> `value` - The input string containing template keys.
+> `templatePatternType` - The type of template pattern to match.
+> `includeTemplatePattern` - Determines whether to include the template pattern itself.
+>
+>Returns: A dictionary where keys are unique template keys, and values are the number of times each key appears in the input string.
#### GetValueBetweenLessAndGreaterThanCharsIfExist
>```csharp
>string GetValueBetweenLessAndGreaterThanCharsIfExist(this string value)
@@ -1884,6 +1998,32 @@ Extensions for the string class.
> `newValue` - The new value for NewLine.
>
>Remarks: This method don't use the platform dependent System.Environment.Newline but instead works for all platforms as Windows, Unix and Mac. "\r\n" (\u000D\u000A) for Windows "\n" (\u000A) for Unix "\r" (\u000D) for Mac
+#### ReplaceTemplateKeyWithValue
+>```csharp
+>string ReplaceTemplateKeyWithValue(this string value, string templateKey, string templateValue, TemplatePatternType templatePatternType = HardBrackets)
+>```
+>Summary: Replaces a template key in the input string with a specified template value based on the given template pattern type.
+>
+>Parameters:
+> `value` - The input string containing template keys.
+> `templateKey` - The template key to be replaced.
+> `templateValue` - The value to replace the template key with.
+> `templatePatternType` - The type of template pattern to match in the input string.
+>
+>Returns: The modified input string with the template key replaced by the template value.
+#### ReplaceTemplateKeysWithValues
+>```csharp
+>string ReplaceTemplateKeysWithValues(this string value, IDictionary templateKeyValues, TemplatePatternType templatePatternType = HardBrackets)
+>```
+>Summary: Replaces multiple template keys in the input string with their corresponding template values based on the given template pattern type and a dictionary of key-value pairs.
+>
+>Parameters:
+> `value` - The input string containing template keys.
+> `templateKeyValues` - A dictionary of key-value pairs where keys are template keys
+ and values are the replacements for those keys.
+> `templatePatternType` - The type of template pattern to match in the input string.
+>
+>Returns: The modified input string with template keys replaced by their corresponding template values.
#### SetStringFormatParameterTemplatePlaceholders
>```csharp
>string SetStringFormatParameterTemplatePlaceholders(this string value, Dictionary replacements)
diff --git a/src/Atc/Enums/ArrowDirectionType.cs b/src/Atc/Enums/ArrowDirectionType.cs
new file mode 100644
index 00000000..a45d480b
--- /dev/null
+++ b/src/Atc/Enums/ArrowDirectionType.cs
@@ -0,0 +1,36 @@
+// ReSharper disable once CheckNamespace
+namespace Atc;
+
+[Flags]
+public enum ArrowDirectionType
+{
+ ///
+ /// Default None.
+ ///
+ [LocalizedDescription(null, typeof(EnumResources))]
+ None = 0x0,
+
+ ///
+ /// Left.
+ ///
+ [LocalizedDescription("ArrowDirectionTypeLeft", typeof(EnumResources))]
+ Left = 0x1,
+
+ ///
+ /// Up.
+ ///
+ [LocalizedDescription("ArrowDirectionTypeUp", typeof(EnumResources))]
+ Up = 0x2,
+
+ ///
+ /// Right.
+ ///
+ [LocalizedDescription("ArrowDirectionTypeRight", typeof(EnumResources))]
+ Right = 0x4,
+
+ ///
+ /// Down.
+ ///
+ [LocalizedDescription("ArrowDirectionTypeDown", typeof(EnumResources))]
+ Down = 0x8,
+}
\ No newline at end of file
diff --git a/src/Atc/Extensions/EnumAtcExtensions.cs b/src/Atc/Extensions/EnumAtcExtensions.cs
new file mode 100644
index 00000000..dbdd649c
--- /dev/null
+++ b/src/Atc/Extensions/EnumAtcExtensions.cs
@@ -0,0 +1,150 @@
+// ReSharper disable once CheckNamespace
+namespace System;
+
+///
+/// Provides extension methods for atc enum types.
+///
+public static class EnumAtcExtensions
+{
+ ///
+ /// Gets the opposite direction of the specified ArrowDirectionType.
+ ///
+ /// The ArrowDirectionType to find the opposite for.
+ /// The opposite ArrowDirectionType. Returns ArrowDirectionType.None if no opposite is defined.
+ public static ArrowDirectionType Opposite(
+ this ArrowDirectionType arrowDirectionType)
+ => arrowDirectionType switch
+ {
+ ArrowDirectionType.Left => ArrowDirectionType.Right,
+ ArrowDirectionType.Up => ArrowDirectionType.Down,
+ ArrowDirectionType.Right => ArrowDirectionType.Left,
+ ArrowDirectionType.Down => ArrowDirectionType.Up,
+ _ => ArrowDirectionType.None,
+ };
+
+ ///
+ /// Gets the opposite direction of the specified CardinalDirectionType.
+ ///
+ /// The CardinalDirectionType to find the opposite for.
+ /// The opposite CardinalDirectionType. Returns CardinalDirectionType.None if no opposite is defined.
+ public static CardinalDirectionType Opposite(
+ this CardinalDirectionType cardinalDirectionType)
+ => cardinalDirectionType switch
+ {
+ CardinalDirectionType.North => CardinalDirectionType.South,
+ CardinalDirectionType.NorthNorthEast => CardinalDirectionType.SouthSouthWest,
+ CardinalDirectionType.NorthEast => CardinalDirectionType.SouthWest,
+ CardinalDirectionType.EastNorthEast => CardinalDirectionType.WestSouthWest,
+ CardinalDirectionType.East => CardinalDirectionType.West,
+ CardinalDirectionType.EastSouthEast => CardinalDirectionType.WestNorthWest,
+ CardinalDirectionType.SouthEast => CardinalDirectionType.NorthWest,
+ CardinalDirectionType.SouthSouthEast => CardinalDirectionType.NorthNorthWest,
+ CardinalDirectionType.South => CardinalDirectionType.North,
+ CardinalDirectionType.SouthSouthWest => CardinalDirectionType.NorthNorthEast,
+ CardinalDirectionType.SouthWest => CardinalDirectionType.NorthEast,
+ CardinalDirectionType.WestSouthWest => CardinalDirectionType.EastNorthEast,
+ CardinalDirectionType.West => CardinalDirectionType.East,
+ CardinalDirectionType.WestNorthWest => CardinalDirectionType.EastSouthEast,
+ CardinalDirectionType.NorthWest => CardinalDirectionType.SouthEast,
+ CardinalDirectionType.NorthNorthWest => CardinalDirectionType.SouthSouthEast,
+ _ => CardinalDirectionType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified ForwardReverseType.
+ ///
+ /// The ForwardReverseType to find the opposite for.
+ /// The opposite ForwardReverseType. Returns ForwardReverseType.None if no opposite is defined.
+ public static ForwardReverseType Opposite(
+ this ForwardReverseType forwardReverseType)
+ => forwardReverseType switch
+ {
+ ForwardReverseType.Forward => ForwardReverseType.Reverse,
+ ForwardReverseType.Reverse => ForwardReverseType.Forward,
+ _ => ForwardReverseType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified InsertRemoveType.
+ ///
+ /// The InsertRemoveType to find the opposite for.
+ /// The opposite InsertRemoveType. Returns InsertRemoveType.None if no opposite is defined.
+ public static InsertRemoveType Opposite(
+ this InsertRemoveType insertRemoveType)
+ => insertRemoveType switch
+ {
+ InsertRemoveType.Insert => InsertRemoveType.Remove,
+ InsertRemoveType.Remove => InsertRemoveType.Insert,
+ _ => InsertRemoveType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified LeftRightType.
+ ///
+ /// The LeftRightType to find the opposite for.
+ /// The opposite LeftRightType. Returns LeftRightType.None if no opposite is defined.
+ public static LeftRightType Opposite(
+ this LeftRightType leftRightType)
+ => leftRightType switch
+ {
+ LeftRightType.Left => LeftRightType.Right,
+ LeftRightType.Right => LeftRightType.Left,
+ _ => LeftRightType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified OnOffType.
+ ///
+ /// The OnOffType to find the opposite for.
+ /// The opposite OnOffType. Returns OnOffType.None if no opposite is defined.
+ public static OnOffType Opposite(
+ this OnOffType onOffType)
+ => onOffType switch
+ {
+ OnOffType.On => OnOffType.Off,
+ OnOffType.Off => OnOffType.On,
+ _ => OnOffType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified SortDirectionType.
+ ///
+ /// The SortDirectionType to find the opposite for.
+ /// The opposite SortDirectionType. Returns SortDirectionType.None if no opposite is defined.
+ public static SortDirectionType Opposite(
+ this SortDirectionType sortDirectionType)
+ => sortDirectionType switch
+ {
+ SortDirectionType.Ascending => SortDirectionType.Descending,
+ SortDirectionType.Descending => SortDirectionType.Ascending,
+ _ => SortDirectionType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified UpDownType.
+ ///
+ /// The UpDownType to find the opposite for.
+ /// The opposite UpDownType. Returns UpDownType.None if no opposite is defined.
+ public static UpDownType Opposite(
+ this UpDownType yesNoType)
+ => yesNoType switch
+ {
+ UpDownType.Up => UpDownType.Down,
+ UpDownType.Down => UpDownType.Up,
+ _ => UpDownType.None,
+ };
+
+ ///
+ /// Gets the opposite state of the specified YesNoType.
+ ///
+ /// The YesNoType to find the opposite for.
+ /// The opposite YesNoType. Returns YesNoType.None if no opposite is defined.
+ public static YesNoType Opposite(
+ this YesNoType yesNoType)
+ => yesNoType switch
+ {
+ YesNoType.Yes => YesNoType.No,
+ YesNoType.No => YesNoType.Yes,
+ _ => YesNoType.None,
+ };
+}
\ No newline at end of file
diff --git a/src/Atc/Extensions/StringExtensions.cs b/src/Atc/Extensions/StringExtensions.cs
index 17886857..7dbaeb77 100644
--- a/src/Atc/Extensions/StringExtensions.cs
+++ b/src/Atc/Extensions/StringExtensions.cs
@@ -5,6 +5,8 @@
// ReSharper disable SwitchStatementHandlesSomeKnownEnumValuesWithDefault
// ReSharper disable SwitchStatementMissingSomeEnumCasesNoDefault
// ReSharper disable SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault
+// ReSharper disable ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
+// ReSharper disable LoopCanBeConvertedToQuery
namespace System;
///
@@ -431,6 +433,114 @@ public static IList GetTemplateKeys(
return result;
}
+ ///
+ /// Gets unique template keys from the input string and counts their occurrences.
+ ///
+ /// The input string containing template keys.
+ /// The type of template pattern to match.
+ /// Determines whether to include the template pattern itself.
+ /// A dictionary where keys are unique template keys, and values are the number of times each
+ /// key appears in the input string.
+ public static IDictionary GetUniqueTemplateKeysWithOccurrence(
+ this string value,
+ TemplatePatternType templatePatternType = TemplatePatternType.HardBrackets,
+ bool includeTemplatePattern = false)
+ {
+ var templateKeys = value.GetTemplateKeys(
+ templatePatternType,
+ includeTemplatePattern);
+
+ var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var templateKey in templateKeys)
+ {
+ if (!result.TryAdd(templateKey, 1))
+ {
+ result[templateKey]++;
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Replaces a template key in the input string with a specified template value based on the given template pattern type.
+ ///
+ /// The input string containing template keys.
+ /// The template key to be replaced.
+ /// The value to replace the template key with.
+ /// The type of template pattern to match in the input string.
+ /// The modified input string with the template key replaced by the template value.
+ public static string ReplaceTemplateKeyWithValue(
+ this string value,
+ string templateKey,
+ string templateValue,
+ TemplatePatternType templatePatternType = TemplatePatternType.HardBrackets)
+ {
+ if (string.IsNullOrEmpty(value) ||
+ templatePatternType == TemplatePatternType.None)
+ {
+ return value;
+ }
+
+ switch (templatePatternType)
+ {
+ case TemplatePatternType.SingleHardBrackets:
+ return value.Replace($"[{templateKey}]", templateValue, StringComparison.OrdinalIgnoreCase);
+ case TemplatePatternType.DoubleHardBrackets:
+ return value.Replace($"[[{templateKey}]]", templateValue, StringComparison.OrdinalIgnoreCase);
+ case TemplatePatternType.SingleCurlyBraces:
+ return value.Replace($"{{{templateKey}}}", templateValue, StringComparison.OrdinalIgnoreCase);
+ case TemplatePatternType.DoubleCurlyBraces:
+ return value.Replace($"{{{{{templateKey}}}}}", templateValue, StringComparison.OrdinalIgnoreCase);
+ case TemplatePatternType.HardBrackets:
+ value = value.Replace($"[[{templateKey}]]", templateValue, StringComparison.OrdinalIgnoreCase);
+ return value.Replace($"[{templateKey}]", templateValue, StringComparison.OrdinalIgnoreCase);
+ case TemplatePatternType.CurlyBraces:
+ value = value.Replace($"{{{{{templateKey}}}}}", templateValue, StringComparison.OrdinalIgnoreCase);
+ return value.Replace($"{{{templateKey}}}", templateValue, StringComparison.OrdinalIgnoreCase);
+ case TemplatePatternType.All:
+ value = value.Replace($"[[{templateKey}]]", templateValue, StringComparison.OrdinalIgnoreCase);
+ value = value.Replace($"[{templateKey}]", templateValue, StringComparison.OrdinalIgnoreCase);
+ value = value.Replace($"{{{{{templateKey}}}}}", templateValue, StringComparison.OrdinalIgnoreCase);
+ return value.Replace($"{{{templateKey}}}", templateValue, StringComparison.OrdinalIgnoreCase);
+ default:
+ throw new SwitchCaseDefaultException(templatePatternType);
+ }
+ }
+
+ ///
+ /// Replaces multiple template keys in the input string with their corresponding template values
+ /// based on the given template pattern type and a dictionary of key-value pairs.
+ ///
+ /// The input string containing template keys.
+ /// A dictionary of key-value pairs where keys are template keys
+ /// and values are the replacements for those keys.
+ /// The type of template pattern to match in the input string.
+ /// The modified input string with template keys replaced by their corresponding template values.
+ public static string ReplaceTemplateKeysWithValues(
+ this string value,
+ IDictionary templateKeyValues,
+ TemplatePatternType templatePatternType = TemplatePatternType.HardBrackets)
+ {
+ if (string.IsNullOrEmpty(value) ||
+ templateKeyValues is null ||
+ templatePatternType == TemplatePatternType.None)
+ {
+ return value;
+ }
+
+ foreach (var templateKeyValue in templateKeyValues)
+ {
+ value = value.ReplaceTemplateKeyWithValue(
+ templateKeyValue.Key,
+ templateKeyValue.Value,
+ templatePatternType);
+ }
+
+ return value;
+ }
+
///
/// Parses the date from iso8601.
///
diff --git a/src/Atc/Extensions/X509Certificate2Extensions.cs b/src/Atc/Extensions/X509Certificate2Extensions.cs
new file mode 100644
index 00000000..4f94401a
--- /dev/null
+++ b/src/Atc/Extensions/X509Certificate2Extensions.cs
@@ -0,0 +1,64 @@
+// ReSharper disable CheckNamespace
+namespace System.Security.Cryptography.X509Certificates;
+
+///
+/// Provides extension methods for the X509Certificate2 class.
+///
+public static class X509Certificate2Extensions
+{
+ ///
+ /// Gets the name identifier of the certificate. This can be the FriendlyName, or a substring of the SubjectName.
+ ///
+ /// The X509Certificate2 object.
+ ///
+ /// The FriendlyName of the certificate if not null or empty; otherwise, a substring of the SubjectName starting
+ /// from "CN=" if it exists, or the full SubjectName if "CN=" is not found.
+ ///
+ public static string GetNameIdentifier(
+ this X509Certificate2 certificate)
+ {
+ if (certificate is null)
+ {
+ throw new ArgumentNullException(nameof(certificate));
+ }
+
+ if (!string.IsNullOrEmpty(certificate.FriendlyName))
+ {
+ return certificate.FriendlyName;
+ }
+
+ if (certificate.SubjectName is null ||
+ string.IsNullOrEmpty(certificate.SubjectName.Name))
+ {
+ return string.Empty;
+ }
+
+ const string searchText = "CN=";
+ var index = certificate.SubjectName.Name.IndexOf(searchText, StringComparison.OrdinalIgnoreCase);
+ return index == -1
+ ? certificate.SubjectName.Name
+ : certificate.SubjectName.Name[(index + searchText.Length)..];
+ }
+
+ ///
+ /// Checks if the certificate is valid. A certificate is considered valid if it is not archived,
+ /// the current date is within the certificate's validity period, and it has a non-empty name identifier.
+ ///
+ /// The X509Certificate2 object.
+ ///
+ /// True if the certificate is valid; otherwise, false.
+ ///
+ public static bool IsValid(
+ this X509Certificate2 certificate)
+ {
+ if (certificate is null)
+ {
+ throw new ArgumentNullException(nameof(certificate));
+ }
+
+ return !certificate.Archived &&
+ DateTime.Now < certificate.NotAfter &&
+ DateTime.Now > certificate.NotBefore &&
+ !string.IsNullOrEmpty(certificate.GetNameIdentifier());
+ }
+}
\ No newline at end of file
diff --git a/src/Atc/Helpers/StackTraceHelper.cs b/src/Atc/Helpers/StackTraceHelper.cs
new file mode 100644
index 00000000..3f89728f
--- /dev/null
+++ b/src/Atc/Helpers/StackTraceHelper.cs
@@ -0,0 +1,42 @@
+namespace Atc.Helpers;
+
+///
+/// Provides utility methods for working with stack traces.
+///
+public static class StackTraceHelper
+{
+ ///
+ /// Determines whether any of the stack frames within the specified frame count contains a constructor.
+ ///
+ /// The maximum number of frames to inspect in the stack trace.
+ ///
+ /// true if a constructor is found within the specified number of frames; otherwise, false.
+ ///
+ public static bool ContainsConstructorWithinFrameCount(
+ int drillDownFrameMax)
+ {
+ var stackFrames = new StackTrace().GetFrames();
+ if (stackFrames is null)
+ {
+ return false;
+ }
+
+ var stackFrameLengthMax = stackFrames.Length;
+ if (stackFrameLengthMax > drillDownFrameMax)
+ {
+ stackFrameLengthMax = drillDownFrameMax;
+ }
+
+ for (var i = 0; i < stackFrameLengthMax; i++)
+ {
+ var methodBase = stackFrames[i].GetMethod();
+ if (methodBase is not null &&
+ ".ctor".Equals(methodBase.Name, StringComparison.Ordinal))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/Atc/Helpers/VersionHelper.cs b/src/Atc/Helpers/VersionHelper.cs
new file mode 100644
index 00000000..b8edd475
--- /dev/null
+++ b/src/Atc/Helpers/VersionHelper.cs
@@ -0,0 +1,96 @@
+namespace Atc.Helpers;
+
+///
+/// Provides utility methods for comparing and handling version strings and Version objects.
+///
+public static class VersionHelper
+{
+ ///
+ /// Compares two version strings to determine if the source version is newer than the destination version.
+ ///
+ /// The source version as a string.
+ /// The destination version as a string.
+ ///
+ /// True if the source version is newer than the destination version; otherwise, false.
+ ///
+ ///
+ /// If either version string is null, or if they are equal, the method returns false.
+ /// If the version strings cannot be parsed into System.Version, a lexical comparison is performed.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "OK.")]
+ public static bool IsSourceNewerThanDestination(
+ string? sourceVersion,
+ string? destinationVersion)
+ {
+ if (sourceVersion is null ||
+ destinationVersion is null ||
+ sourceVersion == destinationVersion)
+ {
+ return false;
+ }
+
+ try
+ {
+ return IsSourceNewerThanDestination(
+ new Version(sourceVersion),
+ new Version(destinationVersion));
+ }
+ catch
+ {
+ var sortedSet = new SortedSet(StringComparer.Ordinal)
+ {
+ sourceVersion,
+ destinationVersion,
+ };
+
+ return destinationVersion == sortedSet.First();
+ }
+ }
+
+ ///
+ /// Compares two Version objects to determine if the source version is newer than the destination version.
+ ///
+ /// The source version as a Version object.
+ /// The destination version as a Version object.
+ ///
+ /// True if the source version is newer than the destination version; otherwise, false.
+ ///
+ ///
+ /// If either Version object is null, or if they are equal, the method returns false.
+ ///
+ public static bool IsSourceNewerThanDestination(
+ Version? sourceVersion,
+ Version? destinationVersion)
+ {
+ if (sourceVersion is null ||
+ destinationVersion is null ||
+ sourceVersion == destinationVersion)
+ {
+ return false;
+ }
+
+ return sourceVersion.IsNewerThan(destinationVersion);
+ }
+
+ ///
+ /// Determines whether both source and destination versions are the default version ("1.0.0.0").
+ ///
+ /// The source version as a Version object.
+ /// The destination version as a Version object.
+ ///
+ /// True if both versions are "1.0.0.0"; otherwise, false.
+ ///
+ public static bool IsDefault(
+ Version? sourceVersion,
+ Version? destinationVersion)
+ {
+ if (sourceVersion is null ||
+ destinationVersion is null)
+ {
+ return false;
+ }
+
+ return "1.0.0.0".Equals(sourceVersion.ToString(), StringComparison.Ordinal) &&
+ "1.0.0.0".Equals(destinationVersion.ToString(), StringComparison.Ordinal);
+ }
+}
\ No newline at end of file
diff --git a/src/Atc/Resources/EnumResources.Designer.cs b/src/Atc/Resources/EnumResources.Designer.cs
index 9198f630..55249c95 100644
--- a/src/Atc/Resources/EnumResources.Designer.cs
+++ b/src/Atc/Resources/EnumResources.Designer.cs
@@ -1,4 +1,4 @@
-//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
//
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
@@ -8,9 +8,10 @@
//
//------------------------------------------------------------------------------
-using System.CodeDom.Compiler;
-
namespace Atc.Resources {
+ using System;
+
+
///
/// A strongly-typed resource class, for looking up localized strings, etc.
///
@@ -18,27 +19,27 @@ namespace Atc.Resources {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [DebuggerNonUserCode()]
- [CompilerGenerated()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class EnumResources {
- private static ResourceManager resourceMan;
+ private static global::System.Resources.ResourceManager resourceMan;
- private static CultureInfo resourceCulture;
+ private static global::System.Globalization.CultureInfo resourceCulture;
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal EnumResources() {
}
///
/// Returns the cached ResourceManager instance used by this class.
///
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public static ResourceManager ResourceManager {
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Resources.ResourceManager ResourceManager {
get {
- if (ReferenceEquals(resourceMan, null)) {
- ResourceManager temp = new ResourceManager("Atc.Resources.EnumResources", typeof(EnumResources).Assembly);
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Atc.Resources.EnumResources", typeof(EnumResources).Assembly);
resourceMan = temp;
}
return resourceMan;
@@ -49,8 +50,8 @@ public static ResourceManager ResourceManager {
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
///
- [EditorBrowsable(EditorBrowsableState.Advanced)]
- public static CultureInfo Culture {
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
@@ -86,6 +87,42 @@ public static string AppTypeService {
}
}
+ ///
+ /// Looks up a localized string similar to Down.
+ ///
+ public static string ArrowDirectionTypeDown {
+ get {
+ return ResourceManager.GetString("ArrowDirectionTypeDown", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Left.
+ ///
+ public static string ArrowDirectionTypeLeft {
+ get {
+ return ResourceManager.GetString("ArrowDirectionTypeLeft", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Right.
+ ///
+ public static string ArrowDirectionTypeRight {
+ get {
+ return ResourceManager.GetString("ArrowDirectionTypeRight", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Up.
+ ///
+ public static string ArrowDirectionTypeUp {
+ get {
+ return ResourceManager.GetString("ArrowDirectionTypeUp", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Amount Of Substance.
///
diff --git a/src/Atc/Resources/EnumResources.da-DK.resx b/src/Atc/Resources/EnumResources.da-DK.resx
index 011235d6..46681d4a 100644
--- a/src/Atc/Resources/EnumResources.da-DK.resx
+++ b/src/Atc/Resources/EnumResources.da-DK.resx
@@ -369,4 +369,16 @@
Ja
+
+ Ned
+
+
+ Venstre
+
+
+ Højre
+
+
+ Op
+
\ No newline at end of file
diff --git a/src/Atc/Resources/EnumResources.de-DE.resx b/src/Atc/Resources/EnumResources.de-DE.resx
index d64b44a8..340661c6 100644
--- a/src/Atc/Resources/EnumResources.de-DE.resx
+++ b/src/Atc/Resources/EnumResources.de-DE.resx
@@ -369,4 +369,16 @@
Ja
+
+ Links
+
+
+ Unten
+
+
+ Rechts
+
+
+ Ober
+
\ No newline at end of file
diff --git a/src/Atc/Resources/EnumResources.resx b/src/Atc/Resources/EnumResources.resx
index 05458fea..0e048a91 100644
--- a/src/Atc/Resources/EnumResources.resx
+++ b/src/Atc/Resources/EnumResources.resx
@@ -126,6 +126,18 @@
Service
+
+ Down
+
+
+ Left
+
+
+ Right
+
+
+ Up
+
Amount Of Substance
diff --git a/test/Atc.Tests/CodeComplianceTests.cs b/test/Atc.Tests/CodeComplianceTests.cs
index 820bc628..0c935523 100644
--- a/test/Atc.Tests/CodeComplianceTests.cs
+++ b/test/Atc.Tests/CodeComplianceTests.cs
@@ -17,6 +17,7 @@ public class CodeComplianceTests
// TODO: Add UnitTest and remove from this list!!
typeof(AssemblyHelper),
typeof(AppDomainExtensions),
+ typeof(X509Certificate2Extensions),
typeof(SemanticVersion),
typeof(MathEx),
typeof(JsonSerializerOptionsFactory),
@@ -37,8 +38,10 @@ public class CodeComplianceTests
typeof(FileHelper<>),
typeof(ByteHelper),
typeof(ByteExtensions),
+ typeof(StackTraceHelper),
// UnitTests are made, but CodeCompliance test cannot detect this
+ typeof(EnumAtcExtensions),
typeof(DynamicJson),
typeof(EnumHelper),
typeof(NumberHelper),
@@ -96,6 +99,7 @@ public void AssertExportedTypesWithWrongNaming()
{
var excludeTypesForNaming = new List
{
+ typeof(EnumAtcExtensions),
typeof(CharExtensions),
typeof(ByteExtensions),
typeof(ByteSizeExtensions), // Extension parameter type should "normal" match the class name-prefix, but because of the code-grouping, it is ok.
diff --git a/test/Atc.Tests/Extensions/EnumAtcExtensionsTests.cs b/test/Atc.Tests/Extensions/EnumAtcExtensionsTests.cs
new file mode 100644
index 00000000..3c259c64
--- /dev/null
+++ b/test/Atc.Tests/Extensions/EnumAtcExtensionsTests.cs
@@ -0,0 +1,83 @@
+namespace Atc.Tests.Extensions;
+
+public class EnumAtcExtensionsTests
+{
+ [Theory]
+ [InlineData(ArrowDirectionType.Left, ArrowDirectionType.Right)]
+ [InlineData(ArrowDirectionType.Up, ArrowDirectionType.Down)]
+ [InlineData(ArrowDirectionType.Right, ArrowDirectionType.Left)]
+ [InlineData(ArrowDirectionType.Down, ArrowDirectionType.Up)]
+ [InlineData(ArrowDirectionType.None, ArrowDirectionType.None)]
+ public void Opposite_ArrowDirectionType(ArrowDirectionType expected, ArrowDirectionType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(CardinalDirectionType.North, CardinalDirectionType.South)]
+ [InlineData(CardinalDirectionType.NorthNorthEast, CardinalDirectionType.SouthSouthWest)]
+ [InlineData(CardinalDirectionType.NorthEast, CardinalDirectionType.SouthWest)]
+ [InlineData(CardinalDirectionType.EastNorthEast, CardinalDirectionType.WestSouthWest)]
+ [InlineData(CardinalDirectionType.East, CardinalDirectionType.West)]
+ [InlineData(CardinalDirectionType.EastSouthEast, CardinalDirectionType.WestNorthWest)]
+ [InlineData(CardinalDirectionType.SouthEast, CardinalDirectionType.NorthWest)]
+ [InlineData(CardinalDirectionType.SouthSouthEast, CardinalDirectionType.NorthNorthWest)]
+ [InlineData(CardinalDirectionType.South, CardinalDirectionType.North)]
+ [InlineData(CardinalDirectionType.SouthSouthWest, CardinalDirectionType.NorthNorthEast)]
+ [InlineData(CardinalDirectionType.SouthWest, CardinalDirectionType.NorthEast)]
+ [InlineData(CardinalDirectionType.WestSouthWest, CardinalDirectionType.EastNorthEast)]
+ [InlineData(CardinalDirectionType.West, CardinalDirectionType.East)]
+ [InlineData(CardinalDirectionType.WestNorthWest, CardinalDirectionType.EastSouthEast)]
+ [InlineData(CardinalDirectionType.NorthWest, CardinalDirectionType.SouthEast)]
+ [InlineData(CardinalDirectionType.NorthNorthWest, CardinalDirectionType.SouthSouthEast)]
+ [InlineData(CardinalDirectionType.None, CardinalDirectionType.None)]
+ public void Opposite_CardinalDirectionType(CardinalDirectionType expected, CardinalDirectionType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(ForwardReverseType.Forward, ForwardReverseType.Reverse)]
+ [InlineData(ForwardReverseType.Reverse, ForwardReverseType.Forward)]
+ [InlineData(ForwardReverseType.None, ForwardReverseType.None)]
+ public void Opposite_ForwardReverseType(ForwardReverseType expected, ForwardReverseType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(InsertRemoveType.Insert, InsertRemoveType.Remove)]
+ [InlineData(InsertRemoveType.Remove, InsertRemoveType.Insert)]
+ [InlineData(InsertRemoveType.None, InsertRemoveType.None)]
+ public void Opposite_InsertRemoveType(InsertRemoveType expected, InsertRemoveType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(LeftRightType.Left, LeftRightType.Right)]
+ [InlineData(LeftRightType.Right, LeftRightType.Left)]
+ [InlineData(LeftRightType.None, LeftRightType.None)]
+ public void Opposite_LeftRightType(LeftRightType expected, LeftRightType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(OnOffType.On, OnOffType.Off)]
+ [InlineData(OnOffType.Off, OnOffType.On)]
+ [InlineData(OnOffType.None, OnOffType.None)]
+ public void Opposite_OnOffType(OnOffType expected, OnOffType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(SortDirectionType.Ascending, SortDirectionType.Descending)]
+ [InlineData(SortDirectionType.Descending, SortDirectionType.Ascending)]
+ [InlineData(SortDirectionType.None, SortDirectionType.None)]
+ public void Opposite_SortDirectionType(SortDirectionType expected, SortDirectionType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(UpDownType.Up, UpDownType.Down)]
+ [InlineData(UpDownType.Down, UpDownType.Up)]
+ [InlineData(UpDownType.None, UpDownType.None)]
+ public void Opposite_UpDownType(UpDownType expected, UpDownType input)
+ => Assert.Equal(expected, input.Opposite());
+
+ [Theory]
+ [InlineData(YesNoType.Yes, YesNoType.No)]
+ [InlineData(YesNoType.No, YesNoType.Yes)]
+ [InlineData(YesNoType.None, YesNoType.None)]
+ public void Opposite_YesNoType(YesNoType expected, YesNoType input)
+ => Assert.Equal(expected, input.Opposite());
+}
\ No newline at end of file
diff --git a/test/Atc.Tests/Extensions/StringExtensionsTests.cs b/test/Atc.Tests/Extensions/StringExtensionsTests.cs
index 66d7d49a..4aded334 100644
--- a/test/Atc.Tests/Extensions/StringExtensionsTests.cs
+++ b/test/Atc.Tests/Extensions/StringExtensionsTests.cs
@@ -132,6 +132,72 @@ public void ContainsTemplatePattern(bool expected, string input, TemplatePattern
public void GetTemplateKeys(string[] expected, string input, TemplatePatternType patternType, bool includePattern)
=> Assert.Equal(expected, input.GetTemplateKeys(patternType, includePattern));
+ [Theory]
+ [InlineData("World", 2, "Hello [World] and [World] again.", TemplatePatternType.SingleHardBrackets, false)]
+ [InlineData("[World]", 2, "Hello [World] and [World] again.", TemplatePatternType.SingleHardBrackets, true)]
+ [InlineData("World", 2, "Hello {World} and {World} again.", TemplatePatternType.SingleCurlyBraces, false)]
+ [InlineData("{World}", 2, "Hello {World} and {World} again.", TemplatePatternType.SingleCurlyBraces, true)]
+ public void GetUniqueTemplateKeysWithOccurrence(
+ string expectedString, int expectedCount, string input, TemplatePatternType patternType, bool includePattern)
+ {
+ // Act
+ var result = input.GetUniqueTemplateKeysWithOccurrence(patternType, includePattern);
+
+ // Assert
+ Assert.NotNull(result);
+ Assert.Single(result);
+ Assert.Equal(expectedString, result.Keys.First());
+ Assert.Equal(expectedCount, result.Values.First());
+ }
+
+ [Theory]
+ [InlineData("Hello John!", "Hello [Name]!", "Name", "John", TemplatePatternType.SingleHardBrackets)]
+ [InlineData("Hello [John]!", "Hello [[Name]]!", "Name", "John", TemplatePatternType.SingleHardBrackets)]
+ [InlineData("Hello John!", "Hello [[Name]]!", "Name", "John", TemplatePatternType.DoubleHardBrackets)]
+ [InlineData("Hello John!", "Hello [[Name]]!", "Name", "John", TemplatePatternType.HardBrackets)]
+ [InlineData("Hello John!", "Hello {Name}!", "Name", "John", TemplatePatternType.SingleCurlyBraces)]
+ [InlineData("Hello {John}!", "Hello {{Name}}!", "Name", "John", TemplatePatternType.SingleCurlyBraces)]
+ [InlineData("Hello John!", "Hello {{Name}}!", "Name", "John", TemplatePatternType.DoubleCurlyBraces)]
+ [InlineData("Hello John!", "Hello {{Name}}!", "Name", "John", TemplatePatternType.CurlyBraces)]
+ [InlineData("Hello John John!", "Hello [[Name]] {{Name}}!", "Name", "John", TemplatePatternType.All)]
+ public void ReplaceTemplateKeyWithValue(
+ string expected, string input, string templateKey, string templateValue, TemplatePatternType patternType)
+ => Assert.Equal(expected, input.ReplaceTemplateKeyWithValue(templateKey, templateValue, patternType));
+
+ [Theory]
+ [InlineData("Hello John", "Hello [FirstName]", TemplatePatternType.SingleHardBrackets)]
+ [InlineData("Hello John Doe", "Hello [FirstName] [LastName]", TemplatePatternType.SingleHardBrackets)]
+ [InlineData("Hello John Doe and 30!", "Hello [FirstName] [LastName] and [Age]!", TemplatePatternType.SingleHardBrackets)]
+ [InlineData("Hello John", "Hello [[FirstName]]", TemplatePatternType.DoubleHardBrackets)]
+ [InlineData("Hello John Doe", "Hello [[FirstName]] [[LastName]]", TemplatePatternType.DoubleHardBrackets)]
+ [InlineData("Hello John Doe and 30!", "Hello [[FirstName]] [[LastName]] and [[Age]]!", TemplatePatternType.DoubleHardBrackets)]
+ [InlineData("Hello [Name]", "Hello [Name]", TemplatePatternType.SingleHardBrackets)]
+ [InlineData("Hello [[Name]]", "Hello [[Name]]", TemplatePatternType.DoubleHardBrackets)]
+ [InlineData("Hello John", "Hello {FirstName}", TemplatePatternType.SingleCurlyBraces)]
+ [InlineData("Hello John Doe", "Hello {FirstName} {LastName}", TemplatePatternType.SingleCurlyBraces)]
+ [InlineData("Hello John Doe and 30!", "Hello {FirstName} {LastName} and {Age}!", TemplatePatternType.SingleCurlyBraces)]
+ [InlineData("Hello John", "Hello {{FirstName}}", TemplatePatternType.DoubleCurlyBraces)]
+ [InlineData("Hello John Doe", "Hello {{FirstName}} {{LastName}}", TemplatePatternType.DoubleCurlyBraces)]
+ [InlineData("Hello John Doe and 30!", "Hello {{FirstName}} {{LastName}} and {{Age}}!", TemplatePatternType.DoubleCurlyBraces)]
+ [InlineData("Hello {Name}", "Hello {Name}", TemplatePatternType.SingleCurlyBraces)]
+ [InlineData("Hello {{Name}}", "Hello {{Name}}", TemplatePatternType.DoubleCurlyBraces)]
+ public void ReplaceTemplateKeysWithValues(string expected, string input, TemplatePatternType patternType)
+ {
+ // Arrange
+ var templateKeyValues = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ { "FirstName", "John" },
+ { "LastName", "Doe" },
+ { "Age", "30" },
+ };
+
+ // Act
+ var result = input.ReplaceTemplateKeysWithValues(templateKeyValues, patternType);
+
+ // Assert
+ Assert.Equal(expected, result);
+ }
+
[Theory]
[InlineData("2000-12-01T23:47:37", "2000-12-01T23:47:37")]
public void ParseDateFromIso8601(string expected, string input)
diff --git a/test/Atc.Tests/GlobalUsings.cs b/test/Atc.Tests/GlobalUsings.cs
index 150a1889..eea31997 100644
--- a/test/Atc.Tests/GlobalUsings.cs
+++ b/test/Atc.Tests/GlobalUsings.cs
@@ -8,6 +8,7 @@
global using System.Reflection;
global using System.Runtime.Versioning;
global using System.Security.Claims;
+global using System.Security.Cryptography.X509Certificates;
global using System.Text;
global using System.Text.Json;
global using System.Text.Json.Serialization;
diff --git a/test/Atc.Tests/Helpers/VersionHelperTests.cs b/test/Atc.Tests/Helpers/VersionHelperTests.cs
new file mode 100644
index 00000000..351ebb51
--- /dev/null
+++ b/test/Atc.Tests/Helpers/VersionHelperTests.cs
@@ -0,0 +1,47 @@
+namespace Atc.Tests.Helpers;
+
+public class VersionHelperTests
+{
+ [Theory]
+ [InlineData(false, "1.0.0.0", "2.0.0.0")]
+ [InlineData(true, "2.0.0.0", "1.0.0.0")]
+ [InlineData(false, null, "1.0.0.0")]
+ [InlineData(false, "1.0.0.0", null)]
+ [InlineData(false, "1.0.0.0", "1.0.0.0")]
+ public void IsSourceNewerThanDestination_StringVersions(bool expected, string source, string destination)
+ => Assert.Equal(expected, VersionHelper.IsSourceNewerThanDestination(source, destination));
+
+ [Theory]
+ [InlineData(false, "1.0", "2.0")]
+ [InlineData(true, "2.0", "1.0")]
+ [InlineData(false, null, "1.0")]
+ [InlineData(false, "1.0", null)]
+ public void IsSourceNewerThanDestination_VersionObjects(bool expected, string? source, string? destination)
+ {
+ // Arrange
+ var sourceVersion = source != null ? new Version(source) : null;
+ var destinationVersion = destination != null ? new Version(destination) : null;
+
+ // Act
+ var result = VersionHelper.IsSourceNewerThanDestination(sourceVersion, destinationVersion);
+
+ // Assert
+ Assert.Equal(expected, result);
+ }
+
+ [Theory]
+ [InlineData(true, "1.0.0.0", "1.0.0.0")]
+ [InlineData(false, "1.0.0.0", "2.0.0.0")]
+ public void IsDefault(bool expected, string source, string destination)
+ {
+ // Arrange
+ var sourceVersion = new Version(source);
+ var destinationVersion = new Version(destination);
+
+ // Atc
+ var result = VersionHelper.IsDefault(sourceVersion, destinationVersion);
+
+ // Assert
+ Assert.Equal(expected, result);
+ }
+}
\ No newline at end of file