From 9c11427068c08956b538725efbbd5d5b17c17f92 Mon Sep 17 00:00:00 2001 From: ivano scifoni Date: Fri, 20 Oct 2023 18:08:34 +0200 Subject: [PATCH 01/27] Update wfnetcorev2.yaml --- .github/workflows/wfnetcorev2.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/wfnetcorev2.yaml b/.github/workflows/wfnetcorev2.yaml index 91882ef..0343fbc 100644 --- a/.github/workflows/wfnetcorev2.yaml +++ b/.github/workflows/wfnetcorev2.yaml @@ -100,7 +100,8 @@ jobs: run: dotnet nuget push "*.nupkg" --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --api-key ${{ secrets.GITHUB_TOKEN }} deploy: needs: build - if: github.event_name == 'release' && startsWith(github.ref, 'refs/heads/v') + if: github.event_name == 'release' + #|| startsWith(github.ref, 'refs/heads/v') runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 From d0db1797f656fe96f22aa84ad731b2d4ea094c29 Mon Sep 17 00:00:00 2001 From: ivano scifoni Date: Fri, 20 Oct 2023 18:35:12 +0200 Subject: [PATCH 02/27] Update wfnetcorev2.yaml --- .github/workflows/wfnetcorev2.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/wfnetcorev2.yaml b/.github/workflows/wfnetcorev2.yaml index 0343fbc..ec9fb18 100644 --- a/.github/workflows/wfnetcorev2.yaml +++ b/.github/workflows/wfnetcorev2.yaml @@ -110,6 +110,8 @@ jobs: with: dotnet-version: 7.x source-url: https://nuget.pkg.github.com/shaprcode-it/index.json + env: + NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Create Release NuGet package run: | arrTag=(${GITHUB_REF//\// }) From 36ed252d051ab120ed5bd8d8d9a5f92e6540a2f8 Mon Sep 17 00:00:00 2001 From: ivano scifoni Date: Fri, 20 Oct 2023 18:43:53 +0200 Subject: [PATCH 03/27] Update wfnetcorev2.yaml --- .github/workflows/wfnetcorev2.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/wfnetcorev2.yaml b/.github/workflows/wfnetcorev2.yaml index ec9fb18..77eb1e9 100644 --- a/.github/workflows/wfnetcorev2.yaml +++ b/.github/workflows/wfnetcorev2.yaml @@ -113,13 +113,7 @@ jobs: env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Create Release NuGet package - run: | - arrTag=(${GITHUB_REF//\// }) - VERSION="${arrTag[2]}" - echo Version: $VERSION - VERSION="${VERSION//v}" - echo Clean Version: $VERSION - dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} -o nupkg src/$PROJECT_NAME/$PROJECT_NAME.*proj + run: dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} -o nupkg src/$PROJECT_NAME/$PROJECT_NAME.*proj # - name: Push to GitHub Feed # run: | # for f in ./nupkg/*.nupkg From f2957bc8a432a56473627e8cb1070ab6434dd66f Mon Sep 17 00:00:00 2001 From: ivano scifoni Date: Mon, 23 Oct 2023 09:10:02 +0200 Subject: [PATCH 04/27] Update wfnetcorev2.yaml --- .github/workflows/wfnetcorev2.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wfnetcorev2.yaml b/.github/workflows/wfnetcorev2.yaml index 77eb1e9..7bde791 100644 --- a/.github/workflows/wfnetcorev2.yaml +++ b/.github/workflows/wfnetcorev2.yaml @@ -113,7 +113,8 @@ jobs: env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Create Release NuGet package - run: dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} -o nupkg src/$PROJECT_NAME/$PROJECT_NAME.*proj +# run: dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} -o nupkg src/$PROJECT_NAME/$PROJECT_NAME.*proj + run: dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} ./SharpHelpers/SharpHelpers.sln # - name: Push to GitHub Feed # run: | # for f in ./nupkg/*.nupkg @@ -123,4 +124,4 @@ jobs: - name: Publish the package to GitHub run: dotnet nuget push "*.nupkg" --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --api-key ${{ secrets.GITHUB_TOKEN }} - name: Push to NuGet Feed - run: dotnet nuget push ./nupkg/*.nupkg --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY + run: dotnet nuget push "*.nupkg" --source $NUGET_FEED --skip-duplicate --api-key $NUGET_KEY From 59f29fc77f7dc89540a747a5f187d71eb16e3871 Mon Sep 17 00:00:00 2001 From: ivano scifoni Date: Mon, 23 Oct 2023 09:22:51 +0200 Subject: [PATCH 05/27] Update wfnetcorev2.yaml --- .github/workflows/wfnetcorev2.yaml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/wfnetcorev2.yaml b/.github/workflows/wfnetcorev2.yaml index 7bde791..5713f25 100644 --- a/.github/workflows/wfnetcorev2.yaml +++ b/.github/workflows/wfnetcorev2.yaml @@ -104,23 +104,16 @@ jobs: #|| startsWith(github.ref, 'refs/heads/v') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - name: Download Artifact + uses: actions/download-artifact@v3 + with: + name: nupkg + - name: Display structure of downloaded files + run: ls -R - name: Setup .NET Core uses: actions/setup-dotnet@v3 with: dotnet-version: 7.x - source-url: https://nuget.pkg.github.com/shaprcode-it/index.json - env: - NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - - name: Create Release NuGet package -# run: dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} -o nupkg src/$PROJECT_NAME/$PROJECT_NAME.*proj - run: dotnet pack -v normal -c Release --include-symbols --include-source -p:PackageVersion=${{ steps.gitversion.outputs.nuGetVersionV2 }} ./SharpHelpers/SharpHelpers.sln -# - name: Push to GitHub Feed -# run: | -# for f in ./nupkg/*.nupkg -# do -# curl -vX PUT -u "$GITHUB_USER:$GITHUB_TOKEN" -F package=@$f $GITHUB_FEED -# done - name: Publish the package to GitHub run: dotnet nuget push "*.nupkg" --source "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" --api-key ${{ secrets.GITHUB_TOKEN }} - name: Push to NuGet Feed From 695b412d01901dbcc23b76e376661bca9ada52fe Mon Sep 17 00:00:00 2001 From: "mend-bolt-for-github[bot]" <42819689+mend-bolt-for-github[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:32:08 +0000 Subject: [PATCH 06/27] Add .whitesource configuration file --- .whitesource | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .whitesource diff --git a/.whitesource b/.whitesource new file mode 100644 index 0000000..9c7ae90 --- /dev/null +++ b/.whitesource @@ -0,0 +1,14 @@ +{ + "scanSettings": { + "baseBranches": [] + }, + "checkRunSettings": { + "vulnerableCheckRunConclusionLevel": "failure", + "displayMode": "diff", + "useMendCheckNames": true + }, + "issueSettings": { + "minSeverityLevel": "LOW", + "issueType": "DEPENDENCY" + } +} \ No newline at end of file From fc2ed6ebe731e26e67d013eff947f5ddfc33ac3a Mon Sep 17 00:00:00 2001 From: "p.taliani" Date: Mon, 13 Nov 2023 08:33:03 +0100 Subject: [PATCH 07/27] Fix & add --- .../Boolean/AtomicBooleanTest.cs | 75 +++++++++ .../DateAndTime/DateTimeTest.cs | 41 +++++ SharpHelpers/SharpHelpers/AtomicBoolean.cs | 94 +++++++++++ .../SharpHelpers/AtomicBooleanHelper.cs | 65 ++++++++ SharpHelpers/SharpHelpers/DateTimeSmart.cs | 153 ++++++++++++++++++ SharpHelpers/SharpHelpers/EnumHelper.cs | 4 +- 6 files changed, 430 insertions(+), 2 deletions(-) create mode 100644 SharpHelpers/SharpHelpers.UnitTest/Boolean/AtomicBooleanTest.cs create mode 100644 SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs create mode 100644 SharpHelpers/SharpHelpers/AtomicBoolean.cs create mode 100644 SharpHelpers/SharpHelpers/AtomicBooleanHelper.cs create mode 100644 SharpHelpers/SharpHelpers/DateTimeSmart.cs diff --git a/SharpHelpers/SharpHelpers.UnitTest/Boolean/AtomicBooleanTest.cs b/SharpHelpers/SharpHelpers.UnitTest/Boolean/AtomicBooleanTest.cs new file mode 100644 index 0000000..a2528c9 --- /dev/null +++ b/SharpHelpers/SharpHelpers.UnitTest/Boolean/AtomicBooleanTest.cs @@ -0,0 +1,75 @@ +// (c) 2023 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details)using System; + +using System.Runtime.InteropServices.ComTypes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SharpCoding.SharpHelpers; + +namespace SharpHelpers.UnitTest.Boolean +{ + [TestClass] + public class AtomicBooleanTest + { + [TestMethod] + public void TestBool() + { + var ab1 = new AtomicBoolean(); + + var result = ab1 == true; + Assert.IsFalse(result); + + ab1 = true; + result = ab1 == false; + Assert.IsFalse(result); + + result = ab1 == true; + Assert.IsTrue(result); + + ab1 = false; + result = true == ab1; + Assert.IsFalse(result); + + var ab2 = new AtomicBoolean(); + + result = ab2 == ab1; + Assert.IsTrue(result); + + } + + [TestMethod] + public void TestAnd() + { + var t = new AtomicBoolean(true); + var f = new AtomicBoolean(false); + + Assert.IsTrue(t.And(t)); + Assert.IsFalse(f.And(f)); + Assert.IsFalse(t.And(f)); + Assert.IsFalse(f.And(t)); + } + + [TestMethod] + public void TestOr() + { + var t = new AtomicBoolean(true); + var f = new AtomicBoolean(false); + + Assert.IsFalse(f.Or(f)); + Assert.IsTrue(t.Or(t)); + Assert.IsTrue(t.Or(f)); + Assert.IsTrue(f.Or(t)); + } + + [TestMethod] + public void TestXor() + { + var t = new AtomicBoolean(true); + var f = new AtomicBoolean(false); + + Assert.IsFalse(t.Xor(t)); + Assert.IsFalse(f.Xor(f)); + Assert.IsTrue(t.Xor(f)); + Assert.IsTrue(f.Xor(t)); + } + } +} diff --git a/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs b/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs new file mode 100644 index 0000000..4e09748 --- /dev/null +++ b/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs @@ -0,0 +1,41 @@ +// (c) 2023 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details) + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SharpCoding.SharpHelpers; +using System.Linq; + + +namespace SharpHelpers.UnitTest.DateAndTime +{ + + + [TestClass] + public class DateTimeTest + { + [TestMethod] + public void TestDateTimeSmart() + { + var dtDictionary = new System.Collections.Generic.Dictionary() + { + {"M/dd/yyyy hh:mm", "10/11/2023 09:00"}, + {"MM/dd/yyyy hh:mm:ss tt zzz", "05/01/2009 01:30:42 PM -05:00"}, + {"g", "10/11/2023 09:00"} + }; + + DateTimeSmart.AddFormats(dtDictionary.Keys.ToArray()); + + foreach (var key in dtDictionary.Keys) + { + DateTime dt = (DateTimeSmart)dtDictionary[key]; + Assert.IsFalse(dt == DateTime.MinValue); + } + + DateTime dt1 = (DateTimeSmart)"01-05-2023 22:30:55"; + Assert.IsTrue(dt1 == new DateTime( 2023, 5, 1, 22, 30, 55, DateTimeKind.Local)); + + } + + } +} diff --git a/SharpHelpers/SharpHelpers/AtomicBoolean.cs b/SharpHelpers/SharpHelpers/AtomicBoolean.cs new file mode 100644 index 0000000..b199f85 --- /dev/null +++ b/SharpHelpers/SharpHelpers/AtomicBoolean.cs @@ -0,0 +1,94 @@ +// (c) 2023 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details)using System; + +using System; +using System.Data.Common; +using System.Threading; + +namespace SharpCoding.SharpHelpers +{ + /// + /// This class represents a boolean value that may be updated atomically. + /// It is designed to be thread-safe and can be used in multi-threaded environments + /// where atomic operations are required. + /// + public class AtomicBoolean : IEquatable + { + public static readonly AtomicBoolean False = new AtomicBoolean(false); + public static readonly AtomicBoolean True = new AtomicBoolean(true); + + /// + /// A private integer field that holds the atomic boolean value. It uses 0 for false and 1 for true. + /// + private int _value; + /// + /// FALSE and TRUE: Private constants representing the integer values of false and true respectively. + /// + private const int FALSE = 0; + private const int TRUE = 1; + + /// + /// The constructor that initializes the atomic boolean with a specified value. + /// It uses the Interlocked.Exchange method to safely set the initial value in a thread-safe manner. + /// + /// Initial value of the instance. Is false by default. + public AtomicBoolean(bool value = false) + { + Interlocked.Exchange(ref _value, value ? TRUE : FALSE); + } + + /// + /// A private property that gets the current boolean value of the atomic boolean. + /// It uses the Interlocked.CompareExchange method to safely get the current value in a thread-safe manner. + /// + private bool Value => Interlocked.CompareExchange(ref _value, FALSE, FALSE) == TRUE; + + /// + /// An implicit conversion operator that allows an AtomicBoolean to be used where a bool is expected. + /// + /// + public static implicit operator bool(AtomicBoolean abool) => abool.Value; + + /// + /// An implicit conversion operator that allows a bool to be used where an AtomicBoolean is expected. + /// + /// + public static implicit operator AtomicBoolean(bool v) => new AtomicBoolean(v); + + #region Equality members + + /// + public bool Equals(AtomicBoolean other) + { + if (other is null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + return _value == other._value; + } + + /// + public override bool Equals(object obj) + { + if (obj is null) + return false; + + if (ReferenceEquals(this, obj)) + return true; + + return obj.GetType() == GetType() && Equals((AtomicBoolean)obj); + } + + /// + // ReSharper disable once NonReadonlyMemberInGetHashCode + public override int GetHashCode() => _value; + + public static bool operator ==(AtomicBoolean left, AtomicBoolean right) => Equals(left, right); + + public static bool operator !=(AtomicBoolean left, AtomicBoolean right) => !Equals(left, right); + + #endregion + } +} \ No newline at end of file diff --git a/SharpHelpers/SharpHelpers/AtomicBooleanHelper.cs b/SharpHelpers/SharpHelpers/AtomicBooleanHelper.cs new file mode 100644 index 0000000..8c2a602 --- /dev/null +++ b/SharpHelpers/SharpHelpers/AtomicBooleanHelper.cs @@ -0,0 +1,65 @@ +// (c) 2023 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details)using System; + +namespace SharpCoding.SharpHelpers +{ + /// + /// The AtomicBooleanHelper class is a static class that provides extension methods for the AtomicBoolean class. + /// + public static class AtomicBooleanHelper + { + /// + /// This extension method performs a logical XOR operation on the instance and op2. + /// It returns true if the instance is different from op2. + /// + /// + /// + /// + public static bool Xor(this AtomicBoolean instance, AtomicBoolean op2) => ((bool)instance).Xor(op2); + + /// + /// This extension method performs a logical AND operation on the instance and op2. + /// It returns true if both the instance and op2 are true. + /// + /// + /// + /// + public static bool And(this AtomicBoolean instance, AtomicBoolean op2) => ((bool)instance).And(op2); + + /// + /// This extension method performs a logical OR operation on the instance and op2. + /// It returns true if either the instance that invokes the method or op2 is true. + /// It also returns true when both are true. + /// + /// + /// + /// + public static bool Or(this AtomicBoolean instance, AtomicBoolean op2) => ((bool)instance).Or(op2); + + /// + /// This extension method maps the instance value to one of the two parameters. + /// If the value is false or null, it returns falseValue; otherwise, it returns trueValue. + /// + /// + /// + /// + /// + //public static string ToStringValues(this AtomicBoolean value, string trueValue, string falseValue) => (value == null || value == false) ? falseValue : trueValue; + public static string ToStringValues(this AtomicBoolean value, string trueValue, string falseValue) => (value.GetValueOrDefault() == false) ? falseValue : trueValue; + + /// + /// This extension method retrieves the value of the current AtomicBoolean object or the false value if the object is null. + /// + /// + /// + public static AtomicBoolean GetValueOrDefault(this AtomicBoolean value) => value.GetValueOrDefault(AtomicBoolean.False); + + /// + /// This extension method retrieves the value of the current AtomicBoolean object or the defaultValue if the object is null. + /// + /// + /// + /// + public static AtomicBoolean GetValueOrDefault(this AtomicBoolean value, AtomicBoolean defaultValue) => value ?? defaultValue; + } +} \ No newline at end of file diff --git a/SharpHelpers/SharpHelpers/DateTimeSmart.cs b/SharpHelpers/SharpHelpers/DateTimeSmart.cs new file mode 100644 index 0000000..c933b44 --- /dev/null +++ b/SharpHelpers/SharpHelpers/DateTimeSmart.cs @@ -0,0 +1,153 @@ +// (c) 2023 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details)using System; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + + +namespace SharpCoding.SharpHelpers +{ + /// + /// The DateTimeSmart class is a wrapper around the DateTime structure, + /// providing additional functionality for cast-from-string operations. + /// + public class DateTimeSmart + { + private static string[] _formats = { "dd-MM-yyyy", "yyyy-MM-dd", "dd-MM-yyyy HH:mm:ss" }; + /// + /// A private constructor that initializes a new instance of the DateTimeSmart class with a specified DateTime value + /// + /// + private DateTimeSmart(DateTime dateTime) + { + DateTime = new DateTime(dateTime.Ticks, dateTime.Kind); + } + + /// + /// A private static field that serves as a lock to ensure thread safety when modifying the _formats field. + /// + private static readonly object _formatsLocker = new object(); + + /// + /// A private static method that checks if a string can be a format string for a DateTime object. + /// + /// + /// + /// Nice to have: find a way to validate any format strings regardless of localization and without using try parsing. + /// + /// + private static bool IsValidDateTimeFormat(string format) + { + try + { + var dtIn = DateTime.Now.ToString(format); + // This is not a foolproof method. + return string.Compare(dtIn, format, StringComparison.InvariantCultureIgnoreCase) != 0; + } + catch + { + return false; + } + } + + + /// + /// A public static method that adds new date formats that the class can handle. + /// + /// + /// + /// + /// + public static bool AddFormats(params string[] formats) + { + if (formats == null || formats.Length == 0) + { + throw new ArgumentNullException(nameof(formats)); + } + + var extras = formats.Except(_formats).ToList(); + if (extras.TrueForAll(IsValidDateTimeFormat) == false) + { + throw new ArgumentException("Some string is not in the right format to parse a DateTime.", nameof(formats)); + } + + if (extras.Any()) + { + lock (_formatsLocker) + { + _formats = _formats.Concat(extras).ToArray(); + return true; + } + } + + return false; + } + + /// + /// A public static method that lists the date formats that the class currently can handle. + /// + public static IEnumerable GetCurrentFormats() => _formats; + + /// + /// A private property that gets or sets the internal DateTime value. + /// + private DateTime DateTime { get; set; } + + /// + /// An explicit conversion operator that converts a string to a DateTimeSmart object. + /// + /// + public static explicit operator DateTimeSmart(string date) + { + DateTime.TryParseExact(date, _formats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dateTime); + return new DateTimeSmart(dateTime); + } + + /// + /// An explicit conversion operator that converts a DateTime object to a DateTimeSmart object. + /// + /// + public static explicit operator DateTimeSmart(DateTime dateTime) + { + var dt = dateTime.Kind != DateTimeKind.Unspecified ? dateTime : DateTime.SpecifyKind(dateTime, DateTimeKind.Local); + return new DateTimeSmart(dt); + } + + /// + /// An implicit conversion operator that converts a DateTimeSmart object to a DateTime object. + /// + /// + public static implicit operator DateTime(DateTimeSmart dummy) => dummy.DateTime; + + /// + /// An override of the ToString method that returns the string representation of the DateTime object + /// in the "yyyy-MM-dd HH:mm:ss.fff" format. + /// + /// + public override string ToString() => this.ToString("yyyy-MM-dd HH:mm:ss.fff"); + + /// + /// Overloads of the ToString method that return the string representation of the DateTime object in a specified format + /// + /// + /// + public string ToString(string format) => DateTime.ToString(format); + + /// + /// Overloads of the ToString method that return the string representation of the DateTime object in a specified format and with a specified format provider. + /// + /// + /// + /// + public string ToString(string format, IFormatProvider provider) => DateTime.ToString(format, provider); + + /// + /// Overloads of the ToString method that return the string representation of the DateTime object in a specified format provider. + /// + /// + /// + public string ToString(IFormatProvider provider) => DateTime.ToString(provider); + } +} diff --git a/SharpHelpers/SharpHelpers/EnumHelper.cs b/SharpHelpers/SharpHelpers/EnumHelper.cs index bcf9832..594b8bb 100755 --- a/SharpHelpers/SharpHelpers/EnumHelper.cs +++ b/SharpHelpers/SharpHelpers/EnumHelper.cs @@ -15,8 +15,8 @@ public static class EnumHelper /// public static string GetDescription(this Enum value) { - var description = GetAttribute(value); - return description?.Description; + var description = GetAttribute(value)?.Description; + return string.IsNullOrEmpty(description) ? value.ToString() : description; } /// From b717546d2d327e420a4ecb1014c330f60819932b Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Fri, 16 Aug 2024 12:12:35 +0200 Subject: [PATCH 08/27] Some new IEnumerable extensions method --- .../SharpHelpers.UnitTest.csproj | 6 +- SharpHelpers/SharpHelpers/EnumerableHelper.cs | 120 ++++++++++++++++++ 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj index 6c6b143..056e5df 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj +++ b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj @@ -7,9 +7,9 @@ - - - + + + diff --git a/SharpHelpers/SharpHelpers/EnumerableHelper.cs b/SharpHelpers/SharpHelpers/EnumerableHelper.cs index 0b5423b..8ffd44f 100644 --- a/SharpHelpers/SharpHelpers/EnumerableHelper.cs +++ b/SharpHelpers/SharpHelpers/EnumerableHelper.cs @@ -148,5 +148,125 @@ public static IEnumerable> Split(this IEnumerable enumerable, int } return splitList; } + + /// + /// Check if the list is null or empty. + /// + /// + /// + /// + public static bool IsNullOrEmpty(this IEnumerable source) + { + return source == null || !source.Any(); + } + + /// + /// Apply an action to each element of the list. + /// + /// + /// + /// + /// + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (action == null) throw new ArgumentNullException(nameof(action)); + + foreach (var item in source) + { + action(item); + } + } + + /// + /// Chunk the list into sublists of the specified size. + /// + /// + /// + /// + /// + /// + /// + public static IEnumerable> ChunkBy(this IEnumerable source, int size) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size), "La dimensione deve essere maggiore di zero."); + + return source.Select((x, i) => new { Index = i, Value = x }) + .GroupBy(x => x.Index / size) + .Select(g => g.Select(x => x.Value)); + } + + /// + /// Get a random element from the list. + /// + /// + /// + /// + /// + /// + public static T RandomElement(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + var list = source.ToList(); + + if (list.Count == 0) + throw new InvalidOperationException("La sequenza non contiene elementi."); + + Random rng = new Random(); + int index = rng.Next(list.Count); + return list[index]; + } + + /// + /// Check if all elements in the list are distinct. + /// + /// + /// + /// + /// + public static bool AllDistinct(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + var seen = new HashSet(); + foreach (var item in source) + { + if (!seen.Add(item)) + return false; + } + return true; + } + + /// + /// Shuffle the list. + /// + /// + /// + /// + /// + public static IEnumerable Shuffle(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + Random rng = new Random(); + return source.OrderBy(_ => rng.Next()); + } + + /// + /// Sum the elements of the list. + /// + /// + /// + /// + /// + /// + public static int Sum(this IEnumerable source, Func selector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + return source.Select(selector).Sum(); + } } } From 9ee012c06eb6c18f60354227531a4c4b9156d264 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 18 Aug 2024 12:49:19 +0200 Subject: [PATCH 09/27] Add some new DateTime helpers --- .../DateAndTime/DateTimeTest.cs | 78 +++++++++++++++++++ SharpHelpers/SharpHelpers/DateTimeHelper.cs | 60 +++++++++++++- 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs diff --git a/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs b/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs new file mode 100644 index 0000000..1521494 --- /dev/null +++ b/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs @@ -0,0 +1,78 @@ +// (c) 2023 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details) + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SharpCoding.SharpHelpers; +using System.Linq; + +namespace SharpHelpers.UnitTest.DateAndTime +{ + [TestClass] + public class DateTimeTest + { + [TestMethod] + public void TestDateTimeSmart() + { + var dtDictionary = new System.Collections.Generic.Dictionary() + { + {"M/dd/yyyy hh:mm", "10/11/2023 09:00"}, + {"MM/dd/yyyy hh:mm:ss tt zzz", "05/01/2009 01:30:42 PM -05:00"}, + {"g", "10/11/2023 09:00"} + }; + + DateTimeSmart.AddFormats(dtDictionary.Keys.ToArray()); + + foreach (var key in dtDictionary.Keys) + { + DateTime dt = (DateTimeSmart)dtDictionary[key]; + Assert.IsFalse(dt == DateTime.MinValue); + } + + DateTime dt1 = (DateTimeSmart)"01-05-2023 22:30:55"; + Assert.IsTrue(dt1 == new DateTime( 2023, 5, 1, 22, 30, 55, DateTimeKind.Local)); + } + + [TestMethod] + public void IsLeapYear_ShouldReturnTrueForLeapYear() + { + var leapYear = new DateTime(2024, 1, 1); + var isLeapYear = leapYear.IsLeapYear(); + + Assert.IsTrue(isLeapYear); + } + + [TestMethod] + public void Age_ShouldCalculateCorrectAge() + { + var birthDate = new DateTime(1990, 8, 18); + var today = new DateTime(2024, 8, 18); + var expectedAge = 34; + + var age = birthDate.Age(); + + Assert.AreEqual(expectedAge, age); + } + + [TestMethod] + public void AddBusinessDays_ShouldSkipWeekends() + { + var startDate = new DateTime(2024, 8, 16); // Friday + var expected = new DateTime(2024, 8, 20); // 2 business days later (Tuesday) + + var result = startDate.AddBusinessDays(2); + + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void IsWeekend_ShouldReturnFalseForWeekday() + { + var monday = new DateTime(2024, 8, 19); + + var isMondayWeekend = monday.IsWeekend(); + + Assert.IsFalse(isMondayWeekend); + } + } +} diff --git a/SharpHelpers/SharpHelpers/DateTimeHelper.cs b/SharpHelpers/SharpHelpers/DateTimeHelper.cs index 63260e6..5f4ba48 100644 --- a/SharpHelpers/SharpHelpers/DateTimeHelper.cs +++ b/SharpHelpers/SharpHelpers/DateTimeHelper.cs @@ -97,5 +97,63 @@ public static bool IsBetween(this DateTime dt, DateTime rangeBeg, DateTime range { return dt.Ticks >= rangeBeg.Ticks && dt.Ticks <= rangeEnd.Ticks; } + + /// + /// Check if a day is a weekend. + /// + /// + /// + public static bool IsWeekend(this DateTime dateTime) + { + return dateTime.DayOfWeek == DayOfWeek.Saturday || dateTime.DayOfWeek == DayOfWeek.Sunday; + } + + /// + /// Add business days to a DateTime. + /// + /// + /// + /// + public static DateTime AddBusinessDays(this DateTime dateTime, int businessDays) + { + if (businessDays == 0) + return dateTime; + + int direction = businessDays > 0 ? 1 : -1; + int daysToAdd = Math.Abs(businessDays); + + while (daysToAdd > 0) + { + dateTime = dateTime.AddDays(direction); + if (!dateTime.IsWeekend()) + daysToAdd--; + } + + return dateTime; + } + + /// + /// Return the age of a person. + /// + /// + /// + public static int Age(this DateTime birthDate) + { + var today = DateTime.Today; + var age = today.Year - birthDate.Year; + if (birthDate.Date > today.AddYears(-age)) age--; + return age; + } + + /// + /// Check if the year is a leap year. + /// + /// + /// + public static bool IsLeapYear(this DateTime dateTime) + { + int year = dateTime.Year; + return DateTime.IsLeapYear(year); + } } -} +} \ No newline at end of file From 2dfea7979155f5b8cb8e0fa21b2301f2504ba81c Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 18 Aug 2024 12:49:51 +0200 Subject: [PATCH 10/27] Add some new DateTime helpers --- SharpHelpers/SharpHelpers/DateTimeHelper.cs | 86 ++++++++++----------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/SharpHelpers/SharpHelpers/DateTimeHelper.cs b/SharpHelpers/SharpHelpers/DateTimeHelper.cs index 5f4ba48..19eac81 100644 --- a/SharpHelpers/SharpHelpers/DateTimeHelper.cs +++ b/SharpHelpers/SharpHelpers/DateTimeHelper.cs @@ -1,8 +1,8 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) using System; -using System.Globalization; - +using System.Globalization; + namespace SharpCoding.SharpHelpers { public static class DateTimeHelper @@ -46,56 +46,56 @@ public static DateTime AbsoluteEnd(this DateTime dateTime) public static DateTime? ToUniversalTime(this DateTime? dateTime) { return dateTime?.ToUniversalTime(); - } - - /// - /// Return the start of the current day - /// - /// - /// - public static DateTime GetStartOfDay(this DateTime dateTime) - { - return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0); } - /// - /// Return the end of the current day - /// - /// + /// + /// Return the start of the current day + /// + /// /// - public static DateTime GetEndOfDay(this DateTime dateTime) - { - return new DateTime(dateTime.Year, dateTime.Month, - dateTime.Day, 23, 59, 59, 999); + public static DateTime GetStartOfDay(this DateTime dateTime) + { + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0); } - /// - /// Try to parse the string value with specific DateTime formats - /// - /// - /// + /// + /// Return the end of the current day + /// + /// /// - public static DateTime? AsDateTime(this string value, string[] formats) - { - if (DateTime.TryParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, - out DateTime utcDateTime)) - { - return utcDateTime; - } - - return null; + public static DateTime GetEndOfDay(this DateTime dateTime) + { + return new DateTime(dateTime.Year, dateTime.Month, + dateTime.Day, 23, 59, 59, 999); } - /// - /// Check if a date is between two dates - /// - /// - /// - /// + /// + /// Try to parse the string value with specific DateTime formats + /// + /// + /// /// - public static bool IsBetween(this DateTime dt, DateTime rangeBeg, DateTime rangeEnd) - { - return dt.Ticks >= rangeBeg.Ticks && dt.Ticks <= rangeEnd.Ticks; + public static DateTime? AsDateTime(this string value, string[] formats) + { + if (DateTime.TryParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, + out DateTime utcDateTime)) + { + return utcDateTime; + } + + return null; + } + + /// + /// Check if a date is between two dates + /// + /// + /// + /// + /// + public static bool IsBetween(this DateTime dt, DateTime rangeBeg, DateTime rangeEnd) + { + return dt.Ticks >= rangeBeg.Ticks && dt.Ticks <= rangeEnd.Ticks; } /// From c6be3993e398919276703ca2ea9cffc0e76360ec Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 18 Aug 2024 13:15:56 +0200 Subject: [PATCH 11/27] Add some new DateTime helpers --- .../DateAndTime/DateTimeTest.cs | 46 +++++- SharpHelpers/SharpHelpers/DateTimeHelper.cs | 144 ++++++++++++------ 2 files changed, 142 insertions(+), 48 deletions(-) diff --git a/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs b/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs index 4e09748..7eee216 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs +++ b/SharpHelpers/SharpHelpers.UnitTest/DateAndTime/DateTimeTest.cs @@ -1,16 +1,12 @@ // (c) 2023 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) - using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using SharpCoding.SharpHelpers; using System.Linq; - namespace SharpHelpers.UnitTest.DateAndTime { - - [TestClass] public class DateTimeTest { @@ -33,9 +29,49 @@ public void TestDateTimeSmart() } DateTime dt1 = (DateTimeSmart)"01-05-2023 22:30:55"; - Assert.IsTrue(dt1 == new DateTime( 2023, 5, 1, 22, 30, 55, DateTimeKind.Local)); + Assert.IsTrue(dt1 == new DateTime(2023, 5, 1, 22, 30, 55, DateTimeKind.Local)); + } + [TestMethod] + public void IsLeapYear_ShouldReturnTrueForLeapYear() + { + var leapYear = new DateTime(2024, 1, 1); + var isLeapYear = leapYear.IsLeapYear(); + + Assert.IsTrue(isLeapYear); } + [TestMethod] + public void Age_ShouldCalculateCorrectAge() + { + var birthDate = new DateTime(1990, 8, 18); + var today = new DateTime(2024, 8, 18); + var expectedAge = 34; + + var age = birthDate.Age(); + + Assert.AreEqual(expectedAge, age); + } + + [TestMethod] + public void AddBusinessDays_ShouldSkipWeekends() + { + var startDate = new DateTime(2024, 8, 16); // Friday + var expected = new DateTime(2024, 8, 20); // 2 business days later (Tuesday) + + var result = startDate.AddBusinessDays(2); + + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void IsWeekend_ShouldReturnFalseForWeekday() + { + var monday = new DateTime(2024, 8, 19); + + var isMondayWeekend = monday.IsWeekend(); + + Assert.IsFalse(isMondayWeekend); + } } } diff --git a/SharpHelpers/SharpHelpers/DateTimeHelper.cs b/SharpHelpers/SharpHelpers/DateTimeHelper.cs index 63260e6..3d1a31e 100644 --- a/SharpHelpers/SharpHelpers/DateTimeHelper.cs +++ b/SharpHelpers/SharpHelpers/DateTimeHelper.cs @@ -1,8 +1,8 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) using System; -using System.Globalization; - +using System.Globalization; + namespace SharpCoding.SharpHelpers { public static class DateTimeHelper @@ -46,56 +46,114 @@ public static DateTime AbsoluteEnd(this DateTime dateTime) public static DateTime? ToUniversalTime(this DateTime? dateTime) { return dateTime?.ToUniversalTime(); - } - - /// - /// Return the start of the current day - /// - /// - /// - public static DateTime GetStartOfDay(this DateTime dateTime) - { - return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0); } - /// - /// Return the end of the current day - /// - /// + /// + /// Return the start of the current day + /// + /// /// - public static DateTime GetEndOfDay(this DateTime dateTime) - { - return new DateTime(dateTime.Year, dateTime.Month, - dateTime.Day, 23, 59, 59, 999); + public static DateTime GetStartOfDay(this DateTime dateTime) + { + return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, 0, 0, 0, 0); } - /// - /// Try to parse the string value with specific DateTime formats - /// - /// - /// + /// + /// Return the end of the current day + /// + /// /// - public static DateTime? AsDateTime(this string value, string[] formats) - { - if (DateTime.TryParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, - out DateTime utcDateTime)) - { - return utcDateTime; - } - - return null; + public static DateTime GetEndOfDay(this DateTime dateTime) + { + return new DateTime(dateTime.Year, dateTime.Month, + dateTime.Day, 23, 59, 59, 999); } - /// - /// Check if a date is between two dates - /// - /// - /// - /// + /// + /// Try to parse the string value with specific DateTime formats + /// + /// + /// + /// + public static DateTime? AsDateTime(this string value, string[] formats) + { + if (DateTime.TryParseExact(value, formats, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, + out DateTime utcDateTime)) + { + return utcDateTime; + } + + return null; + } + + /// + /// Check if a date is between two dates + /// + /// + /// + /// + /// + public static bool IsBetween(this DateTime dt, DateTime rangeBeg, DateTime rangeEnd) + { + return dt.Ticks >= rangeBeg.Ticks && dt.Ticks <= rangeEnd.Ticks; + } + + /// + /// Check if a day is a weekend. + /// + /// /// - public static bool IsBetween(this DateTime dt, DateTime rangeBeg, DateTime rangeEnd) - { - return dt.Ticks >= rangeBeg.Ticks && dt.Ticks <= rangeEnd.Ticks; + public static bool IsWeekend(this DateTime dateTime) + { + return dateTime.DayOfWeek == DayOfWeek.Saturday || dateTime.DayOfWeek == DayOfWeek.Sunday; + } + + /// + /// Add business days to a DateTime. + /// + /// + /// + /// + public static DateTime AddBusinessDays(this DateTime dateTime, int businessDays) + { + if (businessDays == 0) + return dateTime; + + int direction = businessDays > 0 ? 1 : -1; + int daysToAdd = Math.Abs(businessDays); + + while (daysToAdd > 0) + { + dateTime = dateTime.AddDays(direction); + if (!dateTime.IsWeekend()) + daysToAdd--; + } + + return dateTime; + } + + /// + /// Return the age of a person. + /// + /// + /// + public static int Age(this DateTime birthDate) + { + var today = DateTime.Today; + var age = today.Year - birthDate.Year; + if (birthDate.Date > today.AddYears(-age)) age--; + return age; + } + + /// + /// Check if the year is a leap year. + /// + /// + /// + public static bool IsLeapYear(this DateTime dateTime) + { + int year = dateTime.Year; + return DateTime.IsLeapYear(year); } } } From 317f3cfc3ef4ea20bbe0de664e4b490b488f9180 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Thu, 22 Aug 2024 15:56:51 +0200 Subject: [PATCH 12/27] Add some Regex extension methods --- .../SharpHelpers.UnitTest/Regex/RegexTests.cs | 96 +++++++++++++++++++ SharpHelpers/SharpHelpers/RegexHelper.cs | 51 +++++++++- 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 SharpHelpers/SharpHelpers.UnitTest/Regex/RegexTests.cs diff --git a/SharpHelpers/SharpHelpers.UnitTest/Regex/RegexTests.cs b/SharpHelpers/SharpHelpers.UnitTest/Regex/RegexTests.cs new file mode 100644 index 0000000..3540521 --- /dev/null +++ b/SharpHelpers/SharpHelpers.UnitTest/Regex/RegexTests.cs @@ -0,0 +1,96 @@ +// (c) 2019 SharpCoding +// This code is licensed under MIT license (see LICENSE.txt for details) +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SharpCoding.SharpHelpers; + +namespace SharpHelpers.UnitTest.Regex +{ + [TestClass] + public class RegexTests + { + [TestMethod] + public void IsMatch_ShouldReturnTrue_WhenPatternMatches() + { + // Arrange + string input = "hello world"; + string pattern = @"hello"; + + // Act + bool result = input.IsMatchRegex(pattern); + + // Assert + Assert.IsTrue(result, "Expected the pattern to match the input string."); + } + + [TestMethod] + public void IsMatch_ShouldReturnFalse_WhenPatternDoesNotMatch() + { + // Arrange + string input = "hello world"; + string pattern = @"goodbye"; + + // Act + bool result = input.IsMatchRegex(pattern); + + // Assert + Assert.IsFalse(result, "Expected the pattern not to match the input string."); + } + + [TestMethod] + public void Match_ShouldReturnFirstMatch_WhenPatternMatches() + { + // Arrange + string input = "hello world"; + string pattern = @"hello"; + + // Act + string result = input.Match(pattern); + + // Assert + Assert.AreEqual("hello", result, "Expected the first match to be 'hello'."); + } + + [TestMethod] + public void Match_ShouldReturnNull_WhenPatternDoesNotMatch() + { + // Arrange + string input = "hello world"; + string pattern = @"goodbye"; + + // Act + string result = input.Match(pattern); + + // Assert + Assert.IsNull(result, "Expected no match to be found."); + } + + [TestMethod] + public void Replace_ShouldReplaceAllOccurrences_WhenPatternMatches() + { + // Arrange + string input = "hello world world"; + string pattern = @"world"; + string replacement = "universe"; + + // Act + string result = input.Replace(pattern, replacement); + + // Assert + Assert.AreEqual("hello universe universe", result, "Expected 'world' to be replaced with 'universe'."); + } + + [TestMethod] + public void Split_ShouldReturnArray_WhenPatternIsUsedAsDelimiter() + { + // Arrange + string input = "hello world universe"; + string pattern = @" "; + + // Act + string[] result = input.Split(pattern); + + // Assert + CollectionAssert.AreEqual(new[] { "hello", "world", "universe" }, result, "Expected the input string to be split by spaces."); + } + } +} diff --git a/SharpHelpers/SharpHelpers/RegexHelper.cs b/SharpHelpers/SharpHelpers/RegexHelper.cs index 37f8f6a..ce3284f 100755 --- a/SharpHelpers/SharpHelpers/RegexHelper.cs +++ b/SharpHelpers/SharpHelpers/RegexHelper.cs @@ -1,5 +1,6 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) +using System; using System.Text.RegularExpressions; namespace SharpCoding.SharpHelpers @@ -14,8 +15,56 @@ public static class RegexHelper /// public static bool IsMatchRegex(this string value, string pattern) { + if (value == null) throw new ArgumentNullException(nameof(value)); + if (pattern == null) throw new ArgumentNullException(nameof(pattern)); + var regex = new Regex(pattern); - return (regex.IsMatch(value)); + return regex.IsMatch(value); + } + + /// + /// Retrieves the first match found in the input string. + /// + /// The input string to search. + /// The regular expression pattern. + /// The first match found, or null if no match is found. + public static string Match(this string input, string pattern) + { + if (input == null) throw new ArgumentNullException(nameof(input)); + if (pattern == null) throw new ArgumentNullException(nameof(pattern)); + + var match = Regex.Match(input, pattern); + return match.Success ? match.Value : null; + } + + /// + /// Replaces all occurrences of a pattern with a replacement string. + /// + /// The input string to modify. + /// The regular expression pattern. + /// The replacement string. + /// A new string with all occurrences replaced. + public static string Replace(this string input, string pattern, string replacement) + { + if (input == null) throw new ArgumentNullException(nameof(input)); + if (pattern == null) throw new ArgumentNullException(nameof(pattern)); + if (replacement == null) throw new ArgumentNullException(nameof(replacement)); + + return Regex.Replace(input, pattern, replacement); + } + + /// + /// Splits a string into an array of substrings using a regex pattern as a delimiter. + /// + /// The input string to split. + /// The regular expression pattern to use as a delimiter. + /// An array of substrings. + public static string[] Split(this string input, string pattern) + { + if (input == null) throw new ArgumentNullException(nameof(input)); + if (pattern == null) throw new ArgumentNullException(nameof(pattern)); + + return Regex.Split(input, pattern); } } } From b5f4dbd98a91eafbd9f03bbc5cc8c115fa8970de Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Mon, 26 Aug 2024 19:00:39 +0200 Subject: [PATCH 13/27] Update README.md --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0f40fe6..6da2b13 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -[![Github license](mit.svg)](https://github.com/sharpcode-it/SharpHelpers/blob/master/LICENSE) +# SharpHelpers (SharpCoding Community Library) + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Nuget](https://img.shields.io/nuget/v/SharpHelpers?style=plastic)](https://www.nuget.org/packages/SharpHelpers) +[![issues - SharpHelpers](https://img.shields.io/github/issues/sharpcode-it/SharpHelpers)](https://github.com/sharpcode-it/SharpHelpers/issues) +[![stars - SharpHelpers](https://img.shields.io/github/stars/sharpcode-it/SharpHelpers?style=social)](https://github.com/sharpcode-it/SharpHelpers) |Version|Status| |:-:|:-:| @@ -6,7 +11,6 @@ |Master|![.NET Core](https://github.com/sharpcode-it/SharpHelpers/workflows/.NET%20Core/badge.svg?branch=master)| |v1.0|![.NET Core](https://github.com/sharpcode-it/SharpHelpers/workflows/.NET%20Core/badge.svg?branch=v1.0)| -# SharpHelpers (SharpCoding Community Library) -------------------------------------- ## What is this? @@ -14,13 +18,13 @@ SharpHelpers is a collections of some handy code packages and tutorials to make Get SharpHelpers: - git clone git://github.com/sharpcodingIT/SharpHelpers/ +`git clone git://github.com/sharpcodingIT/SharpHelpers/` -------------------------------------- ## What do i find? The library contains a series of Helpers, under MIT license for use and consumption of any developer. -The project contains several helpers for the manipulation and management of different types of primitive type: +The project includes various helpers designed for the manipulation and management of different primitive types: - Boolean - Byte - Enum @@ -35,6 +39,7 @@ The project contains several helpers for the manipulation and management of diff - DateTime - Regex - XmlDocument +- DataTable -------------------------------------- ## Contributing @@ -46,6 +51,11 @@ Want to contribute? Great! Here are a few guidelines. 3. All code should have a unit test. If you make a feature, there should be significant tests around the feature. If you do a bug fix, there should be a test specific to that bug so it doesn't happen again. 4. Pull requests should have a single commit. If you have multiple commits, squash them into a single commit before requesting a pull. 5. Try and follow the code styling already in place. + + * [Setting up Git](https://docs.github.com/en/get-started/getting-started-with-git/set-up-git) + * [Fork the repository](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) + * [Open an issue](https://github.com/sharpcode-it/SharpHelpers/issues) if you encounter a bug or have a suggestion for improvements/features + -------------------------------------- ### License From 9acd0d33307c55c96998dcedc2d4efbb4307c63d Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Tue, 27 Aug 2024 11:32:18 +0200 Subject: [PATCH 14/27] Add some Dictionary methods Refactor Dictionary Unit Tests --- .../Dictionary/DictionaryTest.cs | 179 ++++++++++++++++-- .../SharpHelpers.UnitTest.csproj | 2 +- SharpHelpers/SharpHelpers/DictionaryHelper.cs | 57 ++++++ 3 files changed, 221 insertions(+), 17 deletions(-) diff --git a/SharpHelpers/SharpHelpers.UnitTest/Dictionary/DictionaryTest.cs b/SharpHelpers/SharpHelpers.UnitTest/Dictionary/DictionaryTest.cs index 6e63f7e..27535ae 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/Dictionary/DictionaryTest.cs +++ b/SharpHelpers/SharpHelpers.UnitTest/Dictionary/DictionaryTest.cs @@ -4,37 +4,184 @@ using SharpCoding.SharpHelpers; using System; using System.Collections.Generic; -using System.Linq; namespace SharpHelpers.UnitTest.Dictionary { - [TestClass] public class DictionaryTest { [TestMethod] - public void TestAddFormat() + public void AddFormat_ShouldAddFormattedStringToDictionary() { - var dic = new Dictionary() { { 1, DateTime.Now }, { 2, DateTime.Now } }; - + // Arrange + var dictionary = new Dictionary(); + + // Act + dictionary.AddFormat(1, "Hello, {0}!", "World"); + + // Assert + Assert.AreEqual("Hello, World!", dictionary[1]); } + [TestMethod] - public void TestRemoveAll() + [ExpectedException(typeof(ArgumentNullException))] + public void AddFormat_ShouldThrowArgumentNullException_WhenDictionaryIsNull() { - var dic = new Dictionary() { { 1, DateTime.Now }, { 2, DateTime.Now } }; - dic.RemoveAll(a=> a.Key < 2 ); - Assert.IsTrue(dic.Count() == 1); + // Arrange + Dictionary dictionary = null; + // Act + dictionary.AddFormat(1, "Hello, {0}!", "World"); + + // Assert - [ExpectedException] handles the assertion } + [TestMethod] - public void TestGetOrCreate() + public void RemoveAll_ShouldRemoveItemsBasedOnCondition() { - var dic = new Dictionary() { { 1, DateTime.Now }, { 2, DateTime.Now} }; - Assert.IsNotNull(dic.GetOrCreate(1)); - Assert.IsNotNull(dic.GetOrCreate(3)); + // Arrange + var dictionary = new Dictionary + { + { 1, "Apple" }, + { 2, "Banana" }, + { 3, "Avocado" } + }; - + // Act + dictionary.RemoveAll(kvp => kvp.Value.StartsWith("A")); + + // Assert + Assert.AreEqual(1, dictionary.Count); + Assert.IsTrue(dictionary.ContainsKey(2)); } - } -} + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void RemoveAll_ShouldThrowArgumentNullException_WhenDictionaryIsNull() + { + // Arrange + Dictionary dictionary = null; + + // Act + dictionary.RemoveAll(kvp => kvp.Value.StartsWith("A")); + + // Assert - [ExpectedException] handles the assertion + } + + [TestMethod] + public void GetOrCreate_ShouldCreateAndReturnNewValue_WhenKeyDoesNotExist() + { + // Arrange + var dictionary = new Dictionary>(); + + // Act + var result = dictionary.GetOrCreate(1); + + // Assert + Assert.IsNotNull(result); + Assert.AreEqual(0, result.Count); + Assert.IsTrue(dictionary.ContainsKey(1)); + } + + [TestMethod] + public void AddOrUpdate_ShouldAddNewItem_WhenKeyDoesNotExist() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + dictionary.AddOrUpdate(1, "NewValue"); + + // Assert + Assert.AreEqual("NewValue", dictionary[1]); + } + + [TestMethod] + public void AddOrUpdate_ShouldUpdateExistingItem_WhenKeyExists() + { + // Arrange + var dictionary = new Dictionary + { + { 1, "OldValue" } + }; + + // Act + dictionary.AddOrUpdate(1, "UpdatedValue"); + + // Assert + Assert.AreEqual("UpdatedValue", dictionary[1]); + } + + [TestMethod] + public void RemoveIfExists_ShouldReturnTrueAndRemoveItem_WhenKeyExists() + { + // Arrange + var dictionary = new Dictionary + { + { 1, "Value" } + }; + + // Act + var result = dictionary.RemoveIfExists(1); + + // Assert + Assert.IsTrue(result); + Assert.IsFalse(dictionary.ContainsKey(1)); + } + + [TestMethod] + public void RemoveIfExists_ShouldReturnFalse_WhenKeyDoesNotExist() + { + // Arrange + var dictionary = new Dictionary(); + + // Act + var result = dictionary.RemoveIfExists(1); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + public void Merge_ShouldMergeDictionariesAndUpdateValues() + { + // Arrange + var dictionary = new Dictionary + { + { 1, "Value1" }, + { 2, "Value2" } + }; + + var otherDictionary = new Dictionary + { + { 2, "UpdatedValue2" }, + { 3, "Value3" } + }; + + // Act + dictionary.Merge(otherDictionary); + + // Assert + Assert.AreEqual(3, dictionary.Count); + Assert.AreEqual("UpdatedValue2", dictionary[2]); + Assert.AreEqual("Value3", dictionary[3]); + } + + [TestMethod] + public void ToReadableString_ShouldReturnFormattedStringRepresentationOfDictionary() + { + // Arrange + var dictionary = new Dictionary + { + { 1, "Value1" }, + { 2, "Value2" } + }; + + // Act + var result = dictionary.ToReadableString(); + + // Assert + Assert.AreEqual("{1: Value1, 2: Value2}", result); + } + } +} \ No newline at end of file diff --git a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj index 056e5df..666fc5a 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj +++ b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj @@ -7,7 +7,7 @@ - + diff --git a/SharpHelpers/SharpHelpers/DictionaryHelper.cs b/SharpHelpers/SharpHelpers/DictionaryHelper.cs index 2a61b50..53b0f1d 100644 --- a/SharpHelpers/SharpHelpers/DictionaryHelper.cs +++ b/SharpHelpers/SharpHelpers/DictionaryHelper.cs @@ -63,6 +63,63 @@ public static TValue GetOrCreate(this IDictionary di dictionary[key] = ret; } return ret; + } + + /// + /// Tries to add a key-value pair to the dictionary. If the key already exists, it updates the value. + /// + /// The dictionary to operate on. + /// The key to add or update. + /// The value to associate with the key. + public static void AddOrUpdate(this Dictionary dictionary, TKey key, TValue value) + { + if (dictionary.ContainsKey(key)) + { + dictionary[key] = value; + } + else + { + dictionary.Add(key, value); + } + } + + /// + /// Removes the entry with the specified key if it exists in the dictionary, and returns a boolean indicating success. + /// + /// The dictionary to operate on. + /// The key to remove. + /// True if the key was found and removed, otherwise false. + public static bool RemoveIfExists(this Dictionary dictionary, TKey key) + { + return dictionary.Remove(key); + } + + /// + /// Merges the entries from another dictionary into the current dictionary. If a key already exists, its value is updated. + /// + /// The dictionary to operate on. + /// The dictionary to merge from. + public static void Merge(this Dictionary dictionary, Dictionary otherDictionary) + { + foreach (var kvp in otherDictionary) + { + dictionary.AddOrUpdate(kvp.Key, kvp.Value); + } + } + + /// + /// Converts the dictionary into a readable string format, useful for debugging. + /// + /// The dictionary to convert to a string. + /// A string representation of the dictionary. + public static string ToReadableString(this Dictionary dictionary) + { + var entries = new List(); + foreach (var kvp in dictionary) + { + entries.Add($"{kvp.Key}: {kvp.Value}"); + } + return "{" + string.Join(", ", entries) + "}"; } } } From deff7754a86694123f976e5a6dd0176e8175ecf8 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Wed, 28 Aug 2024 11:50:17 +0200 Subject: [PATCH 15/27] Migrate from Newtonsoft.Json to System.Text.Json --- .../ObjectExtensions/ObjectSerializationhelper.cs | 6 +++--- SharpHelpers/SharpHelpers/SharpHelpers.csproj | 7 ++++--- SharpHelpers/SharpHelpers/StringHelper.cs | 11 ++++++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/SharpHelpers/SharpHelpers/ObjectExtensions/ObjectSerializationhelper.cs b/SharpHelpers/SharpHelpers/ObjectExtensions/ObjectSerializationhelper.cs index f7bcc05..f3a8c46 100755 --- a/SharpHelpers/SharpHelpers/ObjectExtensions/ObjectSerializationhelper.cs +++ b/SharpHelpers/SharpHelpers/ObjectExtensions/ObjectSerializationhelper.cs @@ -3,7 +3,7 @@ using System.IO; using System.Xml; using System.Xml.Serialization; -using Newtonsoft.Json; +using System.Text.Json; namespace SharpCoding.SharpHelpers.ObjectExtensions { @@ -16,7 +16,7 @@ public static class ObjectSerializationHelper /// public static string SerializeToJson(this object istance) { - return istance == null ? string.Empty : JsonConvert.SerializeObject(istance); + return istance == null ? string.Empty : JsonSerializer.Serialize(istance); } /// @@ -27,7 +27,7 @@ public static string SerializeToJson(this object istance) /// public static T DeserializeFromJson(this string istance) where T : class { - return string.IsNullOrEmpty(istance) ? default : JsonConvert.DeserializeObject(istance); + return string.IsNullOrEmpty(istance) ? default : JsonSerializer.Deserialize(istance); } /// diff --git a/SharpHelpers/SharpHelpers/SharpHelpers.csproj b/SharpHelpers/SharpHelpers/SharpHelpers.csproj index 2f0da91..473433f 100644 --- a/SharpHelpers/SharpHelpers/SharpHelpers.csproj +++ b/SharpHelpers/SharpHelpers/SharpHelpers.csproj @@ -20,9 +20,6 @@ ico.png - - - @@ -38,4 +35,8 @@ + + + + diff --git a/SharpHelpers/SharpHelpers/StringHelper.cs b/SharpHelpers/SharpHelpers/StringHelper.cs index 86adc4f..fb8fdf9 100644 --- a/SharpHelpers/SharpHelpers/StringHelper.cs +++ b/SharpHelpers/SharpHelpers/StringHelper.cs @@ -5,7 +5,7 @@ using System.Globalization; using System.Linq; using System.Text; -using Newtonsoft.Json; +using System.Text.Json; using System.Text.RegularExpressions; using SharpCoding.SharpHelpers.DomainModel; using SharpCoding.SharpHelpers.PrivateMethods; @@ -223,8 +223,13 @@ public static string Right(this string str, int length) /// public static T JsonToObject(this string strJson) { - return JsonConvert.DeserializeObject(strJson, - new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); + var options = new JsonSerializerOptions + { + ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles, + PropertyNameCaseInsensitive = true + }; + + return JsonSerializer.Deserialize(strJson, options); } /// From ff6fbf4387617ed86daf6576b128339f59e52cf4 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 6 Oct 2024 15:44:31 +0200 Subject: [PATCH 16/27] Update packages --- .../SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj index 666fc5a..316d4a9 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj +++ b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj @@ -7,9 +7,9 @@ - - - + + + From bf29712ee4c535ff20e83c0ee74ebe3760b6c926 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 6 Oct 2024 15:48:19 +0200 Subject: [PATCH 17/27] Fix FromFile --- SharpHelpers/SharpHelpers/StreamHelper.cs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/SharpHelpers/SharpHelpers/StreamHelper.cs b/SharpHelpers/SharpHelpers/StreamHelper.cs index 33e7158..177d934 100644 --- a/SharpHelpers/SharpHelpers/StreamHelper.cs +++ b/SharpHelpers/SharpHelpers/StreamHelper.cs @@ -173,22 +173,17 @@ public static bool ToFile(this Stream stream, string fileName, bool overrideExis { return false; } - } - + } + /// /// Loads a stream from a file /// /// /// - public static Stream FromFile(string fileName) - { - var fileStream = File.OpenRead(fileName); - var fileLength = (int)fileStream.Length; - var fileBytes = new byte[fileLength]; - fileStream.Read(fileBytes, 0, fileLength); - fileStream.Close(); - fileStream.Dispose(); - return FromArray(fileBytes); + public static Stream FromFile(this string fileName) + { + byte[] fileBytes = File.ReadAllBytes(fileName); + return FromArray(fileBytes); } } } From cf9197a44329a9bf19a8219fc39b8b0e91611781 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 6 Oct 2024 16:17:00 +0200 Subject: [PATCH 18/27] Refactor ToFile --- SharpHelpers/SharpHelpers/StreamHelper.cs | 72 +++++++++-------------- 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/SharpHelpers/SharpHelpers/StreamHelper.cs b/SharpHelpers/SharpHelpers/StreamHelper.cs index 177d934..1aafbbe 100644 --- a/SharpHelpers/SharpHelpers/StreamHelper.cs +++ b/SharpHelpers/SharpHelpers/StreamHelper.cs @@ -121,9 +121,9 @@ public static Stream FromArray(this byte[] arrayToConvert) /// public static bool ToFile(this Stream stream, string fileName) { - return ToFile(stream, fileName, true, Encoding.Default); - } - + return ToFile(stream, fileName, true); + } + /// /// Writes a stream to file /// @@ -131,48 +131,30 @@ public static bool ToFile(this Stream stream, string fileName) /// Name of the file. /// If set to true override existing file. /// True if successful - public static bool ToFile(this Stream stream, string fileName, bool overrideExisting) - { - return ToFile(stream, fileName, overrideExisting, Encoding.Default); - } - - /// - /// Writes a stream to file - /// - /// - /// - /// - /// - /// - public static bool ToFile(this Stream stream, string fileName, bool overrideExisting, Encoding encoding) - { - //Check if the sepcified file exists - if (File.Exists(fileName)) - if (overrideExisting) - File.Delete(fileName); - else - throw new AccessViolationException("File already exists"); - - try - { - //Create the file if it does not exist and open it - stream.Position = 0; - using (var fileStream = new FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite)) - { - var reader = new BinaryReader(stream); - var writer = new BinaryWriter(fileStream, encoding); - writer.Write(reader.ReadBytes((int)stream.Length)); - writer.Flush(); - writer.Close(); - reader.Close(); - fileStream.Close(); - return true; - } - } - catch - { - return false; - } + public static bool ToFile(this Stream stream, string fileName, bool overrideExisting) + { + try + { + if (File.Exists(fileName)) + { + if (overrideExisting) + File.Delete(fileName); + else + throw new IOException("File already exists"); + } + + stream.Position = 0; + using (var fileStream = new FileStream(fileName, FileMode.CreateNew, FileAccess.Write)) + { + stream.CopyTo(fileStream); + } + + return true; + } + catch (IOException) + { + return false; + } } /// From 20408bf31f0734c62edfab681ea4b23fc3b98113 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 6 Oct 2024 16:17:57 +0200 Subject: [PATCH 19/27] Refactor FromArray --- SharpHelpers/SharpHelpers/StreamHelper.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/SharpHelpers/SharpHelpers/StreamHelper.cs b/SharpHelpers/SharpHelpers/StreamHelper.cs index 1aafbbe..41b8eb9 100644 --- a/SharpHelpers/SharpHelpers/StreamHelper.cs +++ b/SharpHelpers/SharpHelpers/StreamHelper.cs @@ -106,11 +106,8 @@ public static Stream FromString(this string stringToConvert) /// /// public static Stream FromArray(this byte[] arrayToConvert) - { - var stream = new MemoryStream(arrayToConvert.Length); - stream.Write(arrayToConvert, 0, arrayToConvert.Length); - stream.Position = 0; - return stream; + { + return new MemoryStream(arrayToConvert, writable: false); } /// From a65bd5a6b419064f03ebf1130c8e49b789084c63 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 6 Oct 2024 16:23:14 +0200 Subject: [PATCH 20/27] Add some numeric methods --- SharpHelpers/SharpHelpers/NumericHelper.cs | 102 +++++++++++++++++++-- 1 file changed, 93 insertions(+), 9 deletions(-) diff --git a/SharpHelpers/SharpHelpers/NumericHelper.cs b/SharpHelpers/SharpHelpers/NumericHelper.cs index 3c07d98..76ca8f4 100644 --- a/SharpHelpers/SharpHelpers/NumericHelper.cs +++ b/SharpHelpers/SharpHelpers/NumericHelper.cs @@ -7,10 +7,10 @@ namespace SharpCoding.SharpHelpers public static class NumericHelper { /// - /// check if the int number that invokes the method is odd + /// Checks if the integer number is odd. /// - /// - /// + /// The integer number to check. + /// True if the number is odd; otherwise, false. public static bool IsOdd(this int numberToCheck) { var restOfDivision = (numberToCheck % 2); @@ -18,20 +18,20 @@ public static bool IsOdd(this int numberToCheck) } /// - /// check if the int number that invokes the method is even + /// Checks if the integer number is even. /// - /// - /// + /// The integer number to check. + /// True if the number is even; otherwise, false. public static bool IsEven(this int numberToCheck) { return !numberToCheck.IsOdd(); } /// - /// check if the int number that invokes the method is prime + /// Checks if the integer number is prime. /// - /// - /// + /// The integer number to check. + /// True if the number is prime; otherwise, false. public static bool IsPrime(this int numberToCheck) { var limit = Math.Ceiling(Math.Sqrt(numberToCheck)); @@ -44,5 +44,89 @@ public static bool IsPrime(this int numberToCheck) } return true; } + + /// + /// Calculates the factorial of the integer number. + /// + /// The integer number to calculate the factorial for. + /// The factorial of the number. + /// Thrown when the number is negative. + public static long Factorial(this int number) + { + if (number < 0) throw new ArgumentException("Number must be non-negative."); + return number <= 1 ? 1 : number * Factorial(number - 1); + } + + /// + /// Computes the Greatest Common Divisor (GCD) of two integers. + /// + /// The first integer. + /// The second integer. + /// The greatest common divisor of the two numbers. + public static int GCD(this int a, int b) + { + while (b != 0) + { + int temp = b; + b = a % b; + a = temp; + } + return a; + } + + /// + /// Computes the Least Common Multiple (LCM) of two integers. + /// + /// The first integer. + /// The second integer. + /// The least common multiple of the two numbers. + public static int LCM(this int a, int b) + { + return Math.Abs(a * b) / a.GCD(b); + } + + /// + /// Checks if the integer number is negative. + /// + /// The integer number to check. + /// True if the number is negative; otherwise, false. + public static bool IsNegative(this int number) + { + return number < 0; + } + + /// + /// Checks if the integer number is positive. + /// + /// The integer number to check. + /// True if the number is positive; otherwise, false. + public static bool IsPositive(this int number) + { + return number > 0; + } + + /// + /// Clamps the integer number within the specified range. + /// + /// The integer number to clamp. + /// The minimum value. + /// The maximum value. + /// The clamped value within the specified range. + public static int Clamp(this int value, int min, int max) + { + if (value < min) return min; + if (value > max) return max; + return value; + } + + /// + /// Returns the absolute value of the integer number. + /// + /// The integer number to get the absolute value for. + /// The absolute value of the number. + public static int Abs(this int number) + { + return Math.Abs(number); + } } } From 679ce24fe2aa9aa5a8b35fb928c258067838b84e Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Sun, 6 Oct 2024 16:28:41 +0200 Subject: [PATCH 21/27] Refactor IsSystemType --- SharpHelpers/SharpHelpers/ObjectHelper.cs | 46 +++++++++++++---------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/SharpHelpers/SharpHelpers/ObjectHelper.cs b/SharpHelpers/SharpHelpers/ObjectHelper.cs index 35e8582..b1c4e16 100644 --- a/SharpHelpers/SharpHelpers/ObjectHelper.cs +++ b/SharpHelpers/SharpHelpers/ObjectHelper.cs @@ -7,29 +7,37 @@ namespace SharpCoding.SharpHelpers { public static class ObjectHelper { + private static readonly Type[] SystemTypes = + { + typeof(Enum), + typeof(string), + typeof(decimal), + typeof(DateTime), + typeof(DateTimeOffset), + typeof(TimeSpan), + typeof(Guid) + }; + /// - /// This method verifies that the passed type is a .Net Type + /// Checks if the given type is a system type (e.g., primitive, value types, common system types). /// - /// - /// + /// The type to check. + /// True if the type is a system type; otherwise, false. public static bool IsSystemType(this Type type) { - return - type.IsValueType || - type.IsPrimitive || - new[] - { - typeof(Enum), - typeof(string), - typeof(decimal), - typeof(DateTime), - typeof(DateTimeOffset), - typeof(TimeSpan), - typeof(Guid) - }.Contains(type) || - Convert.GetTypeCode(type) != TypeCode.Object || - (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && - IsSystemType(type.GetGenericArguments()[0])); + if (type.IsValueType || type.IsPrimitive) + return true; + + // Handle Nullable types + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) + return IsSystemType(type.GetGenericArguments()[0]); + + // Check against predefined system types + if (SystemTypes.Contains(type)) + return true; + + // Check the type code to cover other system types + return Convert.GetTypeCode(type) != TypeCode.Object; } } } From d8d70e6cecba2d3fa6d0789429f6cc9205523716 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Tue, 8 Oct 2024 00:39:03 +0200 Subject: [PATCH 22/27] Add boolean methods --- SharpHelpers/SharpHelpers/BooleanHelper.cs | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/SharpHelpers/SharpHelpers/BooleanHelper.cs b/SharpHelpers/SharpHelpers/BooleanHelper.cs index c2049c4..7e74f19 100755 --- a/SharpHelpers/SharpHelpers/BooleanHelper.cs +++ b/SharpHelpers/SharpHelpers/BooleanHelper.cs @@ -1,5 +1,7 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) +using System; + namespace SharpCoding.SharpHelpers { public static class BooleanHelper @@ -49,5 +51,51 @@ public static string ToStringValues(this bool? value, string trueValue, string f { return value.HasValue ? (value.Value ? trueValue : falseValue) : falseValue; } + + /// + /// Negates the instance boolean value. + /// + /// The boolean value to negate. + /// Returns the negated boolean value. + public static bool Not(this bool instance) + { + return !instance; + } + + /// + /// Determines if the instance is true and executes the specified action if true. + /// + /// The boolean value to evaluate. + /// The action to execute if the boolean value is true. + public static void IfTrue(this bool instance, Action action) + { + if (instance) + { + action?.Invoke(); + } + } + + /// + /// Determines if the instance is false and executes the specified action if false. + /// + /// The boolean value to evaluate. + /// The action to execute if the boolean value is false. + public static void IfFalse(this bool instance, Action action) + { + if (!instance) + { + action?.Invoke(); + } + } + + /// + /// Returns the boolean value as an integer (1 for true, 0 for false). + /// + /// The boolean value to convert. + /// 1 if true, 0 if false. + public static int ToInt(this bool instance) + { + return instance ? 1 : 0; + } } } From 8ec241cd707af4cf9805967fc6121375915d096e Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Tue, 8 Oct 2024 01:16:42 +0200 Subject: [PATCH 23/27] Add XmlDocument methods --- .../SharpHelpers/XmlDocumentHelper.cs | 91 +++++++++++++++++-- 1 file changed, 82 insertions(+), 9 deletions(-) diff --git a/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs b/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs index 561b41d..04c138d 100755 --- a/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs +++ b/SharpHelpers/SharpHelpers/XmlDocumentHelper.cs @@ -1,6 +1,7 @@ // (c) 2019 SharpCoding // This code is licensed under MIT license (see LICENSE.txt for details) using System; +using System.IO; using System.Text; using System.Xml; using System.Xml.Linq; @@ -10,10 +11,11 @@ namespace SharpCoding.SharpHelpers public static class XmlDocumentHelper { /// - /// Return a XmlDocument from a XDocument + /// Converts an XDocument to an XmlDocument. /// - /// - /// + /// The XDocument to convert. + /// The converted XmlDocument. + /// Thrown when xDocument is null. public static XmlDocument ToXmlDocument(this XDocument xDocument) { if (xDocument == null) throw new ArgumentNullException(nameof(xDocument)); @@ -27,10 +29,11 @@ public static XmlDocument ToXmlDocument(this XDocument xDocument) } /// - /// Return a XDocument from a XmlDocument + /// Converts an XmlDocument to an XDocument. /// - /// - /// + /// The XmlDocument to convert. + /// The converted XDocument. + /// Thrown when xmlDocument is null. public static XDocument ToXDocument(this XmlDocument xmlDocument) { if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); @@ -43,10 +46,11 @@ public static XDocument ToXDocument(this XmlDocument xmlDocument) } /// - /// Return a XmlDocument from a XElement + /// Converts an XElement to an XmlDocument. /// - /// - /// + /// The XElement to convert. + /// The converted XmlDocument. + /// Thrown when xElement is null. public static XmlDocument ToXmlDocument(this XElement xElement) { if (xElement == null) throw new ArgumentNullException(nameof(xElement)); @@ -61,5 +65,74 @@ public static XmlDocument ToXmlDocument(this XElement xElement) doc.LoadXml(sb.ToString()); return doc; } + + /// + /// Finds the first child node with the specified name. + /// + /// The XmlDocument to search. + /// The name of the node to find. + /// The first matching XmlNode, or null if not found. + public static XmlNode FindFirstChild(this XmlDocument xmlDocument, string nodeName) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + if (string.IsNullOrWhiteSpace(nodeName)) throw new ArgumentException("Node name cannot be null or empty.", nameof(nodeName)); + + return xmlDocument.SelectSingleNode($"//{nodeName}"); + } + + /// + /// Adds a new child element with the specified name and value to the given parent node. + /// + /// The XmlDocument to modify. + /// The parent node to which the new element will be added. + /// The name of the new element. + /// The value of the new element. + /// The newly created XmlElement. + public static XmlElement AddChildElement(this XmlDocument xmlDocument, XmlNode parentNode, string elementName, string elementValue) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + if (parentNode == null) throw new ArgumentNullException(nameof(parentNode)); + if (string.IsNullOrWhiteSpace(elementName)) throw new ArgumentException("Element name cannot be null or empty.", nameof(elementName)); + + var newElement = xmlDocument.CreateElement(elementName); + newElement.InnerText = elementValue; + parentNode.AppendChild(newElement); + return newElement; + } + + /// + /// Converts the XmlDocument to a formatted string. + /// + /// The XmlDocument to convert. + /// A string representation of the XmlDocument. + public static string ToFormattedString(this XmlDocument xmlDocument) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + + using (var stringWriter = new StringWriter()) + { + using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true })) + { + xmlDocument.Save(xmlWriter); + } + return stringWriter.ToString(); + } + } + + /// + /// Removes the specified node from the XmlDocument. + /// + /// The XmlDocument to modify. + /// The node to remove. + public static void RemoveNode(this XmlDocument xmlDocument, XmlNode node) + { + if (xmlDocument == null) throw new ArgumentNullException(nameof(xmlDocument)); + if (node == null) throw new ArgumentNullException(nameof(node)); + + if (node.ParentNode != null) + { + node.ParentNode.RemoveChild(node); + } + } } } From 1143262c046964e9778a20e82406e07573ab692e Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Wed, 9 Oct 2024 10:30:16 +0200 Subject: [PATCH 24/27] Update packages --- SharpHelpers/SharpHelpers/SharpHelpers.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharpHelpers/SharpHelpers/SharpHelpers.csproj b/SharpHelpers/SharpHelpers/SharpHelpers.csproj index 473433f..6417469 100644 --- a/SharpHelpers/SharpHelpers/SharpHelpers.csproj +++ b/SharpHelpers/SharpHelpers/SharpHelpers.csproj @@ -37,6 +37,6 @@ - + From 598aa021a99b5a26292d8dc74ee7cdf2be248898 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Fri, 8 Nov 2024 10:15:11 +0100 Subject: [PATCH 25/27] Add DataTable and Enumerable helpers --- .../SharpHelpers.UnitTest.csproj | 4 +- SharpHelpers/SharpHelpers/DataTableHelper.cs | 134 ++++++++++++++++++ SharpHelpers/SharpHelpers/EnumerableHelper.cs | 117 +++++++++++++++ 3 files changed, 253 insertions(+), 2 deletions(-) diff --git a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj index 316d4a9..296e2f4 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj +++ b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/SharpHelpers/SharpHelpers/DataTableHelper.cs b/SharpHelpers/SharpHelpers/DataTableHelper.cs index f1fe5e2..6df897a 100644 --- a/SharpHelpers/SharpHelpers/DataTableHelper.cs +++ b/SharpHelpers/SharpHelpers/DataTableHelper.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Linq; using System.Reflection; namespace SharpCoding.SharpHelpers @@ -51,6 +52,139 @@ public static DataTable SetColumnsOrder(this DataTable table, string[] columnNam list.Add(objClass); } return list; + } + + /// + /// Converts the DataTable to a CSV format string. + /// + /// + /// + /// + public static string ToCsv(this DataTable table, string delimiter = ",") + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + var csv = new List(); + var headers = string.Join(delimiter, table.Columns.Cast().Select(c => c.ColumnName)); + csv.Add(headers); + + foreach (DataRow row in table.Rows) + { + var line = string.Join(delimiter, row.ItemArray.Select(field => field?.ToString())); + csv.Add(line); + } + return string.Join(Environment.NewLine, csv); + } + + /// + /// Adds a new column to the DataTable with the specified default value. + /// + /// + /// + /// + /// + public static void AddColumn(this DataTable table, string columnName, T defaultValue = default) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + var column = new DataColumn(columnName, typeof(T)) { DefaultValue = defaultValue }; + table.Columns.Add(column); + foreach (DataRow row in table.Rows) + { + row[columnName] = defaultValue; + } + } + + /// + /// Merges multiple DataTables with the same schema into one. + /// + /// + /// + /// + public static DataTable MergeTables(IEnumerable tables) + { + if (tables == null) throw new ArgumentNullException(nameof(tables)); + + var resultTable = tables.First().Clone(); + foreach (var table in tables) + { + if (!AreSchemasCompatible(resultTable, table)) + throw new ArgumentException("Tables have incompatible schemas."); + + foreach (DataRow row in table.Rows) + { + resultTable.ImportRow(row); + } + } + return resultTable; + } + + private static bool AreSchemasCompatible(DataTable table1, DataTable table2) + { + if (table1.Columns.Count != table2.Columns.Count) return false; + + for (int i = 0; i < table1.Columns.Count; i++) + { + if (table1.Columns[i].ColumnName != table2.Columns[i].ColumnName || + table1.Columns[i].DataType != table2.Columns[i].DataType) + return false; + } + return true; + } + + /// + /// Filters the rows in the DataTable based on a predicate. + /// + /// + /// + /// + public static DataTable Filter(this DataTable table, Func predicate) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + + var filteredTable = table.Clone(); + foreach (DataRow row in table.AsEnumerable().Where(predicate)) + { + filteredTable.ImportRow(row); + } + return filteredTable; + } + + /// + /// Checks if the DataTable is empty (contains no rows). + /// + /// + /// + public static bool IsEmpty(this DataTable table) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + return table.Rows.Count == 0; + } + + /// + /// Removes duplicate rows based on specified columns. + /// + /// + /// + /// + public static DataTable RemoveDuplicates(this DataTable table, params string[] columnNames) + { + if (table == null) throw new ArgumentNullException(nameof(table)); + + var distinctTable = table.Clone(); + var uniqueRows = new HashSet(); + + foreach (DataRow row in table.Rows) + { + var key = string.Join("|", columnNames.Select(c => row[c]?.ToString() ?? "")); + if (uniqueRows.Add(key)) + { + distinctTable.ImportRow(row); + } + } + return distinctTable; } } } diff --git a/SharpHelpers/SharpHelpers/EnumerableHelper.cs b/SharpHelpers/SharpHelpers/EnumerableHelper.cs index 8ffd44f..eee914d 100644 --- a/SharpHelpers/SharpHelpers/EnumerableHelper.cs +++ b/SharpHelpers/SharpHelpers/EnumerableHelper.cs @@ -268,5 +268,122 @@ public static int Sum(this IEnumerable source, Func selector) return source.Select(selector).Sum(); } + + /// + /// Returns the maximum element based on a given selector function. + /// + /// + /// + /// + /// + /// + /// + public static TSource MaxBy(this IEnumerable source, Func selector) + where TKey : IComparable + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + return source.Aggregate((maxItem, nextItem) => selector(nextItem).CompareTo(selector(maxItem)) > 0 ? nextItem : maxItem); + } + + /// + /// Returns the minimum element based on a given selector function. + /// + /// + /// + /// + /// + /// + /// + public static TSource MinBy(this IEnumerable source, Func selector) + where TKey : IComparable + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (selector == null) throw new ArgumentNullException(nameof(selector)); + + return source.Aggregate((minItem, nextItem) => selector(nextItem).CompareTo(selector(minItem)) < 0 ? nextItem : minItem); + } + + /// + /// Finds the index of the first element that satisfies a given predicate. + /// + /// + /// + /// + /// + /// + public static int FindIndex(this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + + int index = 0; + foreach (var item in source) + { + if (predicate(item)) return index; + index++; + } + return -1; + } + + /// + /// Checks if the source contains any of the specified items. + /// + /// + /// + /// + /// + /// + public static bool ContainsAny(this IEnumerable source, params T[] items) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (items == null) throw new ArgumentNullException(nameof(items)); + + var set = new HashSet(items); + return source.Any(set.Contains); + } + + /// + /// Checks if the source contains all of the specified items. + /// + /// + /// + /// + /// + /// + public static bool ContainsAll(this IEnumerable source, params T[] items) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (items == null) throw new ArgumentNullException(nameof(items)); + + var set = new HashSet(source); + return items.All(set.Contains); + } + + /// + /// Returns the median of a sequence of numbers. + /// + /// + /// + /// + public static double Median(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + var sortedList = source.OrderBy(n => n).ToList(); + int count = sortedList.Count; + if (count == 0) + throw new InvalidOperationException("The source sequence is empty."); + + if (count % 2 == 0) + { + return (sortedList[count / 2 - 1] + sortedList[count / 2]) / 2.0; + } + else + { + return sortedList[count / 2]; + } + } } } From 1463096ed5036e002c06927c16515f9b1711551e Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Tue, 19 Nov 2024 09:45:39 +0100 Subject: [PATCH 26/27] Update packages --- .../SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj | 4 ++-- SharpHelpers/SharpHelpers/SharpHelpers.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj index 296e2f4..587d005 100644 --- a/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj +++ b/SharpHelpers/SharpHelpers.UnitTest/SharpHelpers.UnitTest.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/SharpHelpers/SharpHelpers/SharpHelpers.csproj b/SharpHelpers/SharpHelpers/SharpHelpers.csproj index 6417469..c3c5222 100644 --- a/SharpHelpers/SharpHelpers/SharpHelpers.csproj +++ b/SharpHelpers/SharpHelpers/SharpHelpers.csproj @@ -37,6 +37,6 @@ - + From dcd727d66dce59d51f887aab104d98c4ef6fcf13 Mon Sep 17 00:00:00 2001 From: Francesco Del Re Date: Mon, 25 Nov 2024 18:47:12 +0100 Subject: [PATCH 27/27] Update assembly and package --- SharpHelpers/SharpHelpers/SharpHelpers.csproj | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/SharpHelpers/SharpHelpers/SharpHelpers.csproj b/SharpHelpers/SharpHelpers/SharpHelpers.csproj index c3c5222..ce0c743 100644 --- a/SharpHelpers/SharpHelpers/SharpHelpers.csproj +++ b/SharpHelpers/SharpHelpers/SharpHelpers.csproj @@ -13,12 +13,16 @@ SharpCoding, Helpers, C# SharpHelpers is a collections of some handy code packages and tutorials to make developer's life easier. - 1.0.1 + 2.2.0 8 ico.png + https://github.com/sharpcode-it/SharpHelpers + README.md + 2.2.0 + 2.2.0 @@ -26,6 +30,10 @@ True + + True + \ + True