diff --git a/CHANGELOG.md b/CHANGELOG.md index b6cc96d..29fd490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.2.2] - 2024-02-01 + +- Bugfix in culture handling in DateTimeTypeDecider +- Add nullability annotations + ## [1.2.1] - 2024-01-29 - Version bump to resolve symbol package issue on Nuget.org diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..efd72d5 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,5 @@ + + + enable + + \ No newline at end of file diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs index d6b83d2..1a735e9 100644 --- a/SharedAssemblyInfo.cs +++ b/SharedAssemblyInfo.cs @@ -9,6 +9,6 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("1.2.1")] -[assembly: AssemblyFileVersion("1.2.1")] -[assembly: AssemblyInformationalVersion("1.2.1")] +[assembly: AssemblyVersion("1.2.2")] +[assembly: AssemblyFileVersion("1.2.2")] +[assembly: AssemblyInformationalVersion("1.2.2")] diff --git a/Tests/GuesserTests.cs b/Tests/GuesserTests.cs index 358222f..a6425bd 100644 --- a/Tests/GuesserTests.cs +++ b/Tests/GuesserTests.cs @@ -76,12 +76,14 @@ public void Test_OneString_IsType(string guessFor, Type expectedGuess, string cu : guessFor, Is.EqualTo(expectedParseValue)); } +#pragma warning disable CA1861 [TestCase("en-us",new []{"5","10"}, typeof(int),2,2,0)] [TestCase("en-us", new []{"5","10.1"}, typeof(decimal),4,2,1)] [TestCase("en-us", new []{"5.1","5.000000000"}, typeof(decimal),11,1,1)] +#pragma warning restore CA1861 public void Test_ManyString_IsType(string culture, string[] guessFor, Type expectedGuess,int expectedStringLength, int expectedBefore,int expectedAfter) { var cultureInfo = new CultureInfo(culture); diff --git a/Tests/PackageListIsCorrectTests.cs b/Tests/PackageListIsCorrectTests.cs index 53cc67a..16f6e94 100644 --- a/Tests/PackageListIsCorrectTests.cs +++ b/Tests/PackageListIsCorrectTests.cs @@ -29,7 +29,7 @@ public sealed partial class PackageListIsCorrectTests /// /// [TestCase] - public void TestPackagesDocumentCorrect(string rootPath=null) + public void TestPackagesDocumentCorrect(string? rootPath=null) { var root= FindRoot(rootPath); var undocumented = new StringBuilder(); @@ -72,7 +72,7 @@ public void TestPackagesDocumentCorrect(string rootPath=null) /// /// /// - private static DirectoryInfo FindRoot(string path = null) + private static DirectoryInfo FindRoot(string? path = null) { if (path != null) { diff --git a/Tests/PerformanceTests.cs b/Tests/PerformanceTests.cs index d9f7b11..6ecff3b 100644 --- a/Tests/PerformanceTests.cs +++ b/Tests/PerformanceTests.cs @@ -26,7 +26,8 @@ public void Performance_Decimals() var decider = new DecimalTypeDecider(new CultureInfo("en-GB")); - var req = new DatabaseTypeRequest(null); + // ReSharper disable once NullableWarningSuppressionIsUsed - this is just for benchmarking + var req = new DatabaseTypeRequest(null!); var sw = new Stopwatch(); diff --git a/TypeGuesser.sln b/TypeGuesser.sln index 42f7a1d..d22638f 100644 --- a/TypeGuesser.sln +++ b/TypeGuesser.sln @@ -10,6 +10,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{EAE289E1-E8AF-4627-91A6-12E6D5F9F027}" ProjectSection(SolutionItems) = preProject CHANGELOG.md = CHANGELOG.md + Directory.Build.props = Directory.Build.props Packages.md = Packages.md README.md = README.md SharedAssemblyInfo.cs = SharedAssemblyInfo.cs diff --git a/TypeGuesser/DatabaseTypeRequest.cs b/TypeGuesser/DatabaseTypeRequest.cs index 4d4138f..7b5a7c3 100644 --- a/TypeGuesser/DatabaseTypeRequest.cs +++ b/TypeGuesser/DatabaseTypeRequest.cs @@ -61,7 +61,7 @@ public int? Width /// /// public DatabaseTypeRequest(Type cSharpType, int? maxWidthForStrings = null, - DecimalSize decimalPlacesBeforeAndAfter = null) + DecimalSize? decimalPlacesBeforeAndAfter = null) { CSharpType = cSharpType; Width = maxWidthForStrings; @@ -80,7 +80,7 @@ private bool Equals(DatabaseTypeRequest other) } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; diff --git a/TypeGuesser/Deciders/BoolTypeDecider.cs b/TypeGuesser/Deciders/BoolTypeDecider.cs index 885834b..6ea10c5 100644 --- a/TypeGuesser/Deciders/BoolTypeDecider.cs +++ b/TypeGuesser/Deciders/BoolTypeDecider.cs @@ -21,7 +21,7 @@ protected override IDecideTypesForStrings CloneImpl(CultureInfo newCulture) } /// - protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize size) + protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? size) { // "Y" / "N" is boolean unless the settings say it can't if (!Settings.CharCanBeBoolean && SingleCharacter.IsMatch(candidateString)) diff --git a/TypeGuesser/Deciders/DateTimeTypeDecider.cs b/TypeGuesser/Deciders/DateTimeTypeDecider.cs index c8d93de..479b85c 100644 --- a/TypeGuesser/Deciders/DateTimeTypeDecider.cs +++ b/TypeGuesser/Deciders/DateTimeTypeDecider.cs @@ -32,8 +32,11 @@ public class DateTimeTypeDecider(CultureInfo cultureInfo) : DecideTypesForString /// public static readonly string[] TimeFormats; - private string[] _dateFormatToUse; - private CultureInfo _culture; + private string[] _dateFormatToUse = + cultureInfo.DateTimeFormat.ShortDatePattern.IndexOf('M') > cultureInfo.DateTimeFormat.ShortDatePattern.IndexOf('d') + ? DateFormatsDM + : DateFormatsMD; + private CultureInfo _culture=cultureInfo; /// /// Setting this to false will prevent changing the e.g. when @@ -49,10 +52,7 @@ public class DateTimeTypeDecider(CultureInfo cultureInfo) : DecideTypesForString public override CultureInfo Culture { get => _culture; set { - value ??= CultureInfo.CurrentCulture; - _dateFormatToUse = value.DateTimeFormat.ShortDatePattern.IndexOf('M') > value.DateTimeFormat.ShortDatePattern.IndexOf('d') ? DateFormatsDM : DateFormatsMD; - _culture = value; } } @@ -196,13 +196,13 @@ public void GuessDateFormat(IEnumerable samples) } /// - public override bool IsAcceptableAsType(string candidateString, IDataTypeSize size) + public override bool IsAcceptableAsType(string candidateString, IDataTypeSize? size) { return IsExplicitDate(candidateString) || base.IsAcceptableAsType(candidateString, size); } /// - protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize sizeRecord) + protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord) { //if it's a float then it isn't a date is it! thanks C# for thinking 1.1 is the first of January if (_decimalChecker.IsAcceptableAsType(candidateString, sizeRecord)) diff --git a/TypeGuesser/Deciders/DecideTypesForStrings.cs b/TypeGuesser/Deciders/DecideTypesForStrings.cs index 664b841..6a55fa5 100644 --- a/TypeGuesser/Deciders/DecideTypesForStrings.cs +++ b/TypeGuesser/Deciders/DecideTypesForStrings.cs @@ -50,7 +50,7 @@ protected DecideTypesForStrings(CultureInfo culture, TypeCompatibilityGroup comp } /// - public virtual bool IsAcceptableAsType(string candidateString,IDataTypeSize size) + public virtual bool IsAcceptableAsType(string candidateString,IDataTypeSize? size) { //we must preserve leading zeroes if it's not actually 0 -- if they have 010101 then we have to use string but if they have just 0 we can use decimal return !IDecideTypesForStrings.ZeroPrefixedNumber.IsMatch(candidateString) && IsAcceptableAsTypeImpl(candidateString, size); @@ -71,7 +71,7 @@ protected bool IsExplicitDate(string candidateString) } /// - public object Parse(string value) + public object? Parse(string value) { if (string.IsNullOrWhiteSpace(value)) return null; @@ -105,7 +105,7 @@ public IDecideTypesForStrings Clone() /// /// /// - protected virtual object ParseImpl(string value) + protected virtual object? ParseImpl(string value) { return value.To(Culture); } @@ -116,7 +116,7 @@ protected virtual object ParseImpl(string value) /// /// /// - protected virtual bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize size) + protected virtual bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize? size) { return candidateString.IsConvertibleTo(Culture); } diff --git a/TypeGuesser/Deciders/DecimalTypeDecider.cs b/TypeGuesser/Deciders/DecimalTypeDecider.cs index f30b428..59649f7 100644 --- a/TypeGuesser/Deciders/DecimalTypeDecider.cs +++ b/TypeGuesser/Deciders/DecimalTypeDecider.cs @@ -30,7 +30,7 @@ protected override IDecideTypesForStrings CloneImpl(CultureInfo culture) } /// - protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize sizeRecord) + protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize? sizeRecord) { candidateString = TrimTrailingZeros(candidateString); @@ -41,7 +41,7 @@ protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeS return false; var dec = (SqlDecimal) t; - sizeRecord.Size.IncreaseTo(dec.Precision - dec.Scale,dec.Scale); + sizeRecord?.Size.IncreaseTo(dec.Precision - dec.Scale,dec.Scale); return true; } diff --git a/TypeGuesser/Deciders/IDecideTypesForStrings.cs b/TypeGuesser/Deciders/IDecideTypesForStrings.cs index 3219bad..8ca1aa1 100644 --- a/TypeGuesser/Deciders/IDecideTypesForStrings.cs +++ b/TypeGuesser/Deciders/IDecideTypesForStrings.cs @@ -56,14 +56,14 @@ public partial interface IDecideTypesForStrings /// The current size estimate of floating point numbers (or null if not appropriate). This will be modified by the method /// if appropriate to the data passed /// True if the is a valid value for the by the decider - bool IsAcceptableAsType(string candidateString,IDataTypeSize size); + bool IsAcceptableAsType(string candidateString,IDataTypeSize? size); /// /// Converts the provided to an object of the Type modelled by this . /// /// /// - object Parse(string value); + object? Parse(string value); /// /// Returns a new instance of this class with the same and etc diff --git a/TypeGuesser/Deciders/IntTypeDecider.cs b/TypeGuesser/Deciders/IntTypeDecider.cs index bc7d1ab..795f42c 100644 --- a/TypeGuesser/Deciders/IntTypeDecider.cs +++ b/TypeGuesser/Deciders/IntTypeDecider.cs @@ -20,7 +20,7 @@ protected override IDecideTypesForStrings CloneImpl(CultureInfo newCulture) } /// - protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize sizeRecord) + protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord) { if(IsExplicitDate(candidateString)) return false; @@ -28,7 +28,7 @@ protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataType if (!candidateString.IsConvertibleTo(out int i, Culture)) return false; - sizeRecord.Size.IncreaseTo(i.ToString().Trim('-').Length,0); + sizeRecord?.Size.IncreaseTo(i.ToString().Trim('-').Length,0); return true; } } \ No newline at end of file diff --git a/TypeGuesser/Deciders/NeverGuessedTypeDecider.cs b/TypeGuesser/Deciders/NeverGuessedTypeDecider.cs index eca8d2e..5681574 100644 --- a/TypeGuesser/Deciders/NeverGuessedTypeDecider.cs +++ b/TypeGuesser/Deciders/NeverGuessedTypeDecider.cs @@ -20,7 +20,7 @@ public sealed class NeverGuessTheseTypeDecider(CultureInfo culture) : DecideType protected override object ParseImpl(string value) => throw new NotSupportedException(); /// - protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize sizeRecord) => + protected override bool IsAcceptableAsTypeImpl(string candidateString, IDataTypeSize? sizeRecord) => //strings should never be interpreted as byte arrays false; } \ No newline at end of file diff --git a/TypeGuesser/Deciders/TimeSpanTypeDecider.cs b/TypeGuesser/Deciders/TimeSpanTypeDecider.cs index fc3fe92..e2580d9 100644 --- a/TypeGuesser/Deciders/TimeSpanTypeDecider.cs +++ b/TypeGuesser/Deciders/TimeSpanTypeDecider.cs @@ -20,7 +20,7 @@ public sealed class TimeSpanTypeDecider(CultureInfo culture) : DecideTypesForStr protected override object ParseImpl(string value) => DateTime.Parse(value).TimeOfDay; /// - protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize sizeRecord) + protected override bool IsAcceptableAsTypeImpl(string candidateString,IDataTypeSize? sizeRecord) { try { diff --git a/TypeGuesser/DecimalSize.cs b/TypeGuesser/DecimalSize.cs index 75a71a0..e3ca99a 100644 --- a/TypeGuesser/DecimalSize.cs +++ b/TypeGuesser/DecimalSize.cs @@ -136,7 +136,7 @@ private bool Equals(DecimalSize other) } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is null) return false; if (ReferenceEquals(this, obj)) return true; diff --git a/TypeGuesser/GuessSettings.cs b/TypeGuesser/GuessSettings.cs index 2f31da0..5e00892 100644 --- a/TypeGuesser/GuessSettings.cs +++ b/TypeGuesser/GuessSettings.cs @@ -15,7 +15,7 @@ public class GuessSettings /// /// Optional, when set dates must be in one of these formats and any string in this format will be picked as a date. /// - public string[] ExplicitDateFormats { get; set; } + public string[] ExplicitDateFormats { get; set; } = []; /// diff --git a/TypeGuesser/Guesser.cs b/TypeGuesser/Guesser.cs index eb673af..21ed4d1 100644 --- a/TypeGuesser/Guesser.cs +++ b/TypeGuesser/Guesser.cs @@ -119,7 +119,7 @@ public void AdjustToCompensateForValues(IEnumerable collection) /// /// Thrown if you mix strings with hard Typed objects when supplying /// - public void AdjustToCompensateForValue(object o) + public void AdjustToCompensateForValue(object? o) { while (true) { @@ -136,7 +136,7 @@ public void AdjustToCompensateForValue(object o) var oToString = o.ToString(); //we might need to fallback on a string later on, in this case we should always record the maximum length of input seen before even if it is acceptable as int, double, dates etc - Guess.Width = Math.Max(Guess.Width ?? -1, GetStringLength(oToString)); + Guess.Width = Math.Max(Guess.Width ?? -1, GetStringLength(oToString??string.Empty)); //if it's a string if (o is string oAsString) @@ -179,7 +179,7 @@ public void AdjustToCompensateForValue(object o) } //if we have a decider for this lets get it to tell us the decimal places (if any) - if (_typeDeciders.Dictionary.TryGetValue(o.GetType(),out var decider)) + if (oToString!=null && _typeDeciders.Dictionary.TryGetValue(o.GetType(),out var decider)) decider.IsAcceptableAsType(oToString, Guess); break; } @@ -265,7 +265,7 @@ private void ThrowIfNotSupported(Type currentEstimate) /// /// If the current does not have a parser defined /// - public object Parse(string val) + public object? Parse(string val) { if (Guess.CSharpType == typeof(string)) return val;