Browse Source

Add methods for finding substrings in `Text`

pull/8/head
Anton Tarasenko 4 years ago
parent
commit
9cb465ffe1
  1. 183
      sources/Text/Tests/TEST_Text.uc
  2. 113
      sources/Text/Text.uc

183
sources/Text/Tests/TEST_Text.uc

@ -38,6 +38,7 @@ protected static function TESTS()
Test_Substring();
Test_SeparateByCharacter();
Test_StartsEndsWith();
Test_IndexOf();
}
protected static function Test_TextCreation()
@ -695,6 +696,188 @@ protected static function SubTest_EndsWith_Formatting()
.EndsWith(F("{#454545 r}aZy"),, SFORM_SENSITIVE));
}
protected static function Test_IndexOf()
{
Context("Testing `IndexOf()` method with non-formatted `Text`s.");
SubTest_IndexOfSuccess(SFORM_SENSITIVE);
SubTest_IndexOfSuccess(SFORM_INSENSITIVE);
SubTest_IndexOfFail(SFORM_SENSITIVE);
SubTest_IndexOfFail(SFORM_INSENSITIVE);
Context("Testing `IndexOf()` method with formatted `Text`s.");
SubTest_IndexOfFormatting();
Context("Testing `LastIndexOf()` method with non-formatted `Text`s.");
SubTest_LastIndexOfSuccess(SFORM_SENSITIVE);
SubTest_LastIndexOfSuccess(SFORM_INSENSITIVE);
SubTest_LastIndexOfFail(SFORM_SENSITIVE);
SubTest_LastIndexOfFail(SFORM_INSENSITIVE);
Context("Testing `LastIndexOf()` method with formatted `Text`s.");
SubTest_LastIndexOfFormatting();
}
protected static function SubTest_IndexOfSuccess(Text.FormatSensitivity flag)
{
Issue("`IndexOf()` works incorrectly with empty `Text`s.");
TEST_ExpectTrue(P("").IndexOf(F("{rgb(4,5,6) }"),,, flag) == 0);
TEST_ExpectTrue(P("something").IndexOf(P(""),,, flag) == 0);
TEST_ExpectFalse(
F("{rgb(23,342,32) }").IndexOf(P("something"),,, flag) == 0);
Issue("`IndexOf()` returns non-zero index for identical `Text`s.");
TEST_ExpectTrue(P("Just something").IndexOf(P("Just something")) == 0);
TEST_ExpectTrue(
P("CraZy").IndexOf(P("CRaZy"),, SCASE_INSENSITIVE, flag) == 0);
Issue("`IndexOf()` returns wrong index for correct sub-`Text`s.");
TEST_ExpectTrue(P("Just something").IndexOf(P("some"),,, flag) == 5);
TEST_ExpectTrue(P("Just something").IndexOf(P("some"), 3,, flag) == 5);
TEST_ExpectTrue(P("Just something").IndexOf(P("some"), 5,, flag) == 5);
TEST_ExpectTrue(
P("Just some-something").IndexOf(P("some"), 7,, flag) == 10);
Issue("`IndexOf()` returns wrong index for correct case-insensitive" @
"sub-`Text`s.");
TEST_ExpectTrue(P("JUSt sOmEtHiNG")
.IndexOf(P("sOME"),, SCASE_INSENSITIVE, flag)
== 5);
TEST_ExpectTrue(P("JUSt sOmEtHiNG")
.IndexOf(P("SoMe"), 3, SCASE_INSENSITIVE, flag)
== 5);
TEST_ExpectTrue(P("JUSt sOmEtHiNG")
.IndexOf(P("sOMe"), 5, SCASE_INSENSITIVE, flag)
== 5);
TEST_ExpectTrue(P("JUSt soME-sOmEtHiNG")
.IndexOf(P("SomE"), 7, SCASE_INSENSITIVE, flag)
== 10);
}
protected static function SubTest_IndexOfFail(Text.FormatSensitivity flag)
{
Issue("`IndexOf()` returns non-negative index for longer `Text`s.");
TEST_ExpectTrue(
P("text").IndexOf(P("image"),, SCASE_INSENSITIVE, flag) < 0);
TEST_ExpectTrue(P("++text").IndexOf(P("text+"), 2,, flag) < 0);
Issue("`IndexOf()` returns non-negative index when looking for `Text`s that"
@ "are not a substring.");
TEST_ExpectTrue(P("text").IndexOf(P("exd"),, SCASE_INSENSITIVE, flag) < 0);
TEST_ExpectTrue(P("A string").IndexOf(P(" string"),,, flag) < 0);
TEST_ExpectTrue(P("A string").IndexOf(P(" string"),,, flag) < 0);
TEST_ExpectTrue(P("A string").IndexOf(P("str"), 3,, flag) < 0);
TEST_ExpectTrue(P("A string").IndexOf(P("str"), 20,, flag) < 0);
}
protected static function SubTest_IndexOfFormatting()
{
Issue("`IndexOf()` returns non-zero index for identical `Text`s.");
TEST_ExpectTrue(F("Just {#4fe2ac some}thing")
.IndexOf(F("Just {#4fe2ac some}thing"),,, SFORM_SENSITIVE)
== 0);
TEST_ExpectTrue(F("Cra{#ff0000 Zy}")
.IndexOf(F("Cra{#ff0000 Zy}"),,, SFORM_SENSITIVE)
== 0);
Issue("`IndexOf()` returns wrong index for correct sub-`Text`s.");
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.IndexOf(F("so{#4f632dc me}"),,, SFORM_SENSITIVE)
== 5);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.IndexOf(F("so{#4f632dc me}"), 3,, SFORM_SENSITIVE)
== 5);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.IndexOf(F("so{#4f632dc me}"), 5,, SFORM_SENSITIVE)
== 5);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.IndexOf(F("{#4f632dc some}"),,, SFORM_SENSITIVE)
== 10);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.IndexOf(F("{#4f632dc some}"), 7,, SFORM_SENSITIVE)
== 10);
}
protected static function SubTest_LastIndexOfSuccess(
Text.FormatSensitivity flag)
{
Issue("`LastIndexOf()` works incorrectly with empty `Text`s.");
TEST_ExpectTrue(P("").LastIndexOf(F("{rgb(4,5,6) }"),,, flag) == 0);
TEST_ExpectTrue(P("something").LastIndexOf(P(""),,, flag) == 0);
TEST_ExpectFalse(
F("{rgb(23,342,32) }").LastIndexOf(P("something"),,, flag) == 0);
Issue("`LastIndexOf()` returns non-zero index for identical `Text`s.");
TEST_ExpectTrue(P("Just something").LastIndexOf(P("Just something")) == 0);
TEST_ExpectTrue(
P("CraZy").LastIndexOf(P("CRaZy"),, SCASE_INSENSITIVE, flag) == 0);
Issue("`LastIndexOf()` returns wrong index for correct sub-`Text`s.");
TEST_ExpectTrue(P("Just something").LastIndexOf(P("some"),,, flag) == 5);
TEST_ExpectTrue(P("Just something").LastIndexOf(P("some"), 3,, flag) == 5);
TEST_ExpectTrue(P("Just something").LastIndexOf(P("some"), 5,, flag) == 5);
TEST_ExpectTrue(
P("Just some-something").LastIndexOf(P("some"),,, flag) == 10);
TEST_ExpectTrue(
P("Just some-something").LastIndexOf(P("some"), 6,, flag) == 5);
Issue("`LastIndexOf()` returns wrong index for correct case-insensitive" @
"sub-`Text`s.");
TEST_ExpectTrue(P("JUSt sOmEtHiNG")
.LastIndexOf(P("sOME"),, SCASE_INSENSITIVE, flag)
== 5);
TEST_ExpectTrue(P("JUSt sOmEtHiNG")
.LastIndexOf(P("SoMe"), 3, SCASE_INSENSITIVE, flag)
== 5);
TEST_ExpectTrue(P("JUSt soME-sOmEtHiNG")
.LastIndexOf(P("sOMe"), 5, SCASE_INSENSITIVE, flag)
== 10);
TEST_ExpectTrue(P("JUSt soME-sOmEtHiNG")
.LastIndexOf(P("SomE"), 6, SCASE_INSENSITIVE, flag)
== 5);
}
protected static function SubTest_LastIndexOfFail(Text.FormatSensitivity flag)
{
Issue("`LastIndexOf()` returns non-negative index for longer `Text`s.");
TEST_ExpectTrue(
P("text").LastIndexOf(P("image"),, SCASE_INSENSITIVE, flag) < 0);
TEST_ExpectTrue(P("++text").LastIndexOf(P("text+"), 2,, flag) < 0);
Issue("`LastIndexOf()` returns non-negative index when looking for `Text`s that"
@ "are not a substring.");
TEST_ExpectTrue(
P("text").LastIndexOf(P("exd"),, SCASE_INSENSITIVE, flag) < 0);
TEST_ExpectTrue(P("A string").LastIndexOf(P(" string"),,, flag) < 0);
TEST_ExpectTrue(P("A string").LastIndexOf(P(" string"),,, flag) < 0);
TEST_ExpectTrue(P("A string").LastIndexOf(P("str"), 4,, flag) < 0);
TEST_ExpectTrue(P("A string").LastIndexOf(P("str"), 20,, flag) < 0);
}
protected static function SubTest_LastIndexOfFormatting()
{
Issue("`LastIndexOf()` returns non-zero index for identical `Text`s.");
TEST_ExpectTrue(F("Just {#4fe2ac some}thing")
.LastIndexOf(F("Just {#4fe2ac some}thing"),,, SFORM_SENSITIVE)
== 0);
TEST_ExpectTrue(F("Cra{#ff0000 Zy}")
.LastIndexOf(F("Cra{#ff0000 Zy}"),,, SFORM_SENSITIVE)
== 0);
Issue("`LastIndexOf()` returns wrong index for correct sub-`Text`s.");
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.LastIndexOf(F("{#4f632dc some}"),,, SFORM_SENSITIVE)
== 10);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.LastIndexOf(F("{#4f632dc some}"), 3,, SFORM_SENSITIVE)
== 10);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.LastIndexOf(F("{#4f632dc some}"), 5,, SFORM_SENSITIVE)
== 10);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.LastIndexOf(F("so{#4f632dc me}"),,, SFORM_SENSITIVE)
== 5);
TEST_ExpectTrue(F("Just so{#4f632dc me-some}thing")
.LastIndexOf(F("so{#4f632dc me}"), 7,, SFORM_SENSITIVE)
== 5);
}
defaultproperties
{
caseName = "Text/MutableText"

113
sources/Text/Text.uc

@ -573,7 +573,6 @@ public final function bool StartsWith(
if (GetLength() < otherText.GetLength()) {
return false;
}
// Copy once to avoid doing it each iteration
for (i = 0; i < otherText.GetLength(); i += 1)
{
char1 = GetCharacter(i);
@ -619,7 +618,6 @@ public final function bool EndsWith(
if (GetLength() < otherText.GetLength()) {
return false;
}
// Copy once to avoid doing it each iteration
index = GetLength() - 1;
otherIndex = otherText.GetLength() - 1;
while (otherIndex >= 0)
@ -1193,6 +1191,117 @@ public final function array<MutableText> SplitByCharacter(Character separator)
return result;
}
/**
* Returns the index position of the first occurrence of the `otherText` in
* the caller `Text`, searching forward from index position `fromIndex`.
*
* @param otherText `Text` data to find inside the caller `Text`.
* @param fromIndex Index from which we should start searching.
* @param caseSensitivity Defines whether comparison should be
* case-sensitive. By default it is.
* @param formatSensitivity Defines whether comparison should be
* sensitive for color information. By default it is not.
* @return `-1` if `otherText` is not found after `fromIndex`.
*/
public final function int IndexOf(
Text otherText,
optional int fromIndex,
optional CaseSensitivity caseSensitivity,
optional FormatSensitivity formatSensitivity)
{
local int startCandidate;
local int index, otherIndex;
local Character char1, char2;
if (otherText == none) return -1;
if (fromIndex > GetLength()) return -1;
if (GetLength() - fromIndex < otherText.GetLength()) return -1;
if (otherText.IsEmpty()) return fromIndex;
startCandidate = fromIndex;
for (index = fromIndex; index < GetLength(); index += 1)
{
char1 = GetCharacter(index);
char2 = otherText.GetCharacter(otherIndex);
if ( NormalizeCodePoint(char1.codePoint, caseSensitivity)
!= NormalizeCodePoint(char2.codePoint, caseSensitivity))
{
startCandidate = index + 1;
otherIndex = 0;
continue;
}
if ( formatSensitivity == SFORM_SENSITIVE
&& !_.text.IsFormattingEqual(char1.formatting, char2.formatting))
{
startCandidate = index + 1;
otherIndex = 0;
continue;
}
otherIndex += 1;
if (otherIndex == otherText.GetLength()) {
return startCandidate;
}
}
return -1;
}
/**
* Returns the index position of the last occurrence of the `otherText` in
* the caller `Text`, searching backward from index position `fromIndex`,
* counted the end of the caller `Text`.
*
* @param otherText `Text` data to find inside the caller `Text`.
* @param fromIndex Index from which we should start searching, but
* it's counted from the end of the caller `Text`: `0` means starting from
* the last character, `1` means next to last, etc.
* @param caseSensitivity Defines whether comparison should be
* case-sensitive. By default it is.
* @param formatSensitivity Defines whether comparison should be
* sensitive for color information. By default it is not.
* @return `-1` if `otherText` is not found starting `fromIndex`
* (this index is counted from the end of the caller `Text`).
*/
public final function int LastIndexOf(
Text otherText,
optional int fromIndex,
optional CaseSensitivity caseSensitivity,
optional FormatSensitivity formatSensitivity)
{
local int startCandidate;
local int index, otherIndex;
local Character char1, char2;
if (otherText == none) return -1;
if (fromIndex > GetLength()) return -1;
if (GetLength() - fromIndex < otherText.GetLength()) return -1;
if (otherText.IsEmpty()) return fromIndex;
otherIndex = otherText.GetLength() - 1;
startCandidate = GetLength() - fromIndex - 1;
for (index = startCandidate; index >= 0; index -= 1)
{
char1 = GetCharacter(index);
char2 = otherText.GetCharacter(otherIndex);
if ( NormalizeCodePoint(char1.codePoint, caseSensitivity)
!= NormalizeCodePoint(char2.codePoint, caseSensitivity))
{
startCandidate = index - 1;
otherIndex = otherText.GetLength() - 1;
continue;
}
if ( formatSensitivity == SFORM_SENSITIVE
&& !_.text.IsFormattingEqual(char1.formatting, char2.formatting))
{
startCandidate = index - 1;
otherIndex = otherText.GetLength() - 1;
continue;
}
otherIndex -= 1;
if (otherIndex < 0) {
return startCandidate - (otherText.GetLength() - 1);
}
}
return -1;
}
defaultproperties
{
STRING_SEPARATOR_FORMAT = " "

Loading…
Cancel
Save