From 2bdec6c9deda3d8f8c5631e72c061be88d5a9399 Mon Sep 17 00:00:00 2001 From: jahav Date: Sat, 18 Feb 2023 03:27:58 +0100 Subject: [PATCH] Remove exception from FIND function. --- ClosedXML.Tests/Excel/CalcEngine/TextTests.cs | 74 ++++++++++++++++--- ClosedXML/Excel/CalcEngine/Functions/Text.cs | 25 +++---- 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/ClosedXML.Tests/Excel/CalcEngine/TextTests.cs b/ClosedXML.Tests/Excel/CalcEngine/TextTests.cs index 2770dec43..ca9d732f2 100644 --- a/ClosedXML.Tests/Excel/CalcEngine/TextTests.cs +++ b/ClosedXML.Tests/Excel/CalcEngine/TextTests.cs @@ -179,42 +179,96 @@ public void Exact_Value() Assert.AreEqual(false, actual); } + [Test] + public void Find_Empty_Pattern_And_Empty_Text() + { + // Different behavior from SEARCH + Assert.AreEqual(1, XLWorkbook.EvaluateExpr(@"FIND("""", """")")); + + Assert.AreEqual(2, XLWorkbook.EvaluateExpr(@"FIND("""", ""a"", 2)")); + } + + [Test] + public void Find_Empty_Search_Pattern_Returns_Start_Of_Text() + { + Assert.AreEqual(1, XLWorkbook.EvaluateExpr(@"FIND("""", ""asdf"")")); + } + + [Test] + public void Find_Looks_Only_From_Start_Position_Onward() + { + Assert.AreEqual(XLError.IncompatibleValue, XLWorkbook.EvaluateExpr(@"FIND(""This"", ""This is some text"", 2)")); + } + [Test] public void Find_Start_Position_Too_Large() { - Assert.That(() => XLWorkbook.EvaluateExpr(@"Find(""abc"", ""abcdef"", 10)"), Throws.TypeOf()); + Assert.AreEqual(XLError.IncompatibleValue, XLWorkbook.EvaluateExpr(@"FIND(""abc"", ""abcdef"", 10)")); + } + + [Test] + public void Find_Start_Position_Too_Small() + { + Assert.AreEqual(XLError.IncompatibleValue, XLWorkbook.EvaluateExpr(@"FIND(""text"", ""This is some text"", 0)")); } [Test] - public void Find_String_In_Another_Empty_String() + public void Find_Empty_Searched_Text_Returns_Error() { - Assert.That(() => XLWorkbook.EvaluateExpr(@"Find(""abc"", """")"), Throws.TypeOf()); + Assert.AreEqual(XLError.IncompatibleValue, XLWorkbook.EvaluateExpr(@"FIND(""abc"", """")")); } [Test] public void Find_String_Not_Found() { - Assert.That(() => XLWorkbook.EvaluateExpr(@"Find(""123"", ""asdf"")"), Throws.TypeOf()); + Assert.AreEqual(XLError.IncompatibleValue, XLWorkbook.EvaluateExpr(@"FIND(""123"", ""asdf"")")); } [Test] public void Find_Case_Sensitive_String_Not_Found() { // Find is case-sensitive - Assert.That(() => XLWorkbook.EvaluateExpr(@"Find(""excel"", ""Microsoft Excel 2010"")"), Throws.TypeOf()); + Assert.AreEqual(XLError.IncompatibleValue, XLWorkbook.EvaluateExpr(@"FIND(""excel"", ""Microsoft Excel 2010"")")); } [Test] public void Find_Value() { - Object actual = XLWorkbook.EvaluateExpr(@"Find(""Tuesday"", ""Today is Tuesday"")"); + var actual = XLWorkbook.EvaluateExpr(@"FIND(""Tuesday"", ""Today is Tuesday"")"); Assert.AreEqual(10, actual); - actual = XLWorkbook.EvaluateExpr(@"Find("""", """")"); - Assert.AreEqual(1, actual); + // Doesnt support wildcards + actual = XLWorkbook.EvaluateExpr(@"FIND(""T*y"", ""Today is Tuesday"")"); + Assert.AreEqual(XLError.IncompatibleValue, actual); + } - actual = XLWorkbook.EvaluateExpr(@"Find("""", ""asdf"")"); - Assert.AreEqual(1, actual); + [Test] + public void Find_Arguments_Are_Converted_To_Expected_Types() + { + var actual = XLWorkbook.EvaluateExpr(@"FIND(1.2, ""A1.2B"")"); + Assert.AreEqual(2, actual); + + actual = XLWorkbook.EvaluateExpr(@"FIND(TRUE, ""ATRUE"")"); + Assert.AreEqual(2, actual); + + actual = XLWorkbook.EvaluateExpr(@"FIND(23, 1.2345)"); + Assert.AreEqual(3, actual); + + actual = XLWorkbook.EvaluateExpr(@"FIND(""a"", ""aaaaa"", ""2 1/2"")"); + Assert.AreEqual(2, actual); + } + + [Test] + public void Find_Error_Arguments_Return_The_Error() + { + var actual = XLWorkbook.EvaluateExpr(@"FIND(#N/A, ""a"")"); + Assert.AreEqual(XLError.NoValueAvailable, actual); + + actual = XLWorkbook.EvaluateExpr(@"FIND("""", #N/A)"); + Assert.AreEqual(XLError.NoValueAvailable, actual); + + actual = XLWorkbook.EvaluateExpr(@"FIND(""a"", ""a"", #N/A)"); + Assert.AreEqual(XLError.NoValueAvailable, actual); } [Test] diff --git a/ClosedXML/Excel/CalcEngine/Functions/Text.cs b/ClosedXML/Excel/CalcEngine/Functions/Text.cs index 3fc3c5761..1147e7c11 100644 --- a/ClosedXML/Excel/CalcEngine/Functions/Text.cs +++ b/ClosedXML/Excel/CalcEngine/Functions/Text.cs @@ -25,7 +25,7 @@ public static void Register(FunctionRegistry ce) ce.RegisterFunction("CONCATENATE", 1, int.MaxValue, Concatenate, AllowRange.All); // Joins several text items into one text item ce.RegisterFunction("DOLLAR", 1, 2, Dollar); // Converts a number to text, using the $ (dollar) currency format ce.RegisterFunction("EXACT", 2, Exact); // Checks to see if two text values are identical - ce.RegisterFunction("FIND", 2, 3, Find); //Finds one text value within another (case-sensitive) + ce.RegisterFunction("FIND", 2, 3, AdaptLastOptional(Find), FunctionFlags.Scalar); //Finds one text value within another (case-sensitive) ce.RegisterFunction("FIXED", 1, 3, Fixed); // Formats a number as text with a fixed number of decimals //ce.RegisterFunction("JIS Changes half-width (single-byte) English letters or katakana within a character string to full-width (double-byte) characters ce.RegisterFunction("LEFT", 1, 2, Left); // LEFTB Returns the leftmost characters from a text value @@ -106,20 +106,17 @@ private static object Concatenate(List p) return sb.ToString(); } - private static object Find(List p) + private static AnyValue Find(CalcContext ctx, String findText, String withinText, OneOf startNum) { - var srch = (string)p[0]; - var text = (string)p[1]; - var start = 0; - if (p.Count > 2) - { - start = (int)p[2] - 1; - } - var index = text.IndexOf(srch, start, StringComparison.Ordinal); - if (index == -1) - throw new ArgumentException("String not found."); - else - return index + 1; + var startIndex = startNum.TryPickT0(out var startNumber, out _) ? (int)Math.Truncate(startNumber) - 1 : 0; + if (startIndex < 0 || startIndex > withinText.Length) + return XLError.IncompatibleValue; + + var text = withinText.AsSpan(startIndex); + var index = text.IndexOf(findText.AsSpan()); + return index == -1 + ? XLError.IncompatibleValue + : index + startIndex + 1; } private static object Left(List p)