Skip to content

Commit 12b5df2

Browse files
committed
Add ReferenceParser.TryParseName
1 parent b96cada commit 12b5df2

File tree

2 files changed

+103
-27
lines changed

2 files changed

+103
-27
lines changed

src/ClosedXML.Parser.Tests/ReferenceParserTests.cs

+41
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,47 @@ public void TryParseSheetName_cant_parse_pure_name(string text)
105105
Assert.False(success);
106106
}
107107

108+
[Theory]
109+
[InlineData("Sheet!Name", "Sheet", "Name")]
110+
[InlineData("'Hello World'!Name", "Hello World", "Name")]
111+
[InlineData("' John''s World! '!Name", " John's World! ", "Name")]
112+
public void TryParseName_parses_sheet_and_name(string text, string expectedSheet, string expectedName)
113+
{
114+
var success = ReferenceParser.TryParseName(text, out var sheet, out var name);
115+
Assert.True(success);
116+
Assert.Equal(expectedSheet, sheet);
117+
Assert.Equal(expectedName, name);
118+
}
119+
120+
[Fact]
121+
public void TryParseName_requires_text()
122+
{
123+
Assert.Throws<ArgumentNullException>(() => ReferenceParser.TryParseName(null!, out _, out _));
124+
}
125+
126+
[Theory]
127+
[InlineData("Name")]
128+
[InlineData("some_name")]
129+
public void TryParseName_parses_name(string text)
130+
{
131+
var success = ReferenceParser.TryParseName(text, out var sheet, out var name);
132+
Assert.True(success);
133+
Assert.Null(sheet);
134+
Assert.Equal(text, name);
135+
}
136+
137+
[Theory]
138+
[InlineData("A1")]
139+
[InlineData("$BC$1")]
140+
[InlineData("Sheet!A1")]
141+
[InlineData("14")]
142+
[InlineData("\"Text\"")]
143+
public void TryParseName_cant_parse_anything_but_name(string text)
144+
{
145+
var success = ReferenceParser.TryParseName(text, out _, out _);
146+
Assert.False(success);
147+
}
148+
108149
[Theory]
109150
[MemberData(nameof(ParseA1TestCases))]
110151
public void TryParseA1_unified_can_parse_local_reference(string text, ReferenceArea expectedReference)

src/ClosedXML.Parser/ReferenceParser.cs

+62-27
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,36 @@ public static bool TryParseSheetA1(string text, out string sheetName, out Refere
112112
return TryParseSheetA1(tokens, text, out sheetName, out area);
113113
}
114114

115+
/// <summary>
116+
/// <para>
117+
/// Try to parse <paramref name="text"/> as a name (e.g. <c>Name</c>) or a sheet name
118+
/// (<c>Sheet!Name</c>). If the <paramref name="text"/> is only a name, the output value of the
119+
/// <paramref name="sheetName"/> is <c>null</c>.
120+
/// </para>
121+
/// </summary>
122+
/// <param name="text">Text to parse.</param>
123+
/// <param name="sheetName">The unescaped name of a sheet for sheet name, <c>null</c> for a name.</param>
124+
/// <param name="name">The parsed name.</param>
125+
/// <returns><c>true</c> if parsing was a success, <c>false</c> otherwise.</returns>
126+
[PublicAPI]
127+
public static bool TryParseName(string text, out string? sheetName, out string name)
128+
{
129+
if (text is null)
130+
throw new ArgumentNullException(nameof(text));
131+
132+
var tokens = RolexLexer.GetTokensA1(text.AsSpan());
133+
if (tokens.Count == 2 &&
134+
tokens[0].SymbolId == Token.NAME &&
135+
tokens[1].SymbolId == Token.EofSymbolId)
136+
{
137+
sheetName = null;
138+
name = text;
139+
return true;
140+
}
141+
142+
return TryParseSheetName(tokens, text, out sheetName, out name);
143+
}
144+
115145
/// <summary>
116146
/// Try to parse a text as a sheet name (e.g. <c>Sheet!Name</c>). Doesn't accept pure name
117147
/// without sheet (e.g. <c>name</c>).
@@ -127,33 +157,7 @@ public static bool TryParseSheetName(string text, out string sheetName, out stri
127157
throw new ArgumentNullException(nameof(text));
128158

129159
var tokens = RolexLexer.GetTokensA1(text.AsSpan());
130-
var isValid = tokens.Count switch
131-
{
132-
3 => tokens[0].SymbolId == Token.SINGLE_SHEET_PREFIX &&
133-
tokens[1].SymbolId == Token.NAME &&
134-
tokens[2].SymbolId == Token.EofSymbolId,
135-
_ => false,
136-
};
137-
if (!isValid)
138-
{
139-
sheetName = string.Empty;
140-
name = string.Empty;
141-
return false;
142-
}
143-
144-
var sheetPrefixToken = tokens[0];
145-
var sheetPrefix = text.AsSpan(sheetPrefixToken.StartIndex, sheetPrefixToken.Length);
146-
TokenParser.ParseSingleSheetPrefix(sheetPrefix, out int? workbookIndex, out sheetName);
147-
if (workbookIndex is not null)
148-
{
149-
sheetName = string.Empty;
150-
name = string.Empty;
151-
return false;
152-
}
153-
154-
var nameToken = tokens[1];
155-
name = text.AsSpan().Slice(nameToken.StartIndex, nameToken.Length).ToString();
156-
return true;
160+
return TryParseSheetName(tokens, text, out sheetName, out name);
157161
}
158162

159163
private static bool TryParseA1(List<Token> tokens, string text, out ReferenceArea area)
@@ -219,4 +223,35 @@ private static bool IsA1Reference(IReadOnlyList<Token> tokens)
219223
};
220224
return isValid;
221225
}
226+
227+
private static bool TryParseSheetName(List<Token> tokens, string text, out string sheetName, out string name)
228+
{
229+
var isValid = tokens.Count switch
230+
{
231+
3 => tokens[0].SymbolId == Token.SINGLE_SHEET_PREFIX &&
232+
tokens[1].SymbolId == Token.NAME &&
233+
tokens[2].SymbolId == Token.EofSymbolId,
234+
_ => false,
235+
};
236+
if (!isValid)
237+
{
238+
sheetName = string.Empty;
239+
name = string.Empty;
240+
return false;
241+
}
242+
243+
var sheetPrefixToken = tokens[0];
244+
var sheetPrefix = text.AsSpan(sheetPrefixToken.StartIndex, sheetPrefixToken.Length);
245+
TokenParser.ParseSingleSheetPrefix(sheetPrefix, out int? workbookIndex, out sheetName);
246+
if (workbookIndex is not null)
247+
{
248+
sheetName = string.Empty;
249+
name = string.Empty;
250+
return false;
251+
}
252+
253+
var nameToken = tokens[1];
254+
name = text.AsSpan().Slice(nameToken.StartIndex, nameToken.Length).ToString();
255+
return true;
256+
}
222257
}

0 commit comments

Comments
 (0)