You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
975 lines
31 KiB
975 lines
31 KiB
2 years ago
|
/**
|
||
|
* 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) = "&&"
|
||
|
}
|