From 5e96a1af3136adf6bda8273c85832fa77b0e4e61 Mon Sep 17 00:00:00 2001 From: jahav Date: Fri, 4 Oct 2024 18:48:00 +0200 Subject: [PATCH] Add Reference.TryParseSheetName --- .../ReferenceParserTests.cs | 29 ++++++++++++ src/ClosedXML.Parser/ReferenceParser.cs | 44 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/ClosedXML.Parser.Tests/ReferenceParserTests.cs b/src/ClosedXML.Parser.Tests/ReferenceParserTests.cs index 740209e..9207132 100644 --- a/src/ClosedXML.Parser.Tests/ReferenceParserTests.cs +++ b/src/ClosedXML.Parser.Tests/ReferenceParserTests.cs @@ -76,6 +76,35 @@ public void TryParseSheetA1_requires_argument() Assert.Throws(() => ReferenceParser.TryParseSheetA1(null!, out _, out _)); } + [Theory] + [InlineData("Sheet!Name", "Sheet", "Name")] + [InlineData("'Hello World'!Name", "Hello World", "Name")] + [InlineData("' John''s World! '!Name", " John's World! ", "Name")] + public void TryParseSheetName_parses_sheet_and_name(string text, string expectedSheet, string expectedName) + { + var success = ReferenceParser.TryParseSheetName(text, out var sheet, out var name); + + Assert.True(success); + Assert.Equal(expectedSheet, sheet); + Assert.Equal(expectedName, name); + } + + [Fact] + public void TryParseSheetName_requires_text() + { + Assert.Throws(() => ReferenceParser.TryParseSheetName(null!, out _, out _)); + } + + [Theory] + [InlineData("Name")] + [InlineData("some_name")] + [InlineData("A1")] + public void TryParseSheetName_cant_parse_pure_name(string text) + { + var success = ReferenceParser.TryParseSheetName(text, out _, out _); + Assert.False(success); + } + public static IEnumerable ParseSheetA1TestCases { get diff --git a/src/ClosedXML.Parser/ReferenceParser.cs b/src/ClosedXML.Parser/ReferenceParser.cs index 2b35d59..6787a18 100644 --- a/src/ClosedXML.Parser/ReferenceParser.cs +++ b/src/ClosedXML.Parser/ReferenceParser.cs @@ -119,6 +119,50 @@ public static bool TryParseSheetA1(string text, out string sheetName, out Refere return true; } + /// + /// Try to parse a text as a sheet name (e.g. Sheet!Name). Doesn't accept pure name + /// without sheet (e.g. name). + /// + /// Text to parse. + /// Parsed sheet name, unescaped. + /// Parsed defined name. + /// true if parsing was a success, false otherwise. + [PublicAPI] + public static bool TryParseSheetName(string text, out string sheetName, out string name) + { + if (text is null) + throw new ArgumentNullException(nameof(text)); + + var tokens = RolexLexer.GetTokensA1(text.AsSpan()); + var isValid = tokens.Count switch + { + 3 => tokens[0].SymbolId == Token.SINGLE_SHEET_PREFIX && + tokens[1].SymbolId == Token.NAME && + tokens[2].SymbolId == Token.EofSymbolId, + _ => false, + }; + if (!isValid) + { + sheetName = string.Empty; + name = string.Empty; + return false; + } + + var sheetPrefixToken = tokens[0]; + var sheetPrefix = text.AsSpan(sheetPrefixToken.StartIndex, sheetPrefixToken.Length); + TokenParser.ParseSingleSheetPrefix(sheetPrefix, out int? workbookIndex, out sheetName); + if (workbookIndex is not null) + { + sheetName = string.Empty; + name = string.Empty; + return false; + } + + var nameToken = tokens[1]; + name = text.AsSpan().Slice(nameToken.StartIndex, nameToken.Length).ToString(); + return true; + } + private static bool IsA1Reference(IReadOnlyList tokens) { var isValid = tokens.Count switch