Anton Tarasenko
2 years ago
4 changed files with 1434 additions and 18 deletions
@ -0,0 +1,361 @@ |
|||||||
|
/** |
||||||
|
* Set of tests for functionality of `TextTemplate`. |
||||||
|
* Copyright 2022 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class TEST_TextTemplate extends TestCase |
||||||
|
abstract; |
||||||
|
|
||||||
|
protected static function TESTS() |
||||||
|
{ |
||||||
|
Context("Testing `TextTemplate` class' ability to handle empty (erroneous)" |
||||||
|
@ "argument declarations."); |
||||||
|
Test_EmptyDeclarations(); |
||||||
|
Context("Testing `TextTemplate` class' ability to handle numeric" |
||||||
|
@ "argument declarations."); |
||||||
|
Test_NumericDeclarationsAmount(); |
||||||
|
Test_NumericDeclarationsOrder(); |
||||||
|
Test_NumericDeclarationsNoOrder(); |
||||||
|
Test_NumericDeclarationsRandomValues(); |
||||||
|
Context("Testing `TextTemplate` class' ability to handle text" |
||||||
|
@ "argument declarations."); |
||||||
|
Test_TextDeclarationsGet(); |
||||||
|
Test_TextDeclarationsEmptyCollect(); |
||||||
|
Test_TextDeclarationsCollect(); |
||||||
|
Test_TextDeclarationsOverwriteCollect(); |
||||||
|
Context("Testing complex `TextTemplate` scenarios."); |
||||||
|
Test_Reset(); |
||||||
|
Test_Complex(); |
||||||
|
Test_Formatted(); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_EmptyDeclarations() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Empty `TextTemplate` is not collected into empty text."); |
||||||
|
instance = __().text.MakeTemplate_S(""); |
||||||
|
TEST_ExpectTrue(instance.Collect().IsEmpty()); |
||||||
|
|
||||||
|
Issue("`TextTemplate` with empty numeric definition is not properly" |
||||||
|
@ "collected."); |
||||||
|
instance = __().text.MakeTemplate_S("%"); |
||||||
|
TEST_ExpectTrue(instance.Collect().IsEmpty()); |
||||||
|
instance = __().text.MakeTemplate_S("With some%text"); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "With sometext"); |
||||||
|
|
||||||
|
Issue("`TextTemplate` with empty text definition is not properly" |
||||||
|
@ "collected."); |
||||||
|
instance = __().text.MakeTemplate_S("%%"); |
||||||
|
TEST_ExpectTrue(instance.Collect().IsEmpty()); |
||||||
|
instance = __().text.MakeTemplate_S("With some%%%text and %%%%more!"); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "With sometext and more!"); |
||||||
|
|
||||||
|
Issue("`TextTemplate` with definitions opened at the very end is not" |
||||||
|
@ "properly collected."); |
||||||
|
instance = __().text.MakeTemplate_S("Some text: %"); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "Some text: "); |
||||||
|
instance = __().text.MakeTemplate_S("Another example: %%"); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "Another example: "); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_NumericDeclarationsAmount() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("`GetNumericArgsAmount()` does not return `0` for `Text` without any" |
||||||
|
@ "numeric declarations."); |
||||||
|
instance = __().text.MakeTemplate_S(""); |
||||||
|
TEST_ExpectTrue(instance.GetNumericArgsAmount() == 0); |
||||||
|
instance = __().text.MakeTemplate_S("With some%text"); |
||||||
|
TEST_ExpectTrue(instance.GetNumericArgsAmount() == 0); |
||||||
|
instance = __().text.MakeTemplate_S("With some%%%text and %%%%more!"); |
||||||
|
TEST_ExpectTrue(instance.GetNumericArgsAmount() == 0); |
||||||
|
instance = __().text.MakeTemplate_S("With some%%valid arg%text and" |
||||||
|
@ "%%me too%%more!"); |
||||||
|
TEST_ExpectTrue(instance.GetNumericArgsAmount() == 0); |
||||||
|
|
||||||
|
Issue("`GetNumericArgsAmount()` does not correctly counts numeric" |
||||||
|
@ "declarations."); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %2 %3 %t %4 %5 %%text one!%%" |
||||||
|
@" %6 %7 %8"); |
||||||
|
TEST_ExpectTrue(instance.GetNumericArgsAmount() == 8); |
||||||
|
|
||||||
|
Issue("`GetNumericArgsAmount()` does not correctly counts numeric" |
||||||
|
@ "declarations with escaped '%' characters."); |
||||||
|
instance = __().text.MakeTemplate_S("just &%1 %2 %3 %t &%4 %5 %%text one!%%" |
||||||
|
@" %6 %7 %8"); |
||||||
|
TEST_ExpectTrue(instance.GetNumericArgsAmount() == 6); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_NumericDeclarationsOrder() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %1!"); |
||||||
|
instance.Arg(P("<enter_phrase_here>")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "This is argument: <enter_phrase_here>!"); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %2 %3 %4 %5 %6 %7 %8"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")).Arg(P("d")).Arg(P("e")) |
||||||
|
.Arg(P("f")).Arg(P("g")).Arg(P("h")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "just a b c d e f g h"); |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended after" |
||||||
|
@ "specifying too many."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %1!"); |
||||||
|
instance.Arg(P("<enter_phrase_here>")).Arg(P("Now this is too much!")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "This is argument: <enter_phrase_here>!"); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %2 %3 %4 %5 %6 %7 %8"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")).Arg(P("d")).Arg(P("e")) |
||||||
|
.Arg(P("f")).Arg(P("g")).Arg(P("h")).Arg(P("i")).Arg(P("j")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "just a b c d e f g h"); |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended after" |
||||||
|
@ "specifying too little."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %1!"); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "This is argument: !"); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %2 %3 %4 %5 %6 %7 %8"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")).Arg(P("d")).Arg(P("e")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "just a b c d e "); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_NumericDeclarationsNoOrder() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %2! And this: %1!!"); |
||||||
|
instance.Arg(P("<enter_phrase_here>")).Arg(P("heh")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "This is argument: heh! And this: <enter_phrase_here>!!"); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %3 %5 %2 %4 %8 %6 %7"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")).Arg(P("d")).Arg(P("e")) |
||||||
|
.Arg(P("f")).Arg(P("g")).Arg(P("h")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "just a c e b d h f g"); |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended after" |
||||||
|
@ "specifying too many."); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %3 %5 %2 %4 %8 %6 %7"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")).Arg(P("d")).Arg(P("e")) |
||||||
|
.Arg(P("f")).Arg(P("g")).Arg(P("h")).Arg(P("i")).Arg(P("j")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "just a c e b d h f g"); |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended after" |
||||||
|
@ "specifying too little."); |
||||||
|
instance = __().text.MakeTemplate_S("just %1 %3 %5 %2 %4 %8 %6 %7"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == "just a c b "); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_NumericDeclarationsRandomValues() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Basic numeric declarations are not working as intended when using" |
||||||
|
@ "arbitrary integer values."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument:" |
||||||
|
@ "%205! And this: %-7!!"); |
||||||
|
instance.Arg(P("<enter_phrase_here>")).Arg(P("heh")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "This is argument: heh! And this: <enter_phrase_here>!!"); |
||||||
|
instance = __().text.MakeTemplate_S("just %-3912842 %0 %666 %-111 %34" |
||||||
|
@ "%666999 %10234 %10235"); |
||||||
|
instance.Arg(P("a")).Arg(P("b")).Arg(P("c")).Arg(P("d")).Arg(P("e")) |
||||||
|
.Arg(P("f")).Arg(P("g")).Arg(P("h")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "just a c e b d h f g"); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_TextDeclarationsGet() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
local array<Text> result; |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly returned by" |
||||||
|
@ "the `GetTextlabels()` method."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %%argument!%% and" |
||||||
|
@ "so its this: %%another arg%%"); |
||||||
|
result = instance.GetTextArgs(); |
||||||
|
TEST_ExpectTrue(result[0].ToString() == "argument!"); |
||||||
|
TEST_ExpectTrue(result[1].ToString() == "another arg"); |
||||||
|
TEST_ExpectTrue(result.length == 2); |
||||||
|
instance.TextArg(P("another arg"), P("heh")) |
||||||
|
.TextArg(P("argument!"), P("<enter_phrase_here>")); |
||||||
|
result = instance.GetTextArgs(); |
||||||
|
TEST_ExpectTrue(result[0].ToString() == "argument!"); |
||||||
|
TEST_ExpectTrue(result[1].ToString() == "another arg"); |
||||||
|
TEST_ExpectTrue(result.length == 2); |
||||||
|
|
||||||
|
instance = __().text.MakeTemplate_S("Here: %2 %76 %4 %5 %8"); |
||||||
|
result = instance.GetTextArgs(); |
||||||
|
TEST_ExpectTrue(result.length == 0); |
||||||
|
|
||||||
|
instance = __().text.MakeTemplate_S("More %%complex%% %7 %%/example% here," |
||||||
|
@ "yes. Very %%complex%!"); |
||||||
|
result = instance.GetTextArgs(); |
||||||
|
TEST_ExpectTrue(result[0].ToString() == "complex"); |
||||||
|
TEST_ExpectTrue(result[1].ToString() == "/example"); |
||||||
|
TEST_ExpectTrue(result.length == 2); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_TextDeclarationsEmptyCollect() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("`TextTempalte` is not properly collected with it contains empty" |
||||||
|
@ "text labels."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %%%% and" |
||||||
|
@ "so its this: %%%"); |
||||||
|
instance.TextArg(P(""), P("what do y'll know?!")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== ("This is argument: what do y'll know?! and so its this:" |
||||||
|
@ "what do y'll know?!")); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_TextDeclarationsCollect() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected."); |
||||||
|
instance = __().text.MakeTemplate_S("This is argument: %%argument!%% and" |
||||||
|
@ "so its this: %%another arg%%"); |
||||||
|
instance.TextArg(P("another arg"), P("heh")) |
||||||
|
.TextArg(P("argument!"), P("<enter_phrase_here>")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "This is argument: <enter_phrase_here> and so its this: heh"); |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected when duplicate" |
||||||
|
@ "labels are present."); |
||||||
|
instance = __().text.MakeTemplate_S("More %%complex%% %7 %%/example% here," |
||||||
|
@ "yes. Very %%complex%!"); |
||||||
|
instance.TextArg(P("complex"), P("simple")).TextArg(P("/example"), P("!!")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "More simple !! here, yes. Very simple!"); |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected when specified" |
||||||
|
@ "too little arguments."); |
||||||
|
instance = __().text.MakeTemplate_S("More %%complex%% %7 %%/example% here," |
||||||
|
@ "yes. Very %%complex% and %%nasty%%!!!"); |
||||||
|
instance.TextArg(P("complex"), P("simple")).TextArg(P("nasty"), P("-_-")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "More simple here, yes. Very simple and -_-!!!"); |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected when specified" |
||||||
|
@ "too many arguments."); |
||||||
|
instance = __().text.MakeTemplate_S("More %%complex%% %11 %%/example% here," |
||||||
|
@ "yes. Very %%complex% and %%nasty%%!!!"); |
||||||
|
instance.TextArg(P("complex"), P("simple")).TextArg(P("nasty"), P("-_-")) |
||||||
|
.TextArg(P("/usr/bin"), P("geronimo")).TextArg(P("/example"), P("???")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "More simple ??? here, yes. Very simple and -_-!!!"); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_TextDeclarationsOverwriteCollect() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected when some labels" |
||||||
|
@ "are overwritten."); |
||||||
|
instance = __().text.MakeTemplate_S("More %%complex%% %7 %%/example% here," |
||||||
|
@ "yes. Very %%complex%!"); |
||||||
|
instance.TextArg(P("complex"), P("simple")).TextArg(P("/example"), P("!!")) |
||||||
|
.TextArg(P("complex"), P("nasty")).TextArg(P("/exe"), P("???")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== "More nasty !! here, yes. Very nasty!"); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_Reset() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("`Reset()` does not properly reset user's input."); |
||||||
|
instance = __().text.MakeTemplate_S("Testing %1, %2, %3 and %%one%%," |
||||||
|
@ "%%two%% + %%three%%"); |
||||||
|
instance.Arg_S("1").Arg_S("2").Arg_S("3").Arg_S("more?"); |
||||||
|
instance |
||||||
|
.TextArg(P("one"), P("4")) |
||||||
|
.TextArg(P("two"), P("5")) |
||||||
|
.TextArg(P("three"), P("6")); |
||||||
|
instance.Reset().Arg_S("HEY").Arg_S("ARE").Arg_S("YOU"); |
||||||
|
instance |
||||||
|
.TextArg(P("one"), P("READY")) |
||||||
|
.TextArg(P("two"), P("TO")) |
||||||
|
.TextArg(P("three"), P("GO?")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() == ("Testing HEY, ARE, YOU" |
||||||
|
@ "and READY, TO + GO?")); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_Complex() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
local array<Text> result; |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected in complex scenario" |
||||||
|
@ "with several numeric / text arguments and escaped characters."); |
||||||
|
instance = __().text.MakeTemplate_S("Welcome %%MoonAndStar%%, it is %7 nice" |
||||||
|
@ "to %-2 %%you%%. %%MoonAndStar%% drop your %%weapons%%, it is not too" |
||||||
|
@ "%0 for &m&y %%mercy%&%!"); |
||||||
|
instance |
||||||
|
.TextArg(P("MoonAndStar"), P("Nerevar")) |
||||||
|
.TextArg(P("you"), P("you")) |
||||||
|
.TextArg(P("weapons"), P("cheats")) |
||||||
|
.Arg(P("see")) |
||||||
|
.Arg(P("late")) |
||||||
|
.Arg(P("so")); |
||||||
|
TEST_ExpectTrue(instance.Collect().ToString() |
||||||
|
== ("Welcome Nerevar, it is so nice to see you." |
||||||
|
@ "Nerevar drop your cheats, it is not too late for &m&y %!")); |
||||||
|
result = instance.GetTextArgs(); |
||||||
|
TEST_ExpectTrue(result[0].ToString() == "MoonAndStar"); |
||||||
|
TEST_ExpectTrue(result[1].ToString() == "you"); |
||||||
|
TEST_ExpectTrue(result[2].ToString() == "weapons"); |
||||||
|
TEST_ExpectTrue(result[3].ToString() == "mercy"); |
||||||
|
TEST_ExpectTrue(result.length == 4); |
||||||
|
} |
||||||
|
|
||||||
|
protected static function Test_Formatted() |
||||||
|
{ |
||||||
|
local TextTemplate instance; |
||||||
|
|
||||||
|
Issue("Specified text labels aren't properly collected in complex scenario" |
||||||
|
@ "with several numeric / text arguments and escaped characters and" |
||||||
|
@ "we are asking to parse template as a formatted string" |
||||||
|
@ "(`CollectFormatted()` method)."); |
||||||
|
instance = __().text.MakeTemplate_S("Test simple {%%color%% %1} string" |
||||||
|
@ "that is {%2 %%what%%}!"); |
||||||
|
instance |
||||||
|
.Arg(P("formatted")) |
||||||
|
.Arg(P("$blue")) |
||||||
|
.TextArg(P("color"), P("$red")) |
||||||
|
.TextArg(P("what"), P("colored")); |
||||||
|
TEST_ExpectTrue(instance.CollectFormatted().ToFormattedString() |
||||||
|
== ("Test simple {rgb(255,0,0) formatted} string that is" |
||||||
|
@ "{rgb(0,0,255) colored}!")); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
caseName = "TextTemplate" |
||||||
|
caseGroup = "Text" |
||||||
|
} |
@ -0,0 +1,975 @@ |
|||||||
|
/** |
||||||
|
* Class that allows to work with simple text templates in the form of |
||||||
|
* "Template example, following can be replaced: %1, %2, %3 or %4" or |
||||||
|
* "%%instigator%% {%%rage_color%% raged} %%target_zed%%!". |
||||||
|
* Part noted by '%' characters can be replaced with arbitrary values. |
||||||
|
* It should be more efficient that simply repeatedly calling |
||||||
|
* `MutableText.Replace()` method. |
||||||
|
* Copyright 2022 Anton Tarasenko |
||||||
|
*------------------------------------------------------------------------------ |
||||||
|
* This file is part of Acedia. |
||||||
|
* |
||||||
|
* Acedia is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Acedia is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class TextTemplate extends AcediaObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* # `TextTemplate` |
||||||
|
* |
||||||
|
* This class allows to do more efficient replacements in the templates of |
||||||
|
* form "Template example, following can be replaced: %1, %2, %3 or %4" or |
||||||
|
* "%%instigator%% {%%rage_color%% raged} %%target_zed%%!" that simple |
||||||
|
* repeating of `MutableText.Replace()` method would allow. |
||||||
|
* |
||||||
|
* ## Usage |
||||||
|
* |
||||||
|
* All `TextTemplate`s must be initialized before they can be used. |
||||||
|
* `_.text.MakeTemplate()` method is considered a recommended way to create |
||||||
|
* a new `TextTemplate` and will initialize it for you. In case you have |
||||||
|
* manually allocated `TextTemplate` you need to simply call |
||||||
|
* `TextTemplate.Initialize()` method and provide is with a template source |
||||||
|
* with designated replaceable part (called *labels*). Any label is designated |
||||||
|
* by either: |
||||||
|
* |
||||||
|
* * Specifying a number after a single '%' character (numeric labels); |
||||||
|
* * Or specifying a textual value in between double percent sequence "%%". |
||||||
|
* |
||||||
|
* Numeric labels can then be replaced with user values by various provided |
||||||
|
* `Arg...()` methods and text labels by `TextArg...()` methods. To reuse |
||||||
|
* template with another values replacing specified labels, simply call |
||||||
|
* `TextTemplate.Reset()` method and fill labels anew. |
||||||
|
* |
||||||
|
* ### Escaped sequences |
||||||
|
* |
||||||
|
* `TextTemplate` allows for the escaped sequences in the style of |
||||||
|
* formatted strings: the ones based on the '&' character. More precisely, if |
||||||
|
* you wish to enter actual '%' character in the template, you can escape it |
||||||
|
* with "&%" and it will be treaded as simply '%' character and not a part of |
||||||
|
* label definition. |
||||||
|
* NOTE: any other escaped sequences will be ignored and translated |
||||||
|
* one-to-one. For example, "{%%color%% &{...&}&%!}", if specified that |
||||||
|
* "color" -> "$red", will be translated into "{$red &{...&}%!}". This allows |
||||||
|
* to use `TextTemplate`s to prepare formatted strings in a more convenient |
||||||
|
* fashion. |
||||||
|
* |
||||||
|
* ### Error handling |
||||||
|
* |
||||||
|
* `TextTemplate` lack any means of reporting errors in template's formatting |
||||||
|
* and tries to correct as many errors as possible. |
||||||
|
* |
||||||
|
* 1. If after the single '%' character follows not a number, but another |
||||||
|
* character - that '%' will be ignored and thrown away; |
||||||
|
* 2. After opening text label with double percent "%%" to stop specifying |
||||||
|
* the text label it is sufficient to type a single '%' character. |
||||||
|
* |
||||||
|
* ### Numeric labels values |
||||||
|
* |
||||||
|
* You can specify any values for numeric labels and they will be filled in |
||||||
|
* order. However the proper notation is to start with `1` and then use |
||||||
|
* following natural numbers in order (e.g. "%1", "%2", "%3", etc.). |
||||||
|
* |
||||||
|
* ### Text labels duplicates |
||||||
|
* |
||||||
|
* It is allowed for the same text label to appear several times in a template |
||||||
|
* and each entry will be replaced with a specified user value. |
||||||
|
* |
||||||
|
* ### Unspecified arguments |
||||||
|
* |
||||||
|
* If no argument was given for some label - it will simply be replaced with |
||||||
|
* empty text. |
||||||
|
*/ |
||||||
|
|
||||||
|
// *Labels* will be an internal name for the parts of the template that is |
||||||
|
// supposed to be replaced. This struct describes one such part. |
||||||
|
struct Label |
||||||
|
{ |
||||||
|
// If Label starts with two percent characters ("%%"), then it is |
||||||
|
// a text Label and we will remember contents within "%%...%%" in this |
||||||
|
// field (`textLabel != none`). |
||||||
|
// Otherwise it is a numeric Label and `textLabel` will be set |
||||||
|
// to `none`. |
||||||
|
var MutableText textLabel; |
||||||
|
// For numeric labels - number after that percent character '%'. |
||||||
|
// In case numeric labels were specified incorrectly, these values will |
||||||
|
// be normalized to start from `1` and increase by +1, while preserving |
||||||
|
// their order. |
||||||
|
var int numberLabel; |
||||||
|
// Before which part (from `parts`) to insert this Label? |
||||||
|
var int insertionIndex; |
||||||
|
}; |
||||||
|
|
||||||
|
var private bool initialized; |
||||||
|
// Remembers amount of numeric labels in the template |
||||||
|
var private int numericLabelsAmount; |
||||||
|
// Arrays of labels and parts in between that template got broken into |
||||||
|
// during initialization |
||||||
|
var private array<Label> labels; |
||||||
|
var private array<MutableText> parts; |
||||||
|
|
||||||
|
// Values user specified as replacements to the labels |
||||||
|
var private array<Text> numericArguments; |
||||||
|
// Fort text arguments we keep two arrays of equal size: `textLabels` records |
||||||
|
// what text Label is replaced and `textArguments` (at the same index) |
||||||
|
// records with what it needs to be replaced |
||||||
|
var private array<Text> textLabels; |
||||||
|
var private array<Text> textArguments; |
||||||
|
|
||||||
|
var private const int TPERCENT, TAMPERSAND; |
||||||
|
|
||||||
|
const CODEPOINT_PERCENT = 37; // '%' |
||||||
|
const CODEPOINT_AMPERSAND = 38; // '&' |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
// Clear user input |
||||||
|
Reset(); |
||||||
|
// Clear numeric labels |
||||||
|
numericLabelsAmount = 0; |
||||||
|
_.memory.FreeMany(parts); |
||||||
|
// Clear text Labels |
||||||
|
parts.length = 0; |
||||||
|
for (i = 0; i < Labels.length; i += 1) { |
||||||
|
_.memory.Free(Labels[i].textLabel); |
||||||
|
} |
||||||
|
labels.length = 0; |
||||||
|
// Now we can safely mark caller template as not initialized |
||||||
|
initialized = false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Checks if caller `TextTemplate` was already initialized. |
||||||
|
* |
||||||
|
* If you use recommended means of creating `TextTemplate`s (`MakeTemplate` and |
||||||
|
* `MakeTemplate_S`), then all your templates for not `none` arguments will be |
||||||
|
* initialized. |
||||||
|
* |
||||||
|
* @return `true` if it was initialized and `false` otherwise. |
||||||
|
*/ |
||||||
|
public final function bool IsInitialized() |
||||||
|
{ |
||||||
|
return initialized; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes caller `TextTemplate` with a given `input`. |
||||||
|
* |
||||||
|
* Initialization should only be performed once for every `TextTemplate` - |
||||||
|
* right after its allocation, before any use. |
||||||
|
* |
||||||
|
* If you use recommended means of creating `TextTemplate`s (`MakeTemplate`), |
||||||
|
* then all your templates for not `none` arguments will be initialized. |
||||||
|
* |
||||||
|
* @param input `string` containing template to prepare `TextTemplate` from. |
||||||
|
*/ |
||||||
|
public final function Initialize_S(string input) |
||||||
|
{ |
||||||
|
local MutableText wrapper; |
||||||
|
wrapper = _.text.FromStringM(input); |
||||||
|
Initialize(wrapper); |
||||||
|
_.memory.Free(wrapper); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Initializes caller `TextTemplate` with a given `input`. |
||||||
|
* |
||||||
|
* Initialization should only be performed once for every `TextTemplate` - |
||||||
|
* right after its allocation, before any use. |
||||||
|
* |
||||||
|
* If you use recommended means of creating `TextTemplate`s (`MakeTemplate`), |
||||||
|
* then all your templates for not `none` arguments will be initialized. |
||||||
|
* |
||||||
|
* @param input Text containing template to prepare `TextTemplate` from. |
||||||
|
*/ |
||||||
|
public final function Initialize(BaseText input) |
||||||
|
{ |
||||||
|
local Parser parser; |
||||||
|
local MutableText nextPart; |
||||||
|
local BaseText.Character percent, nextCharacter; |
||||||
|
|
||||||
|
if (input == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
percent = _.text.CharacterFromCodePoint(CODEPOINT_PERCENT); |
||||||
|
parser = input.Parse(); |
||||||
|
while (!parser.HasFinished()) |
||||||
|
{ |
||||||
|
nextPart = MatchUntilUnescapedPercent(parser); // guaranteed success |
||||||
|
parts[parts.length] = nextPart; |
||||||
|
if (parser.HasFinished()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
// If there is still input - next character is definitely "%" |
||||||
|
parser.MCharacter(nextCharacter).Confirm(); |
||||||
|
parser.MCharacter(nextCharacter); // But what is this? |
||||||
|
if (parser.Ok() && _.text.IsCodePoint(nextCharacter, CODEPOINT_PERCENT)) |
||||||
|
{ |
||||||
|
// "%%" prefix (already confirmed) - should have text Label |
||||||
|
MatchTextLabel(parser, percent); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// single "%" prefix - look for number |
||||||
|
MatchNumericLabel(parser.R()); |
||||||
|
} |
||||||
|
} |
||||||
|
initialized = true; |
||||||
|
_.memory.Free(parser); |
||||||
|
NormalizeArguments(); |
||||||
|
} |
||||||
|
|
||||||
|
private final function MatchTextLabel( |
||||||
|
Parser parser, |
||||||
|
BaseText.Character percentCharacter) |
||||||
|
{ |
||||||
|
local MutableText nextLabel; |
||||||
|
|
||||||
|
parser.MUntil(nextLabel, percentCharacter).Confirm(); |
||||||
|
labels[labels.length] = MakeTextLabel(nextLabel); |
||||||
|
// Tries to parse "%%", but will be content with a single "%" |
||||||
|
parser.Match(T(TPERCENT)).Confirm(); |
||||||
|
parser.Match(T(TPERCENT)).Confirm(); |
||||||
|
// Just reset to last properly parsed point in case of the failure |
||||||
|
parser.R(); |
||||||
|
} |
||||||
|
|
||||||
|
private final function MatchNumericLabel(Parser parser) |
||||||
|
{ |
||||||
|
local int nextNumericLabel; |
||||||
|
if (parser.MInteger(nextNumericLabel).Ok()) { |
||||||
|
labels[labels.length] = MakeNumericLabel(nextNumericLabel); |
||||||
|
} |
||||||
|
parser.Confirm(); |
||||||
|
// Just reset to last properly parsed point in case of the failure |
||||||
|
parser.R(); |
||||||
|
} |
||||||
|
|
||||||
|
// Normalize enumeration by replacing them with natural numbers sequence: |
||||||
|
// [0, 1, 2, ...] in the same order: |
||||||
|
// [2, 6, 3] -> [0, 2, 1] |
||||||
|
// [-2, 0, 4, -7] -> [1, 2, 3, 0] |
||||||
|
// [1, 1, 2, 1] -> [0, 1, 3, 2] |
||||||
|
private final function NormalizeArguments() |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local int nextArgument; |
||||||
|
local int lowestArgument, lowestArgumentIndex; |
||||||
|
local array<int> argumentsOrder, normalizedArguments; |
||||||
|
for (i = 0; i < labels.length; i += 1) |
||||||
|
{ |
||||||
|
if (labels[i].textLabel == none) |
||||||
|
{ |
||||||
|
numericLabelsAmount += 1; |
||||||
|
argumentsOrder[argumentsOrder.length] = labels[i].numberLabel; |
||||||
|
} |
||||||
|
} |
||||||
|
normalizedArguments.length = argumentsOrder.length; |
||||||
|
while (nextArgument < normalizedArguments.length) |
||||||
|
{ |
||||||
|
// Find next minimal index and record next natural number |
||||||
|
// (`nextArgument`) into it |
||||||
|
for (i = 0; i < argumentsOrder.length; i += 1) |
||||||
|
{ |
||||||
|
if (argumentsOrder[i] < lowestArgument || i == 0) |
||||||
|
{ |
||||||
|
lowestArgumentIndex = i; |
||||||
|
lowestArgument = argumentsOrder[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
argumentsOrder[lowestArgumentIndex] = MaxInt; |
||||||
|
normalizedArguments[lowestArgumentIndex] = nextArgument; |
||||||
|
nextArgument += 1; |
||||||
|
} |
||||||
|
nextArgument = 0; |
||||||
|
for (i = 0; i < labels.length; i += 1) |
||||||
|
{ |
||||||
|
if (labels[i].textLabel == none) |
||||||
|
{ |
||||||
|
labels[i].numberLabel = normalizedArguments[nextArgument]; |
||||||
|
nextArgument += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private final function Label MakeTextLabel(MutableText Label2) |
||||||
|
{ |
||||||
|
local Label result; |
||||||
|
result.textLabel = Label2; |
||||||
|
result.insertionIndex = parts.length; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
private final function Label MakeNumericLabel(int Label2) |
||||||
|
{ |
||||||
|
local Label result; |
||||||
|
result.numberLabel = Label2; |
||||||
|
result.insertionIndex = parts.length; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
// Skips until next non-escaped (that is "&%") '%' character |
||||||
|
// (or until the end). |
||||||
|
// It returns any contents inside `parser` before that character replacing |
||||||
|
// "&%" with simply "%", but leaving all other escaped sequences intact, |
||||||
|
// e.g. "&a" -> "&a" and "&{" -> "&{". This is needed to allow parsing of |
||||||
|
// the result of substitution as a formatted string. |
||||||
|
private final function MutableText MatchUntilUnescapedPercent(Parser parser) |
||||||
|
{ |
||||||
|
local MutableText result, nextMatched; |
||||||
|
local BaseText.Character ampersand, nextCharacter; |
||||||
|
local array<Text> delimiters; |
||||||
|
|
||||||
|
ampersand = _.text.CharacterFromCodePoint(CODEPOINT_AMPERSAND); |
||||||
|
delimiters[0] = T(TPERCENT); |
||||||
|
delimiters[1] = T(TAMPERSAND); |
||||||
|
result = _.text.Empty(); |
||||||
|
while (!parser.HasFinished()) |
||||||
|
{ |
||||||
|
parser.MUntilMany(nextMatched, delimiters).Confirm(); |
||||||
|
result.Append(nextMatched); |
||||||
|
_.memory.Free(nextMatched); |
||||||
|
if (parser.HasFinished()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
parser.MCharacter(nextCharacter); |
||||||
|
// If this is "%": we are done |
||||||
|
if (_.text.IsCodePoint(nextCharacter, CODEPOINT_PERCENT)) |
||||||
|
{ |
||||||
|
parser.R(); |
||||||
|
return result; |
||||||
|
} |
||||||
|
// Otherwise it is "&": skip it and the next character |
||||||
|
parser.MCharacter(nextCharacter).Confirm(); |
||||||
|
if (!parser.Ok()) |
||||||
|
{ |
||||||
|
result.AppendCharacter(ampersand); |
||||||
|
return result; |
||||||
|
} |
||||||
|
if (!_.text.IsCodePoint(nextCharacter, CODEPOINT_PERCENT)) { |
||||||
|
result.AppendCharacter(ampersand); |
||||||
|
} |
||||||
|
result.AppendCharacter(nextCharacter); |
||||||
|
} |
||||||
|
parser.Confirm(); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resets any data user entered into this template via any "Arg"-methods. |
||||||
|
* |
||||||
|
* This method allows to reuse a once initialized `TextTemplate` any amount of |
||||||
|
* times. |
||||||
|
* |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate Reset() |
||||||
|
{ |
||||||
|
if (!initialized) { |
||||||
|
return self; |
||||||
|
} |
||||||
|
_.memory.FreeMany(numericArguments); |
||||||
|
_.memory.FreeMany(textLabels); |
||||||
|
_.memory.FreeMany(textArguments); |
||||||
|
numericArguments.length = 0; |
||||||
|
textLabels.length = 0; |
||||||
|
textArguments.length = 0; |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns amount of numeric arguments inside the caller `TextTemplate`. |
||||||
|
* |
||||||
|
* @return Amount of numeric arguments inside the caller `TextTemplate`. |
||||||
|
* `-1` for uninitialized `TextTemplate`s. |
||||||
|
*/ |
||||||
|
public final function int GetNumericArgsAmount() |
||||||
|
{ |
||||||
|
if (initialized) { |
||||||
|
return numericLabelsAmount; |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns array of text arguments inside the caller `TextTemplate`. |
||||||
|
* |
||||||
|
* @return Array of text arguments inside the caller `TextTemplate`. |
||||||
|
* Empty array for uninitialized `TextTemplate`s. |
||||||
|
*/ |
||||||
|
public final function array<Text> GetTextArgs() |
||||||
|
{ |
||||||
|
local int i, j; |
||||||
|
local bool duplicate; |
||||||
|
local array<Text> result; |
||||||
|
if (!initialized) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
for (i = 0; i < labels.length; i += 1) |
||||||
|
{ |
||||||
|
if (labels[i].textLabel == none) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
duplicate = false; |
||||||
|
for (j = 0; j < result.length; j += 1) |
||||||
|
{ |
||||||
|
if (result[j].Compare(labels[i].textLabel)) |
||||||
|
{ |
||||||
|
duplicate = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (!duplicate && labels[i].textLabel != none) { |
||||||
|
result[result.length] = labels[i].textLabel.Copy(); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next numeric argument with `argument` value. |
||||||
|
* |
||||||
|
* If all numeric arguments were already filled this call will do nothing. |
||||||
|
* |
||||||
|
* @param argument Value to replace next numeric argument in the caller |
||||||
|
* `TextTemplate`. `none` values will be ignored (method will do nothing). |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate Arg_S(string argument) |
||||||
|
{ |
||||||
|
local MutableText wrapper; |
||||||
|
wrapper = _.text.FromStringM(argument); |
||||||
|
Arg(wrapper); |
||||||
|
_.memory.Free(wrapper); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next numeric argument with `argument` value. |
||||||
|
* |
||||||
|
* If all numeric arguments were already filled this call will do nothing. |
||||||
|
* |
||||||
|
* @param argument Value to replace next numeric argument in the caller |
||||||
|
* `TextTemplate`. `none` equals to an empty text. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate Arg(BaseText argument) |
||||||
|
{ |
||||||
|
if (numericLabelsAmount <= numericArguments.length) { |
||||||
|
return self; |
||||||
|
} |
||||||
|
if (argument != none) { |
||||||
|
numericArguments[numericArguments.length] = argument.Copy(); |
||||||
|
} |
||||||
|
else { |
||||||
|
numericArguments[numericArguments.length] = none; |
||||||
|
} |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next numeric argument with `argument` integer value. |
||||||
|
* |
||||||
|
* If all numeric arguments were already filled this call will do nothing. |
||||||
|
* |
||||||
|
* @param argument Value to replace next numeric argument in the caller |
||||||
|
* `TextTemplate`. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate ArgInt(int argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromIntM(argument); |
||||||
|
Arg(textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next numeric argument with `argument` floating point value. |
||||||
|
* |
||||||
|
* If all numeric arguments were already filled this call will do nothing. |
||||||
|
* |
||||||
|
* @param argument Value to replace next numeric argument in the caller |
||||||
|
* `TextTemplate`. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate ArgFloat(float argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromFloatM(argument); |
||||||
|
Arg(textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next numeric argument with `argument` boolean value. |
||||||
|
* |
||||||
|
* If all numeric arguments were already filled this call will do nothing. |
||||||
|
* |
||||||
|
* @param argument Value to replace next numeric argument in the caller |
||||||
|
* `TextTemplate`. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate ArgBool(bool argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromBoolM(argument); |
||||||
|
Arg(textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next numeric argument with `argument` class value. |
||||||
|
* |
||||||
|
* If all numeric arguments were already filled this call will do nothing. |
||||||
|
* |
||||||
|
* @param argument Value to replace next numeric argument in the caller |
||||||
|
* `TextTemplate`. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate ArgClass(class<Object> argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromClassM(argument); |
||||||
|
Arg(textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with `argument` value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArg_S(string label, string argument) |
||||||
|
{ |
||||||
|
local MutableText labelWrapper, argumentWrapper; |
||||||
|
labelWrapper = _.text.FromStringM(label); |
||||||
|
argumentWrapper = _.text.FromStringM(argument); |
||||||
|
TextArg(labelWrapper, argumentWrapper); |
||||||
|
_.memory.Free(labelWrapper); |
||||||
|
_.memory.Free(argumentWrapper); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with `argument` value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. `none` is equal to an empty text. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArg(BaseText label, BaseText argument) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (label == none) { |
||||||
|
return self; |
||||||
|
} |
||||||
|
for (i = 0; i < textLabels.length; i += 1) |
||||||
|
{ |
||||||
|
if (label.Compare(textLabels[i])) |
||||||
|
{ |
||||||
|
_.memory.Free(textArguments[i]); |
||||||
|
if (argument != none) { |
||||||
|
textArguments[i] = argument.Copy(); |
||||||
|
} |
||||||
|
else { |
||||||
|
textArguments[i] = none; |
||||||
|
} |
||||||
|
return self; |
||||||
|
} |
||||||
|
} |
||||||
|
if (label != none) { |
||||||
|
textLabels[textLabels.length] = label.Copy(); |
||||||
|
} |
||||||
|
else { |
||||||
|
textLabels[textLabels.length] = none; |
||||||
|
} |
||||||
|
if (argument != none) { |
||||||
|
textArguments[i] = argument.Copy(); |
||||||
|
} |
||||||
|
else { |
||||||
|
textArguments[i] = none; |
||||||
|
} |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with integer `argument` |
||||||
|
* value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgInt_S(string label, int argument) |
||||||
|
{ |
||||||
|
local MutableText labelWrapper; |
||||||
|
local MutableText textRepresentation; |
||||||
|
labelWrapper = _.text.FromStringM(label); |
||||||
|
textRepresentation = _.text.FromIntM(argument); |
||||||
|
TextArg(labelWrapper, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
_.memory.Free(labelWrapper); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with integer `argument` |
||||||
|
* value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgInt(BaseText label, int argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromIntM(argument); |
||||||
|
TextArg(label, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with floating point |
||||||
|
* `argument` value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgFloat_S(string label, float argument) |
||||||
|
{ |
||||||
|
local MutableText labelWrapper; |
||||||
|
local MutableText textRepresentation; |
||||||
|
labelWrapper = _.text.FromStringM(label); |
||||||
|
textRepresentation = _.text.FromFloatM(argument); |
||||||
|
TextArg(labelWrapper, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
_.memory.Free(labelWrapper); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with floating point |
||||||
|
* `argument` value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgFloat(BaseText label, float argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromFloatM(argument); |
||||||
|
TextArg(label, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with boolean `argument` |
||||||
|
* value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgBool_S(string label, bool argument) |
||||||
|
{ |
||||||
|
local MutableText labelWrapper; |
||||||
|
local MutableText textRepresentation; |
||||||
|
labelWrapper = _.text.FromStringM(label); |
||||||
|
textRepresentation = _.text.FromBoolM(argument); |
||||||
|
TextArg(labelWrapper, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
_.memory.Free(labelWrapper); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with boolean `argument` |
||||||
|
* value. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgBool(BaseText label, bool argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromBoolM(argument); |
||||||
|
TextArg(label, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with class value `argument`. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgClass_S( |
||||||
|
string label, |
||||||
|
class<Object> argument) |
||||||
|
{ |
||||||
|
local MutableText labelWrapper; |
||||||
|
local MutableText textRepresentation; |
||||||
|
labelWrapper = _.text.FromStringM(label); |
||||||
|
textRepresentation = _.text.FromClassM(argument); |
||||||
|
TextArg(labelWrapper, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
_.memory.Free(labelWrapper); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Replaces next text argument with label `label` with class value `argument`. |
||||||
|
* |
||||||
|
* If all text argument with that label was already set, then it will override |
||||||
|
* previous value. |
||||||
|
* |
||||||
|
* @param label Label of the text argument to replace. |
||||||
|
* @param argument Value to replace specified text argument in the caller |
||||||
|
* `TextTemplate` with. |
||||||
|
* @return Reference to the caller `TextTemplate` to allow for method chaining. |
||||||
|
*/ |
||||||
|
public final function TextTemplate TextArgClass( |
||||||
|
BaseText label, |
||||||
|
class<Object> argument) |
||||||
|
{ |
||||||
|
local MutableText textRepresentation; |
||||||
|
textRepresentation = _.text.FromClassM(argument); |
||||||
|
TextArg(label, textRepresentation); |
||||||
|
_.memory.Free(textRepresentation); |
||||||
|
return self; |
||||||
|
} |
||||||
|
|
||||||
|
private final function Text BorrowNumericArg(int index) |
||||||
|
{ |
||||||
|
if (index < 0) return none; |
||||||
|
if (index >= numericArguments.length) return none; |
||||||
|
|
||||||
|
return numericArguments[index]; |
||||||
|
} |
||||||
|
|
||||||
|
private final function Text BorrowTextArg(BaseText Label) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (Label == none) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
for (i = 0; i < textLabels.length; i += 1) |
||||||
|
{ |
||||||
|
if (Label.Compare(textLabels[i])) { |
||||||
|
return textArguments[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Assembles initialized `TextTemplate` from provided arguments into a final |
||||||
|
* `Text` result. |
||||||
|
* |
||||||
|
* Unspecified arguments will be replaced with empty texts. |
||||||
|
* |
||||||
|
* Returns `string`. To return mutable `Text` or `MutableText` use |
||||||
|
* `Collect()` or `CollectM()`. |
||||||
|
* |
||||||
|
* @return Result of replacing all argument inside caller `TextTemplate` with |
||||||
|
* arguments, specified by user. |
||||||
|
*/ |
||||||
|
public final function string Collect_S() |
||||||
|
{ |
||||||
|
return _.text.ToString(CollectM()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Assembles initialized `TextTemplate` from provided arguments into a final |
||||||
|
* `Text` result. |
||||||
|
* |
||||||
|
* Unspecified arguments will be replaced with empty texts. |
||||||
|
* |
||||||
|
* Returns immutable `Text`. To return mutable `MutableText` use `CollectM()`. |
||||||
|
* |
||||||
|
* @return Result of replacing all argument inside caller `TextTemplate` with |
||||||
|
* arguments, specified by user. |
||||||
|
*/ |
||||||
|
public final function Text Collect() |
||||||
|
{ |
||||||
|
local MutableText mutableResult; |
||||||
|
|
||||||
|
mutableResult = CollectM(); |
||||||
|
if (mutableResult != none) { |
||||||
|
return mutableResult.IntoText(); |
||||||
|
} |
||||||
|
return none; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Assembles initialized `TextTemplate` from provided arguments into a final |
||||||
|
* `Text` result. |
||||||
|
* |
||||||
|
* Unspecified arguments will be replaced with empty texts. |
||||||
|
* |
||||||
|
* Returns mutable `MutableText`. To return immutable `Text` use `CollectM()`. |
||||||
|
* |
||||||
|
* @return Result of replacing all argument inside caller `TextTemplate` with |
||||||
|
* arguments, specified by user. |
||||||
|
*/ |
||||||
|
public final function MutableText CollectM() |
||||||
|
{ |
||||||
|
local int i, labelCounter; |
||||||
|
local Label nextLabel; |
||||||
|
local MutableText builder; |
||||||
|
if (!initialized) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
builder = _.text.Empty(); |
||||||
|
for (i = 0; i < parts.length; i += 1) |
||||||
|
{ |
||||||
|
if ( labelCounter < labels.length |
||||||
|
&& labels[labelCounter].insertionIndex == i) |
||||||
|
{ |
||||||
|
nextLabel = labels[labelCounter]; |
||||||
|
if (nextLabel.textLabel == none) { |
||||||
|
builder.Append(BorrowNumericArg(nextLabel.numberLabel)); |
||||||
|
} |
||||||
|
else { |
||||||
|
builder.Append(BorrowTextArg(nextLabel.textLabel)); |
||||||
|
} |
||||||
|
labelCounter += 1; |
||||||
|
} |
||||||
|
builder.Append(parts[i]); |
||||||
|
} |
||||||
|
while (labelCounter < labels.length) |
||||||
|
{ |
||||||
|
nextLabel = labels[labelCounter]; |
||||||
|
if (nextLabel.textLabel == none) { |
||||||
|
builder.Append(BorrowNumericArg(nextLabel.numberLabel)); |
||||||
|
} |
||||||
|
else { |
||||||
|
builder.Append(BorrowTextArg(nextLabel.textLabel)); |
||||||
|
} |
||||||
|
labelCounter += 1; |
||||||
|
} |
||||||
|
return builder; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Assembles initialized `TextTemplate` from provided arguments and parses |
||||||
|
* resulting text as formatted string. |
||||||
|
* |
||||||
|
* Allows to do things like specify color inside formatted strings: |
||||||
|
* "{%%color_arg%% ColoredText}". |
||||||
|
* |
||||||
|
* Unspecified arguments will be replaced with empty texts. |
||||||
|
* |
||||||
|
* Returns immutable `Text`. To return mutable `MutableText` use `CollectM()`. |
||||||
|
* |
||||||
|
* @return Result of replacing all argument inside caller `TextTemplate` with |
||||||
|
* arguments, specified by user and then parsing that intermediate result |
||||||
|
* as a formatting string. |
||||||
|
*/ |
||||||
|
public final function Text CollectFormatted() |
||||||
|
{ |
||||||
|
local Text result; |
||||||
|
local MutableText source; |
||||||
|
source = CollectM(); |
||||||
|
if (source == none) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
result = _.text.FromFormatted(source); |
||||||
|
_.memory.Free(source); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Assembles initialized `TextTemplate` from provided arguments and parses |
||||||
|
* resulting text as formatted string. |
||||||
|
* |
||||||
|
* Allows to do things like specify color inside formatted strings: |
||||||
|
* "{%%color_arg%% ColoredText}". |
||||||
|
* |
||||||
|
* Unspecified arguments will be replaced with empty texts. |
||||||
|
* |
||||||
|
* Returns mutable `MutableText`. To return immutable `Text` use `CollectM()`. |
||||||
|
* |
||||||
|
* @return Result of replacing all argument inside caller `TextTemplate` with |
||||||
|
* arguments, specified by user and then parsing that intermediate result |
||||||
|
* as a formatting string. |
||||||
|
*/ |
||||||
|
public final function MutableText CollectFormattedM() |
||||||
|
{ |
||||||
|
local MutableText result, source; |
||||||
|
source = CollectM(); |
||||||
|
if (source == none) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
result = _.text.FromFormattedM(source); |
||||||
|
_.memory.Free(source); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
TPERCENT = 0 |
||||||
|
stringConstants(0) = "%" |
||||||
|
TAMPERSAND = 1 |
||||||
|
stringConstants(1) = "&&" |
||||||
|
} |
Loading…
Reference in new issue