diff --git a/sources/Commands/ACommandFeature.uc b/sources/Commands/ACommandFeature.uc
index 6448aa6..16ed03c 100644
--- a/sources/Commands/ACommandFeature.uc
+++ b/sources/Commands/ACommandFeature.uc
@@ -17,16 +17,26 @@
* You should have received a copy of the GNU General Public License
* along with Acedia. If not, see .
*/
-class ACommandFeature extends Command;
+class ACommandFeature extends Command
+ dependson(PendingConfigsTool);
-// TODO: autoconf, newconf, deleteconf, setconf
-// TODO: when displaying features - display which one is enabled
+var private class selectedFeatureClass;
+var private Text selectedConfigName;
-var private ACommandFeature_Announcer announcer;
+var private PendingConfigsTool pendingConfigs;
+var private ACommandFeature_Announcer announcer;
+
+protected function Constructor()
+{
+ pendingConfigs =
+ PendingConfigsTool(_.memory.Allocate(class'PendingConfigsTool'));
+ super.Constructor();
+}
protected function Finalizer()
{
_.memory.Free(announcer);
+ _.memory.Free(pendingConfigs);
super.Finalizer();
}
@@ -34,123 +44,271 @@ protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("feature")).Group(P("admin"))
.Summary(P("Managing features."))
- .Describe(P("Command for displaying and enabling/disabling features."));
+ .Describe(P("Command for managing features and their configs."));
builder.SubCommand(P("enable"))
.ParamText(P("feature"))
.OptionalParams()
.ParamText(P("config"))
- .Describe(P("Enables specified ."));
+ .Describe(P("Enables specified . If isn't specified -"
+ @ "choses the \"default\" one, making new config with default"
+ @ "settings if it doesn't exist."));
builder.SubCommand(P("disable"))
.ParamText(P("feature"))
.Describe(P("Disables specified ."));
+ builder.SubCommand(P("showconf"))
+ .ParamText(P("feature"))
+ .OptionalParams()
+ .ParamText(P("config"))
+ .Describe(P("Show given for the given ."));
+ builder.SubCommand(P("editconf"))
+ .ParamText(P("feature"))
+ .ParamText(P("config"))
+ .ParamText(P("variable_path"))
+ .ParamRemainder(P("value"))
+ .Describe(P("Changes a value inside given of the given"
+ @ " by setting value at JSON path to"
+ @ "the JSON value . Changes won't be immediately applied to"
+ @ "the game and kept as pending."));
+ builder.SubCommand(P("saveconf"))
+ .ParamText(P("feature"))
+ .ParamText(P("config"))
+ .Describe(P("Saves pending changes for the given of the given"
+ @ "."));
+ builder.SubCommand(P("newconf"))
+ .ParamText(P("feature"))
+ .ParamText(P("config"))
+ .Describe(P("Creates new config for the given ."));
+ builder.SubCommand(P("removeconf"))
+ .ParamText(P("feature"))
+ .ParamText(P("config"))
+ .Describe(P("Removes specified of the specified ."));
+ builder.SubCommand(P("autoconf"))
+ .ParamText(P("feature"))
+ .OptionalParams()
+ .ParamText(P("config"))
+ .Describe(P("Changes current auto config config of the specified"
+ @ ". Auto config is a config that is supposed to be"
+ @ "automatically enabled at the start of the Acedia, unless"
+ @ "otherwise specified for the loader."));
+ builder.Option(P("all"))
+ .Describe(F("Affects subcommand {$TextEmphasis showconf} by making it"
+ @ "show all available configs."));
+ builder.Option(P("save"))
+ .Describe(F("Affects subcommand {$TextEmphasis editconf} by making it"
+ @ "also save all pending changes."));
announcer = ACommandFeature_Announcer(
_.memory.Allocate(class'ACommandFeature_Announcer'));
}
protected function Executed(CallData arguments, EPlayer instigator)
{
+ local bool saveFlag, allFlag;
+
announcer.Setup(none, instigator, othersConsole);
+ saveFlag = arguments.options.HasKey(P("save"));
+ allFlag = arguments.options.HasKey(P("all"));
+ SelectFeatureAndConfig(arguments);
if (arguments.subCommandName.IsEmpty()) {
ShowAllFeatures();
}
- else if (arguments.subCommandName.Compare(P("enable")))
- {
- EnableFeature(
- arguments.parameters.GetText(P("feature")),
- arguments.parameters.GetText(P("config")));
+ else if (arguments.subCommandName.Compare(P("enable"))) {
+ EnableFeature();
}
else if (arguments.subCommandName.Compare(P("disable"))) {
- DisableFeature(arguments.parameters.GetText(P("feature")));
+ DisableFeature();
+ }
+ else if (arguments.subCommandName.Compare(P("showconf"))) {
+ ShowSelectedConfigs(allFlag);
+ }
+ else if (arguments.subCommandName.Compare(P("editconf")))
+ {
+ EditFeatureConfig(
+ arguments.parameters.GetText(P("variable_path")),
+ arguments.parameters.GetText(P("value")),
+ saveFlag);
+ }
+ else if (arguments.subCommandName.Compare(P("saveconf"))) {
+ SaveFeatureConfig();
+ }
+ else if (arguments.subCommandName.Compare(P("newconf"))) {
+ NewFeatureConfig();
+ }
+ else if (arguments.subCommandName.Compare(P("removeconf"))) {
+ RemoveFeatureConfig();
}
+ else if (arguments.subCommandName.Compare(P("autoconf"))) {
+ SetAutoFeatureConfig();
+ }
+ _.memory.Free(selectedConfigName);
+ selectedConfigName = none;
}
-protected function EnableFeature(BaseText featureName, BaseText configParameter)
+protected function SelectFeatureAndConfig(CallData arguments)
{
- local bool wasEnabled;
- local Text oldConfig, newConfig;
- local Feature instance;
- local class featureClass;
+ local Text userGivenFeatureName, userGivenConfigName;
- featureClass = LoadFeatureClass(featureName);
- if (featureClass == none) {
+ userGivenFeatureName = arguments.parameters.GetText(P("feature"));
+ selectedFeatureClass = LoadFeatureClass(userGivenFeatureName);
+ if (selectedFeatureClass == none && !arguments.subCommandName.IsEmpty()) {
return;
}
- wasEnabled = featureClass.static.IsEnabled();
- oldConfig = featureClass.static.GetCurrentConfig();
- newConfig = GetConfigFromParameter(configParameter, featureClass);
+ _.memory.Free(userGivenFeatureName);
+ userGivenConfigName = arguments.parameters.GetText(P("config"));
+ if (userGivenConfigName != none)
+ {
+ selectedConfigName = userGivenConfigName.LowerCopy();
+ userGivenConfigName.FreeSelf();
+ }
+ pendingConfigs.SelectConfig(selectedFeatureClass, selectedConfigName);
+}
+
+protected function EnableFeature()
+{
+ local bool wasEnabled;
+ local Text oldConfig, newConfig;
+ local Feature instance;
+
+ wasEnabled = selectedFeatureClass.static.IsEnabled();
+ oldConfig = selectedFeatureClass.static.GetCurrentConfig();
+ newConfig = PickConfigBasedOnParameter();
// Already enabled with the same config!
if (oldConfig != none && oldConfig.Compare(newConfig, SCASE_INSENSITIVE))
{
- announcer.AnnounceFailedAlreadyEnabled(featureClass, newConfig);
+ announcer.AnnounceFailedAlreadyEnabled(selectedFeatureClass, newConfig);
_.memory.Free(newConfig);
_.memory.Free(oldConfig);
return;
}
// Try enabling and report the result
- instance = featureClass.static.EnableMe(newConfig);
- if (instance == none) {
- announcer.AnnounceFailedCannotEnableFeature(featureClass, newConfig);
+ instance = selectedFeatureClass.static.EnableMe(newConfig);
+ if (instance == none)
+ {
+ announcer.AnnounceFailedCannotEnableFeature(
+ selectedFeatureClass,
+ newConfig);
}
- else if (wasEnabled) {
- announcer.AnnounceSwappedConfig(featureClass, oldConfig, newConfig);
+ else if (wasEnabled)
+ {
+ announcer.AnnounceSwappedConfig(
+ selectedFeatureClass,
+ oldConfig,
+ newConfig);
}
else {
- announcer.AnnounceEnabledFeature(featureClass, newConfig);
+ announcer.AnnounceEnabledFeature(selectedFeatureClass, newConfig);
}
_.memory.Free(newConfig);
_.memory.Free(oldConfig);
}
-protected function DisableFeature(Text featureName)
+protected function DisableFeature()
{
- local class featureClass;
-
- featureClass = LoadFeatureClass(featureName);
- if (featureClass == none) {
- return;
- }
- if (!featureClass.static.IsEnabled())
+ if (!selectedFeatureClass.static.IsEnabled())
{
- announcer.AnnounceFailedAlreadyDisabled(featureClass);
+ announcer.AnnounceFailedAlreadyDisabled(selectedFeatureClass);
return;
}
- featureClass.static.DisableMe();
+ selectedFeatureClass.static.DisableMe();
// It is possible that this command itself is destroyed after above command
// so do the check just in case
if (IsAllocated()) {
- announcer.AnnounceDisabledFeature(featureClass);
+ announcer.AnnounceDisabledFeature(selectedFeatureClass);
}
}
-protected function Text GetConfigFromParameter(
- BaseText configParameter,
- class featureClass)
+protected function ShowSelectedConfigs(bool showAllFeatures)
{
- local Text resolvedConfig;
+ local int i;
+ local array availableConfigs;
+ local MutableText configList;
local class configClass;
- if (featureClass == none) {
- return none;
+ if (showAllFeatures)
+ {
+ configClass = selectedFeatureClass.default.configClass;
+ if (configClass != none) {
+ availableConfigs = configClass.static.AvailableConfigs();
+ }
+ for (i = 0; i < availableConfigs.length; i += 1) {
+ ShowFeatureConfig(availableConfigs[i]);
+ }
+ _.memory.FreeMany(availableConfigs);
+ return;
}
- configClass = featureClass.default.configClass;
+ if (selectedConfigName != none)
+ {
+ ShowFeatureConfig(selectedConfigName);
+ return;
+ }
+ configList = PrintConfigList(selectedFeatureClass);
+ callerConsole
+ .Flush()
+ .Write(P("Available configs: "))
+ .WriteLine(configList);
+ _.memory.Free(configList);
+}
+
+protected function ShowFeatureConfig(BaseText configName)
+{
+ local MutableText dataAsJSON;
+ local HashTable currentData, pendingData;
+
+ if (configName == none) {
+ return;
+ }
+ currentData = GetCurrentConfigData(configName);
+ if (currentData == none)
+ {
+ announcer.AnnounceFailedNoDataForConfig(
+ selectedFeatureClass,
+ configName);
+ return;
+ }
+ // Display current data
+ dataAsJSON = _.json.PrettyPrint(currentData);
+ announcer.AnnounceCurrentConfig(selectedFeatureClass, configName);
+ callerConsole.Flush().WriteLine(dataAsJSON);
+ _.memory.Free(dataAsJSON);
+ // Display pending data
+ pendingConfigs.SelectConfig(selectedFeatureClass, configName);
+ pendingData = pendingConfigs.GetPendingConfigData();
+ if (pendingData != none)
+ {
+ dataAsJSON = _.json.PrettyPrint(pendingData);
+ announcer.AnnouncePendingConfig(
+ selectedFeatureClass,
+ configName);
+ callerConsole.Flush().WriteLine(dataAsJSON);
+ _.memory.Free(dataAsJSON);
+ }
+ _.memory.Free(pendingData);
+ _.memory.Free(currentData);
+}
+
+protected function Text PickConfigBasedOnParameter()
+{
+ local Text resolvedConfig;
+ local class configClass;
+
+ configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
- announcer.AnnounceFailedNoConfigClass(featureClass);
+ announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return none;
}
// If config was specified - simply check that it exists
- if (configParameter != none)
+ if (selectedConfigName != none)
{
- if (configClass.static.Exists(configParameter)) {
- return configParameter.Copy();
+ if (configClass.static.Exists(selectedConfigName)) {
+ return selectedConfigName.Copy();
}
- announcer.AnnounceFailedConfigMissing(configParameter);
+ announcer.AnnounceFailedConfigMissing(selectedConfigName);
return none;
}
// If it wasn't specified - try auto config instead
resolvedConfig = configClass.static.GetAutoEnabledConfig();
if (resolvedConfig == none) {
- announcer.AnnounceFailedNoConfigProvided(featureClass);
+ announcer.AnnounceFailedNoConfigProvided(selectedFeatureClass);
}
return resolvedConfig;
}
@@ -186,59 +344,299 @@ protected function ShowAllFeatures()
}
}
-protected function ShowFeature(class feature)
+protected function ShowFeature(class featureClass)
{
- local int i;
- local Text autoConfig;
- local MutableText featureName, builder;
- local ListBuilder configList;
- local array availableConfigs;
- local class configClass;
+ local MutableText featureName;
+ local MutableText configList;
- if (feature == none) {
+ if (featureClass == none) {
return;
}
- configClass = feature.default.configClass;
- if (configClass != none) {
- availableConfigs = configClass.static.AvailableConfigs();
- }
featureName = _.text
- .FromClassM(feature)
+ .FromClassM(featureClass)
.ChangeDefaultColor(_.color.TextEmphasis);
- builder = _.text.Empty();
- if (feature.static.IsEnabled()) {
- builder.Append(F("[ {$TextPositive enabled} ] "));
+ configList = PrintConfigList(featureClass);
+ callerConsole.Flush();
+ if (featureClass.static.IsEnabled()) {
+ callerConsole.Write(F("[ {$TextPositive enabled} ] "));
}
else {
- builder.Append(F("[ {$TextNegative disabled} ] "));
+ callerConsole.Write(F("[ {$TextNegative disabled} ] "));
}
- builder.Append(featureName);
+ callerConsole.Write(featureName)
+ .Write(P(" with configs: "))
+ .WriteLine(configList);
_.memory.Free(featureName);
- if (availableConfigs.length == 1) {
- builder.Append(P(" with config:"));
- }
- else if (availableConfigs.length > 1) {
- builder.Append(P(" with configs:"));
- }
- callerConsole.Write(builder);
- _.memory.Free(builder);
- configList = ListBuilder(_.memory.Allocate(class'ListBuilder'));
- autoConfig = configClass.static.GetAutoEnabledConfig();
+ _.memory.Free(configList);
+}
+
+protected function MutableText PrintConfigList(class featureClass)
+{
+ local int i;
+ local Text autoConfig;
+ local ListBuilder configList;
+ local MutableText result, nextConfig;
+ local array availableConfigs;
+ local class configClass;
+
+ if (featureClass == none) return none;
+ configClass = featureClass.default.configClass;
+ if (configClass == none) return none;
+
+ availableConfigs = configClass.static.AvailableConfigs();
+ autoConfig = configClass.static.GetAutoEnabledConfig();
+ configList = ListBuilder(_.memory.Allocate(class'ListBuilder'));
for (i = 0; i < availableConfigs.length; i += 1)
{
- configList.Item(availableConfigs[i]);
+ nextConfig = availableConfigs[i].MutableCopy();
+ if (pendingConfigs.HasPendingConfigFor(featureClass, nextConfig)) {
+ nextConfig.Append(F("{$TextEmphasis *}"));
+ }
+ configList.Item(nextConfig);
+ _.memory.Free(nextConfig);
if ( autoConfig != none
&& autoConfig.Compare(availableConfigs[i], SCASE_INSENSITIVE))
{
configList.Comment(F("{$TextPositive auto enabled}"));
}
}
- builder = configList.GetMutable();
- callerConsole.WriteLine(builder);
- _.memory.FreeMany(availableConfigs);
+ result = configList.GetMutable();
_.memory.Free(configList);
_.memory.Free(autoConfig);
- _.memory.Free(builder);
+ _.memory.FreeMany(availableConfigs);
+ return result;
+}
+
+protected function MutableText PrettyPrintValueAt(BaseText pathToValue)
+{
+ local MutableText printedValue;
+ local AcediaObject value;
+ local HashTable relevantData;
+
+ relevantData = pendingConfigs.GetPendingConfigData();
+ if (relevantData == none) {
+ relevantData = GetCurrentSelectedConfigData();
+ }
+ if (relevantData != none) {
+ value = relevantData.GetItemBy(pathToValue);
+ }
+ if (value != none)
+ {
+ printedValue = _.json.PrettyPrint(value);
+ _.memory.Free(value);
+ }
+ _.memory.Free(relevantData);
+ return printedValue;
+}
+
+protected function EditFeatureConfig(
+ BaseText pathToValue,
+ BaseText newValue,
+ bool saveConfig)
+{
+ local MutableText printedOldValue;
+ local MutableText printedNewValue;
+ local PendingConfigsTool.PendingConfigToolResult error;
+
+ printedOldValue = PrettyPrintValueAt(pathToValue);
+ error = pendingConfigs.EditConfig(pathToValue, newValue);
+ if (error == PCTE_None) {
+ printedNewValue = PrettyPrintValueAt(pathToValue);
+ }
+ if (error == PCTE_ConfigMissing) {
+ announcer.AnnounceFailedConfigMissing(selectedConfigName);
+ }
+ else if (error == PCTE_ExpectedObject) {
+ announcer.AnnounceFailedExpectedObject();
+ }
+ else if (error == PCTE_BadPointer)
+ {
+ announcer.AnnounceFailedBadPointer(
+ selectedFeatureClass,
+ selectedConfigName,
+ pathToValue);
+ }
+ else if (printedOldValue == none)
+ {
+ announcer.AnnounceConfigNewValue(
+ selectedFeatureClass,
+ selectedConfigName,
+ pathToValue,
+ printedNewValue);
+ }
+ else
+ {
+ announcer.AnnounceConfigEdited(
+ selectedFeatureClass,
+ selectedConfigName,
+ pathToValue,
+ printedOldValue,
+ printedNewValue);
+ }
+ if (saveConfig && error == PCTE_None) {
+ SaveFeatureConfig();
+ }
+ _.memory.Free(printedOldValue);
+ _.memory.Free(printedNewValue);
+}
+
+protected function SaveFeatureConfig()
+{
+ local BaseText enabledConfigName;
+ local HashTable pendingData;
+ local class configClass;
+
+ configClass = selectedFeatureClass.default.configClass;
+ if (configClass == none)
+ {
+ announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
+ return;
+ }
+ pendingData = pendingConfigs.GetPendingConfigData();
+ if (pendingData == none)
+ {
+ announcer.AnnounceFailedPendingConfigMissing(selectedConfigName);
+ return;
+ }
+ // Make sure config exists
+ configClass.static.NewConfig(selectedConfigName);
+ configClass.static.SaveData(selectedConfigName, pendingData);
+ // Re-apply config if it is active?
+ enabledConfigName = selectedFeatureClass.static.GetCurrentConfig();
+ if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE))
+ {
+ selectedFeatureClass.static.EnableMe(selectedConfigName);
+ announcer.AnnouncePublicPendingConfigSaved(selectedFeatureClass);
+ }
+ else {
+ announcer.AnnouncePrivatePendingConfigSaved(selectedFeatureClass);
+ }
+ _.memory.Free(enabledConfigName);
+ pendingData.FreeSelf();
+ pendingConfigs.RemoveConfig();
+ return;
+}
+
+protected function NewFeatureConfig()
+{
+ local BaseText enabledConfigName;
+ local class configClass;
+
+ configClass = selectedFeatureClass.default.configClass;
+ if (configClass == none)
+ {
+ announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
+ return;
+ }
+ if (configClass.static.Exists(selectedConfigName))
+ {
+ announcer.AnnounceFailedConfigAlreadyExists(
+ selectedFeatureClass,
+ selectedConfigName);
+ return;
+ }
+ if (!configClass.static.NewConfig(selectedConfigName))
+ {
+ announcer.AnnounceFailedBadConfigName(selectedConfigName);
+ return;
+ }
+ enabledConfigName = selectedFeatureClass.static.GetCurrentConfig();
+ if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE))
+ {
+ selectedFeatureClass.static.EnableMe(selectedConfigName);
+ announcer.AnnouncePublicPendingConfigSaved(selectedFeatureClass);
+ }
+ _.memory.Free(enabledConfigName);
+ announcer.AnnounceConfigCreated(selectedFeatureClass, selectedConfigName);
+}
+
+protected function RemoveFeatureConfig()
+{
+ local class configClass;
+
+ configClass = selectedFeatureClass.default.configClass;
+ if (configClass == none)
+ {
+ announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
+ return;
+ }
+ if (!configClass.static.Exists(selectedConfigName))
+ {
+ announcer.AnnounceFailedConfigDoesNotExist(
+ selectedFeatureClass,
+ selectedConfigName);
+ return;
+ }
+ pendingConfigs.RemoveConfig();
+ configClass.static.DeleteConfig(selectedConfigName);
+ announcer.AnnounceConfigRemoved(selectedFeatureClass, selectedConfigName);
+}
+
+protected function SetAutoFeatureConfig()
+{
+ local Text currentAutoEnabledConfig;
+ local class configClass;
+
+ configClass = selectedFeatureClass.default.configClass;
+ if (configClass == none)
+ {
+ announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
+ return;
+ }
+ if ( selectedConfigName != none
+ && !configClass.static.Exists(selectedConfigName))
+ {
+ announcer.AnnounceFailedConfigDoesNotExist(
+ selectedFeatureClass,
+ selectedConfigName);
+ return;
+ }
+ currentAutoEnabledConfig = configClass.static.GetAutoEnabledConfig();
+ if (selectedConfigName == none && currentAutoEnabledConfig == none) {
+ announcer.AnnounceFailedAlreadyNoAutoEnabled(selectedFeatureClass);
+ }
+ else if (selectedConfigName != none &&
+ selectedConfigName.Compare(currentAutoEnabledConfig, SCASE_INSENSITIVE))
+ {
+ announcer.AnnounceFailedAlreadySameAutoEnabled(
+ selectedFeatureClass,
+ selectedConfigName);
+ }
+ else
+ {
+ configClass.static.SetAutoEnabledConfig(selectedConfigName);
+ if (selectedConfigName != none)
+ {
+ announcer.AnnounceAutoEnabledConfig(
+ selectedFeatureClass,
+ selectedConfigName);
+ }
+ else {
+ announcer.AnnounceRemovedAutoEnabledConfig(selectedFeatureClass);
+ }
+ }
+ _.memory.Free(currentAutoEnabledConfig);
+}
+
+private function HashTable GetCurrentConfigData(BaseText configName)
+{
+ local class configClass;
+
+ if (configName == none) {
+ return none;
+ }
+ configClass = selectedFeatureClass.default.configClass;
+ if (configClass == none)
+ {
+ announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
+ return none;
+ }
+ return configClass.static.LoadData(configName);
+}
+
+private function HashTable GetCurrentSelectedConfigData()
+{
+ return GetCurrentConfigData(selectedConfigName);
}
defaultproperties
diff --git a/sources/Commands/ACommandFeature_Announcer.uc b/sources/Commands/ACommandFeature_Announcer.uc
index e220c8a..599b5e7 100644
--- a/sources/Commands/ACommandFeature_Announcer.uc
+++ b/sources/Commands/ACommandFeature_Announcer.uc
@@ -20,25 +20,57 @@
class ACommandFeature_Announcer extends CommandAnnouncer;
var private AnnouncementVariations enabledFeature, disabledFeature;
-var private AnnouncementVariations swappedConfig;
+var private AnnouncementVariations swappedConfig, pendingConfigSaved;
+var private AnnouncementVariations showCurrentConfig, showPendingConfig;
+var private AnnouncementVariations configCreated, configRemoved, configEdited;
+var private AnnouncementVariations configEditedNew;
+var private AnnouncementVariations pendingConfigSavedPublic;
+var private AnnouncementVariations pendingConfigSavedPrivate;
+var private AnnouncementVariations autoEnabled, removedAutoEnabled;
+var private AnnouncementVariations failedAlreadyNoAutoEnabled;
+var private AnnouncementVariations failedAlreadySameAutoEnabled;
+var private AnnouncementVariations failedConfigAlreadyExists;
+var private AnnouncementVariations failedConfigDoesNotExists;
var private AnnouncementVariations failedToLoadFeatureClass;
var private AnnouncementVariations failedNoConfigProvided, failedConfigMissing;
var private AnnouncementVariations failedCannotEnableFeature;
-var private AnnouncementVariations failedNoConfigClass;
+var private AnnouncementVariations failedNoConfigClass, failedBadConfigName;
var private AnnouncementVariations failedAlreadyEnabled, failedAlreadyDisabled;
+var private AnnouncementVariations failedNoDataForConfig, failedExpectedObject;
+var private AnnouncementVariations failedBadPointer, failedPendingConfigMissing;
protected function Finalizer()
{
FreeVariations(enabledFeature);
FreeVariations(disabledFeature);
FreeVariations(swappedConfig);
+ FreeVariations(pendingConfigSaved);
+ FreeVariations(showCurrentConfig);
+ FreeVariations(showPendingConfig);
+ FreeVariations(configCreated);
+ FreeVariations(configRemoved);
+ FreeVariations(configEdited);
+ FreeVariations(configEditedNew);
+ FreeVariations(pendingConfigSavedPublic);
+ FreeVariations(pendingConfigSavedPrivate);
+ FreeVariations(autoEnabled);
+ FreeVariations(removedAutoEnabled);
+ FreeVariations(failedAlreadyNoAutoEnabled);
+ FreeVariations(failedAlreadySameAutoEnabled);
+ FreeVariations(failedConfigAlreadyExists);
+ FreeVariations(failedConfigDoesNotExists);
FreeVariations(failedToLoadFeatureClass);
FreeVariations(failedNoConfigProvided);
FreeVariations(failedConfigMissing);
FreeVariations(failedCannotEnableFeature);
FreeVariations(failedNoConfigClass);
+ FreeVariations(failedBadConfigName);
FreeVariations(failedAlreadyEnabled);
FreeVariations(failedAlreadyDisabled);
+ FreeVariations(failedNoDataForConfig);
+ FreeVariations(failedExpectedObject);
+ FreeVariations(failedBadPointer);
+ FreeVariations(failedPendingConfigMissing);
super.Finalizer();
}
@@ -117,6 +149,268 @@ public final function AnnounceSwappedConfig(
MakeAnnouncement(swappedConfig);
}
+public final function AnnouncePublicPendingConfigSaved(
+ class featureClass)
+{
+ local int i;
+ local array templates;
+
+ if (!pendingConfigSavedPublic.initialized)
+ {
+ pendingConfigSavedPublic.initialized = true;
+ pendingConfigSavedPublic.toSelfReport = _.text.MakeTemplate_S(
+ "Active config for feature {$TextEmphasis `%1`} was"
+ @ "{$TextNeutral modified}");
+ pendingConfigSavedPublic.toSelfPublic = _.text.MakeTemplate_S(
+ "%%instigator%% {$TextNeutral modified} active config for feature"
+ @ "{$TextEmphasis `%1`}");
+ }
+ templates = MakeArray(pendingConfigSavedPublic);
+ for (i = 0; i < templates.length; i += 1) {
+ templates[i].Reset().ArgClass(featureClass);
+ }
+ MakeAnnouncement(pendingConfigSavedPublic);
+}
+
+public final function AnnouncePrivatePendingConfigSaved(
+ class featureClass)
+{
+ if (!pendingConfigSavedPrivate.initialized)
+ {
+ pendingConfigSavedPrivate.initialized = true;
+ pendingConfigSavedPrivate.toSelfReport = _.text.MakeTemplate_S(
+ "Active config for feature {$TextEmphasis `%1`} was"
+ @ "{$TextNeutral modified}");
+ }
+ pendingConfigSavedPrivate.toSelfReport
+ .Reset()
+ .ArgClass(featureClass);
+ MakeAnnouncement(pendingConfigSavedPrivate);
+}
+
+public final function AnnounceCurrentConfig(
+ class featureClass,
+ BaseText config)
+{
+ if (!showCurrentConfig.initialized)
+ {
+ showCurrentConfig.initialized = true;
+ showCurrentConfig.toSelfReport = _.text.MakeTemplate_S(
+ "Current config \"%2\" for feature {$TextEmphasis `%1`}:");
+ }
+ showCurrentConfig.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(showCurrentConfig);
+}
+
+public final function AnnouncePendingConfig(
+ class featureClass,
+ BaseText config)
+{
+ if (!showPendingConfig.initialized)
+ {
+ showPendingConfig.initialized = true;
+ showPendingConfig.toSelfReport = _.text.MakeTemplate_S(
+ "Pending config \"%2\" for feature {$TextEmphasis `%1`}:");
+ }
+ showPendingConfig.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(showPendingConfig);
+}
+
+public final function AnnounceConfigCreated(
+ class featureClass,
+ BaseText config)
+{
+ if (!configCreated.initialized)
+ {
+ configCreated.initialized = true;
+ configCreated.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextPositive Created config} \"%2\" for feature"
+ @ "{$TextEmphasis `%1`}");
+ }
+ configCreated.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(configCreated);
+}
+
+public final function AnnounceConfigRemoved(
+ class featureClass,
+ BaseText config)
+{
+ if (!configRemoved.initialized)
+ {
+ configRemoved.initialized = true;
+ configRemoved.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextNegative Removed config} \"%2\" for feature"
+ @ "{$TextEmphasis `%1`}");
+ }
+ configRemoved.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(configRemoved);
+}
+
+public final function AnnounceConfigEdited(
+ class featureClass,
+ BaseText config,
+ BaseText pathToValue,
+ BaseText oldValue,
+ BaseText newValue)
+{
+ if (!configEdited.initialized)
+ {
+ configEdited.initialized = true;
+ configEdited.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextNeutral Edited config} \"%2\" for feature"
+ @ "{$TextEmphasis `%1`} by replacing old value %4 at \"%3\""
+ @ "with new value %5");
+ }
+ configEdited.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config)
+ .Arg(pathToValue)
+ .Arg(oldValue)
+ .Arg(newValue);
+ MakeAnnouncement(configEdited);
+}
+
+public final function AnnounceConfigNewValue(
+ class featureClass,
+ BaseText config,
+ BaseText pathToValue,
+ BaseText newValue)
+{
+ if (!configEditedNew.initialized)
+ {
+ configEditedNew.initialized = true;
+ configEditedNew.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextNeutral Edited config} \"%2\" for feature"
+ @ "{$TextEmphasis `%1`} by adding value %4 at \"%3\"");
+ }
+ configEditedNew.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config)
+ .Arg(pathToValue)
+ .Arg(newValue);
+ MakeAnnouncement(configEditedNew);
+}
+
+public final function AnnounceAutoEnabledConfig(
+ class featureClass,
+ BaseText config)
+{
+ if (!autoEnabled.initialized)
+ {
+ autoEnabled.initialized = true;
+ autoEnabled.toSelfReport = _.text.MakeTemplate_S(
+ "Config \"%2\" for feature {$TextEmphasis `%1`} will now be"
+ @ "{$TextPositive auto-enabled}!");
+ }
+ autoEnabled.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(autoEnabled);
+}
+
+public final function AnnounceRemovedAutoEnabledConfig(
+ class featureClass)
+{
+ if (!removedAutoEnabled.initialized)
+ {
+ removedAutoEnabled.initialized = true;
+ removedAutoEnabled.toSelfReport = _.text.MakeTemplate_S(
+ "No config for feature {$TextEmphasis `%1`} will now be"
+ @ "{$TextPositive auto-enabled}!");
+ }
+ removedAutoEnabled.toSelfReport
+ .Reset()
+ .ArgClass(featureClass);
+ MakeAnnouncement(removedAutoEnabled);
+}
+
+public final function AnnounceFailedAlreadyNoAutoEnabled(
+ class featureClass)
+{
+ if (!failedAlreadyNoAutoEnabled.initialized)
+ {
+ failedAlreadyNoAutoEnabled.initialized = true;
+ failedAlreadyNoAutoEnabled.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextFailure Cannot remove} auto-enabled config status for"
+ @ "feature {$TextEmphasis `%1`}: it already has"
+ @ "{$TextNeutral no auto-enabled config}!");
+ }
+ failedAlreadyNoAutoEnabled.toSelfReport
+ .Reset()
+ .ArgClass(featureClass);
+ MakeAnnouncement(failedAlreadyNoAutoEnabled);
+}
+
+public final function AnnounceFailedAlreadySameAutoEnabled(
+ class featureClass,
+ BaseText config)
+{
+ if (!failedAlreadySameAutoEnabled.initialized)
+ {
+ failedAlreadySameAutoEnabled.initialized = true;
+ failedAlreadySameAutoEnabled.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextFailure Cannot make} config \"%2\" auto-enabled for feature"
+ @ "{$TextEmphasis `%1`}: it already"
+ @ "{$TextNeutral is auto-enabled}!");
+ }
+ failedAlreadySameAutoEnabled.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(failedAlreadySameAutoEnabled);
+}
+
+public final function AnnounceFailedConfigAlreadyExists(
+ class featureClass,
+ BaseText config)
+{
+ if (!failedConfigAlreadyExists.initialized)
+ {
+ failedConfigAlreadyExists.initialized = true;
+ failedConfigAlreadyExists.toSelfReport = _.text.MakeTemplate_S(
+ "Config \"%2\" for feature {$TextEmphasis `%1`}"
+ @ "{$TextFailure already exists}");
+ }
+ failedConfigAlreadyExists.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(failedConfigAlreadyExists);
+}
+
+ public final function AnnounceFailedConfigDoesNotExist(
+ class featureClass,
+ BaseText config)
+{
+ if (!failedConfigDoesNotExists.initialized)
+ {
+ failedConfigDoesNotExists.initialized = true;
+ failedConfigDoesNotExists.toSelfReport = _.text.MakeTemplate_S(
+ "Config \"%2\" for feature {$TextEmphasis `%1`}"
+ @ "{$TextFailure doesn't exist}");
+ }
+ failedConfigDoesNotExists.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(failedConfigDoesNotExists);
+}
+
public final function AnnounceFailedToLoadFeatureClass(BaseText failedClassName)
{
if (!failedToLoadFeatureClass.initialized)
@@ -136,7 +430,7 @@ public final function AnnounceFailedNoConfigProvided(
{
failedNoConfigProvided.initialized = true;
failedNoConfigProvided.toSelfReport = _.text.MakeTemplate_S(
- "{$TextFailue No config specified} and {$TextFailure no"
+ "{$TextFailure No config specified} and {$TextFailure no"
@ "auto-enabled config} exists for feature {$TextEmphasis `%1`}");
}
failedNoConfigProvided.toSelfReport.Reset().ArgClass(featureClass);
@@ -149,12 +443,25 @@ public final function AnnounceFailedConfigMissing(BaseText config)
{
failedConfigMissing.initialized = true;
failedConfigMissing.toSelfReport = _.text.MakeTemplate_S(
- "Specified config \"%1\" {$TextFailue doesn't exist}");
+ "Specified config \"%1\" {$TextFailure doesn't exist}");
}
failedConfigMissing.toSelfReport.Reset().Arg(config);
MakeAnnouncement(failedConfigMissing);
}
+public final function AnnounceFailedPendingConfigMissing(BaseText config)
+{
+ if (!failedPendingConfigMissing.initialized)
+ {
+ failedPendingConfigMissing.initialized = true;
+ failedPendingConfigMissing.toSelfReport = _.text.MakeTemplate_S(
+ "Specified config \"%1\" {$TextFailure doesn't have} any pending"
+ @ "changes");
+ }
+ failedPendingConfigMissing.toSelfReport.Reset().Arg(config);
+ MakeAnnouncement(failedPendingConfigMissing);
+}
+
public final function AnnounceFailedCannotEnableFeature(
class featureClass,
BaseText config)
@@ -180,14 +487,26 @@ public final function AnnounceFailedNoConfigClass(
{
failedNoConfigClass.initialized = true;
failedNoConfigClass.toSelfReport = _.text.MakeTemplate_S(
- "Feature {$TextEmphasis `%1`} {$TextFailure does not have config"
- @ "class}! This is most likely caused by its faulty"
+ "Feature {$TextEmphasis `%1`} {$TextFailure does not have} config"
+ @ "class! This is most likely caused by its faulty"
@ "implementation");
}
failedNoConfigClass.toSelfReport.Reset().ArgClass(featureClass);
MakeAnnouncement(failedNoConfigClass);
}
+public final function AnnounceFailedBadConfigName(BaseText configName)
+{
+ if (!failedBadConfigName.initialized)
+ {
+ failedBadConfigName.initialized = true;
+ failedBadConfigName.toSelfReport = _.text.MakeTemplate_S(
+ "{$TextFailure Cannot create} a config with invalid name \"%1\"");
+ }
+ failedBadConfigName.toSelfReport.Reset().Arg(configName);
+ MakeAnnouncement(failedBadConfigName);
+}
+
public final function AnnounceFailedAlreadyDisabled(
class featureClass)
{
@@ -195,7 +514,8 @@ public final function AnnounceFailedAlreadyDisabled(
{
failedAlreadyDisabled.initialized = true;
failedAlreadyDisabled.toSelfReport = _.text.MakeTemplate_S(
- "Feature {$TextEmphasis `%1`} is already {$TextNegative disabled}");
+ "{$TextFailure Cannot disable} feature {$TextEmphasis `%1`}: it is"
+ @ "already {$TextNegative disabled}");
}
failedAlreadyDisabled.toSelfReport.Reset().ArgClass(featureClass);
MakeAnnouncement(failedAlreadyDisabled);
@@ -209,8 +529,8 @@ public final function AnnounceFailedAlreadyEnabled(
{
failedAlreadyEnabled.initialized = true;
failedAlreadyEnabled.toSelfReport = _.text.MakeTemplate_S(
- "Feature {$TextEmphasis `%1`} is already {$TextNegative enabled}"
- @ "with specified config \"%2\"");
+ "{$TextFailure Cannot enable} feature {$TextEmphasis `%1`}: it is"
+ @ "already {$TextPositive enabled} with specified config \"%2\"");
}
failedAlreadyEnabled.toSelfReport
.Reset()
@@ -219,6 +539,56 @@ public final function AnnounceFailedAlreadyEnabled(
MakeAnnouncement(failedAlreadyEnabled);
}
+public final function AnnounceFailedNoDataForConfig(
+ class featureClass,
+ BaseText config)
+{
+ if (!failedNoDataForConfig.initialized)
+ {
+ failedNoDataForConfig.initialized = true;
+ failedNoDataForConfig.toSelfReport = _.text.MakeTemplate_S(
+ "Feature {$TextEmphasis `%1`} is {$TextFailure missing data} for"
+ @ "config \"%2\"");
+ }
+ failedNoDataForConfig.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config);
+ MakeAnnouncement(failedNoDataForConfig);
+}
+
+public final function AnnounceFailedExpectedObject()
+{
+ if (!failedExpectedObject.initialized)
+ {
+ failedExpectedObject.initialized = true;
+ failedExpectedObject.toSelfReport = _.text.MakeTemplate_S(
+ "Value change {$TextFailure failed}, because when changing"
+ @ "the value of the whole config, a JSON object must be provided");
+ }
+ MakeAnnouncement(failedExpectedObject);
+}
+
+public final function AnnounceFailedBadPointer(
+ class featureClass,
+ BaseText config,
+ BaseText pointer)
+{
+ if (!failedBadPointer.initialized)
+ {
+ failedBadPointer.initialized = true;
+ failedBadPointer.toSelfReport = _.text.MakeTemplate_S(
+ "Provided JSON pointer \"%3\" is {$TextFailure invalid} for config"
+ @ "\"%2\" of feature {$TextEmphasis `%1`}");
+ }
+ failedBadPointer.toSelfReport
+ .Reset()
+ .ArgClass(featureClass)
+ .Arg(config)
+ .Arg(pointer);
+ MakeAnnouncement(failedBadPointer);
+}
+
defaultproperties
{
}
\ No newline at end of file
diff --git a/sources/Tools/PendingConfigsTool.uc b/sources/Tools/PendingConfigsTool.uc
new file mode 100644
index 0000000..b938747
--- /dev/null
+++ b/sources/Tools/PendingConfigsTool.uc
@@ -0,0 +1,353 @@
+/**
+ * Auxiliary object for `ACommandFeature` to help with managing pending
+ * configs for `Feature`s. Pending configs are `HashTable`s with config data
+ * that are yet to be applied to configs and `Feature`s. They allow users to
+ * make several changes to the data before actually applying changes to
+ * the gameplay.
+ * 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 .
+ */
+class PendingConfigsTool extends AcediaObject;
+
+/**
+ * This tool works by selecting feature (by class) and config (by `Text`
+ * name) on which it will operate with `SelectConfig()` method and then
+ * invoking the rest of its methods on these selections.
+ * There are some expections (`HasPendingConfigFor()` method) that
+ * explicitly take these values as parameters.
+ * This tool is supposed to be created once for the "feature" command and
+ * have its `SelectConfig()` called each execution with user-specified
+ * parameters.
+ */
+var private class selectedFeatureClass;
+var private Text selectedConfigName;
+
+// Possible errors that might occur when working with pending configs
+enum PendingConfigToolResult
+{
+ // No error
+ PCTE_None,
+ // Pending version of specified config does not exist
+ PCTE_ConfigMissing,
+ // JSON object (`HashTable`) was expected as a parameter for the operation,
+ // but something else was given
+ PCTE_ExpectedObject,
+ // Specified JSON pointer points an non-existent location
+ PCTE_BadPointer
+};
+
+struct PendingConfigs
+{
+ var class featureClass;
+ var HashTable pendingSaves;
+};
+var private array featurePendingEdits;
+
+protected function Finalizer()
+{
+ local int i;
+
+ for (i = 0; i < featurePendingEdits.length; i ++) {
+ _.memory.Free(featurePendingEdits[i].pendingSaves);
+ }
+ featurePendingEdits.length = 0;
+}
+
+/**
+ * Selects feature and config to perform all future operations on.
+ *
+ * @param featureClass Class of the feature for which to edit pending
+ * configs.
+ * @param configName Name of the pending config that caller tool will
+ * work with.
+ */
+public final function SelectConfig(
+ class featureClass,
+ BaseText configName)
+{
+ _.memory.Free(selectedConfigName);
+ selectedFeatureClass = featureClass;
+ selectedConfigName = none;
+ if (configName != none) {
+ selectedConfigName = configName.LowerCopy();
+ }
+}
+
+/**
+ * Checks wither caller tool has recorded pending config named `configName`
+ * for `Feature` defined by class `featureClass`.
+ * This method does no checks regarding existence of an actual config for
+ * the specified `Feature` - it only checks whether caller tool has pending
+ * version.
+ *
+ * @param featureClass Class of the `Feature` to check for pending config.
+ * @param configName Name of the config to check for existence of its
+ * pending version.
+ * @return `true` if specified pending config exists and `false` otherwise.
+ */
+public function bool HasPendingConfigFor(
+ class featureClass,
+ BaseText configName)
+{
+ local int i;
+ local bool result;
+ local Text lowerCaseConfigName;
+
+ if (featureClass == none) return false;
+ if (configName == none) return false;
+
+ for (i = 0; i < featurePendingEdits.length; i ++)
+ {
+ if (featurePendingEdits[i].featureClass == featureClass)
+ {
+ lowerCaseConfigName = configName.LowerCopy();
+ result = featurePendingEdits[i].pendingSaves
+ .HasKey(lowerCaseConfigName);
+ lowerCaseConfigName.FreeSelf();
+ return result;
+ }
+ }
+ return false;
+}
+
+/**
+ * Returns data recorded for the selected pending config inside caller tool.
+ *
+ * @param createIfMissing Method only returns data of the pending version of
+ * the config and if selected config does not yet have a pending version,
+ * it will, by default, return `none`. This parameter allows this method to
+ * create a pending config, based on current config with selected name
+ * (if it exists).
+ * @return Data recorded for the selected pending config. If selected config
+ * does not have a pending version, `createIfMissing` is set to `false`
+ * or not even current config with selected name exists - method returns
+ * `none`.
+ */
+public function HashTable GetPendingConfigData(optional bool createIfMissing)
+{
+ local int editsIndex;
+ local HashTable result;
+ local PendingConfigs newRecord;
+
+ if (selectedConfigName == none) {
+ return none;
+ }
+ editsIndex = GetPendingConfigDataIndex();
+ if (editsIndex >= 0)
+ {
+ result = featurePendingEdits[editsIndex]
+ .pendingSaves
+ .GetHashTable(selectedConfigName);
+ if (result != none) {
+ return result;
+ }
+ }
+ if (createIfMissing)
+ {
+ if (editsIndex < 0)
+ {
+ editsIndex = featurePendingEdits.length;
+ newRecord.featureClass = selectedFeatureClass;
+ newRecord.pendingSaves = _.collections.EmptyHashTable();
+ featurePendingEdits[editsIndex] = newRecord;
+ }
+ result = GetCurrentConfigData();
+ if (result != none)
+ {
+ featurePendingEdits[editsIndex]
+ .pendingSaves
+ .SetItem(selectedConfigName, result);
+ }
+ }
+ return result;
+}
+
+/**
+ * Makes and edit to the config.
+ *
+ * @param pathToValue JSON path at which to make a change.
+ * @param newValue Value to record at the specified path.
+ * @return Result of the operation that reports any errors that have occured.
+ * Any changes are made iff result is `PCTE_None`.
+ */
+public function PendingConfigToolResult EditConfig(
+ BaseText pathToValue,
+ BaseText newValue)
+{
+ local HashTable pendingData;
+ local JSONPointer pointer;
+ local Parser parser;
+ local AcediaObject newValueAsJSON;
+ local PendingConfigToolResult result;
+
+ if (pathToValue == none) {
+ return PCTE_BadPointer;
+ }
+ pendingData = GetPendingConfigData(true);
+ if (pendingData == none) {
+ return PCTE_ConfigMissing;
+ }
+ // Get guaranteed not-`none` JSON value, treating it as JSON string
+ // if necessary
+ parser = _.text.Parse(newValue);
+ newValueAsJSON = _.json.ParseWith(parser);
+ parser.FreeSelf();
+ if (newValueAsJSON == none && newValue != none) {
+ newValueAsJSON = newValue.Copy();
+ }
+ // Set new data
+ pointer = _.json.Pointer(pathToValue);
+ result = SetItemByJSON(pendingData, pointer, newValueAsJSON);
+ pointer.FreeSelf();
+ pendingData.FreeSelf();
+ _.memory.Free(newValueAsJSON);
+ return result;
+}
+
+private function PendingConfigToolResult SetItemByJSON(
+ HashTable data,
+ JSONPointer pointer,
+ AcediaObject jsonValue)
+{
+ local Text containerIndex;
+ local AcediaObject container;
+ local PendingConfigToolResult result;
+
+ if (pointer.IsEmpty())
+ {
+ if (HashTable(jsonValue) != none)
+ {
+ result = ChangePendingConfigData(HashTable(jsonValue));
+ _.memory.Free(jsonValue);
+ return result;
+ }
+ _.memory.Free(jsonValue);
+ return PCTE_ExpectedObject;
+ }
+ // Since `!pointer.IsEmpty()`, we are guaranteed to pop a valid value
+ containerIndex = pointer.Pop();
+ container = data.GetItemByJSON(pointer);
+ if (container == none)
+ {
+ containerIndex.FreeSelf();
+ return PCTE_BadPointer;
+ }
+ result = SetContainerItemByText(container, containerIndex, jsonValue);
+ containerIndex.FreeSelf();
+ container.FreeSelf();
+ return result;
+}
+
+private function PendingConfigToolResult SetContainerItemByText(
+ AcediaObject container,
+ BaseText containerIndex,
+ AcediaObject jsonValue)
+{
+ local int arrayIndex;
+ local Parser parser;
+ local ArrayList arrayListContainer;
+ local HashTable hashTableContainer;
+
+ hashTableContainer = HashTable(container);
+ arrayListContainer = ArrayList(container);
+ if (hashTableContainer != none) {
+ hashTableContainer.SetItem(containerIndex, jsonValue);
+ }
+ if (arrayListContainer != none)
+ {
+ parser = containerIndex.Parse();
+ if (parser.MInteger(arrayIndex, 10).Ok())
+ {
+ arrayListContainer.SetItem(arrayIndex, jsonValue);
+ parser.FreeSelf();
+ return PCTE_None;
+ }
+ parser.FreeSelf();
+ if (containerIndex.Compare(P("-"))) {
+ arrayListContainer.AddItem(jsonValue);
+ }
+ else {
+ return PCTE_BadPointer;
+ }
+ }
+ return PCTE_None;
+}
+
+/**
+ * Removes selected pending config.
+ *
+ * @return Result of the operation that reports any errors that have occured.
+ * Any changes are made iff result is `PCTE_None`.
+ */
+public final function PendingConfigToolResult RemoveConfig()
+{
+ local int editIndex;
+ local HashTable pendingSaves;
+
+ editIndex = GetPendingConfigDataIndex();
+ if (editIndex < 0) return PCTE_ConfigMissing;
+ pendingSaves = featurePendingEdits[editIndex].pendingSaves;
+ if (!pendingSaves.HasKey(selectedConfigName)) return PCTE_ConfigMissing;
+
+ pendingSaves.RemoveItem(selectedConfigName);
+ if (pendingSaves.GetLength() <= 0)
+ {
+ pendingSaves.FreeSelf();
+ featurePendingEdits.Remove(editIndex, 1);
+ }
+ return PCTE_None;
+}
+
+private function int GetPendingConfigDataIndex()
+{
+ local int i;
+
+ for (i = 0; i < featurePendingEdits.length; i ++)
+ {
+ if (featurePendingEdits[i].featureClass == selectedFeatureClass) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+private function PendingConfigToolResult ChangePendingConfigData(
+ HashTable newData)
+{
+ local int editsIndex;
+
+ if (selectedConfigName == none) {
+ return PCTE_None;
+ }
+ editsIndex = GetPendingConfigDataIndex();
+ if (editsIndex < 0) {
+ return PCTE_ConfigMissing;
+ }
+ featurePendingEdits[editsIndex].pendingSaves
+ .SetItem(selectedConfigName, newData);
+ return PCTE_None;
+}
+
+private function HashTable GetCurrentConfigData()
+{
+ return selectedFeatureClass.default.configClass.static
+ .LoadData(selectedConfigName);
+}
+
+defaultproperties
+{
+}
\ No newline at end of file