Skip to content

Commit

Permalink
Add Reference.TryParseSheetName
Browse files Browse the repository at this point in the history
  • Loading branch information
jahav committed Oct 4, 2024
1 parent 6ac6999 commit 5e96a1a
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/ClosedXML.Parser.Tests/ReferenceParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,35 @@ public void TryParseSheetA1_requires_argument()
Assert.Throws<ArgumentNullException>(() => 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<ArgumentNullException>(() => 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<object[]> ParseSheetA1TestCases
{
get
Expand Down
44 changes: 44 additions & 0 deletions src/ClosedXML.Parser/ReferenceParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,50 @@ public static bool TryParseSheetA1(string text, out string sheetName, out Refere
return true;
}

/// <summary>
/// Try to parse a text as a sheet name (e.g. <c>Sheet!Name</c>). Doesn't accept pure name
/// without sheet (e.g. <c>name</c>).
/// </summary>
/// <param name="text">Text to parse.</param>
/// <param name="sheetName">Parsed sheet name, unescaped.</param>
/// <param name="name">Parsed defined name.</param>
/// <returns><c>true</c> if parsing was a success, <c>false</c> otherwise.</returns>
[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<Token> tokens)
{
var isValid = tokens.Count switch
Expand Down

0 comments on commit 5e96a1a

Please sign in to comment.