From 8dafa00762dd684836eccd638c11f6a760a6215a Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Wed, 12 Jun 2019 08:31:32 -0700 Subject: [PATCH] (initial) data action system, define command support --- .../aufdemrand/denizencore/objects/dList.java | 5 + .../scripts/commands/CommandRegistry.java | 33 ++-- .../scripts/commands/core/DefineCommand.java | 40 +++- .../data/ActionableDataProvider.java | 19 ++ .../utilities/data/DataAction.java | 184 ++++++++++++++++++ .../utilities/data/DataActionException.java | 10 + .../utilities/data/DataActionHelper.java | 77 ++++++++ .../utilities/data/DataActionType.java | 59 ++++++ 8 files changed, 402 insertions(+), 25 deletions(-) create mode 100644 src/main/java/net/aufdemrand/denizencore/utilities/data/ActionableDataProvider.java create mode 100644 src/main/java/net/aufdemrand/denizencore/utilities/data/DataAction.java create mode 100644 src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionException.java create mode 100644 src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionHelper.java create mode 100644 src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionType.java diff --git a/src/main/java/net/aufdemrand/denizencore/objects/dList.java b/src/main/java/net/aufdemrand/denizencore/objects/dList.java index 2671a23c..732d47db 100644 --- a/src/main/java/net/aufdemrand/denizencore/objects/dList.java +++ b/src/main/java/net/aufdemrand/denizencore/objects/dList.java @@ -95,6 +95,11 @@ public void addObject(int index, dObject obj) { super.add(index, obj.toString()); } + public void setObject(int index, dObject obj) { + objectForms.set(index, obj); + super.set(index, obj.toString()); + } + public dObject getObject(int id) { return objectForms.get(id); } diff --git a/src/main/java/net/aufdemrand/denizencore/scripts/commands/CommandRegistry.java b/src/main/java/net/aufdemrand/denizencore/scripts/commands/CommandRegistry.java index 8b1008b9..349be707 100644 --- a/src/main/java/net/aufdemrand/denizencore/scripts/commands/CommandRegistry.java +++ b/src/main/java/net/aufdemrand/denizencore/scripts/commands/CommandRegistry.java @@ -253,14 +253,14 @@ public void registerCoreCommands() { // <--[command] // @Name Define - // @Syntax define [] [] + // @Syntax define [](:)[:] // @Required 1 // @Short Creates a temporary variable inside a script queue. // @Group core // @Description // Definitions are queue-level (or script-level) 'variables' that can be used throughout a script, once - // defined, by using the ]> tag. Definitions are only valid on the current queue and are + // defined, by using the <[]> tag. Definitions are only valid on the current queue and are // not transferred to any new queues constructed within the script, such as a 'run' command, without explicitly // specifying to do so. // @@ -268,31 +268,28 @@ public void registerCoreCommands() { // that is, you can't add or remove from the definition, but you can re-create it if you wish to specify a new // value. Definitions are also automatically removed when the queue is completed, so there is no worry for // leaving unused data hanging around. - + // + // Refer to <@link language data actions> + // // @Tags - // ]> to get the value assigned to an ID - + // <[]> to get the value assigned to an ID + // // @Usage // Use to make complex tags look less complex, and scripts more readable // - narrate 'You invoke your power of notice...' - // - define range '' - // - define blocks '' - // - narrate '[NOTICE] You have noticed ].within[].size> blocks in the area that may be of interest.' - + // - define range: + // - define blocks: + // - narrate '[NOTICE] You have noticed ].within[<[range]>].size> blocks in the area that may be of interest.' + // // @Usage // Use to keep the value of a replaceable tag that you might use many times within a single script. Definitions // can be faster and cleaner than reusing a replaceable tag over and over - // - define arg1 - // - if == hello: + // - define arg1: + // - if <[arg1]> == hello: // - narrate 'Hello!' - // - else if == goodbye: + // - else if <[arg1]> == goodbye: // - narrate 'Goodbye!' - - // @Usage - // Use to pass some important information (arguments) on to another queue - // - run 'new_task' d:hello|world - // 'new_task' now has some definitions, and , that contains the contents specified, 'hello' and 'world'. - + // // @Usage // Use to remove a definition // - define myDef:! diff --git a/src/main/java/net/aufdemrand/denizencore/scripts/commands/core/DefineCommand.java b/src/main/java/net/aufdemrand/denizencore/scripts/commands/core/DefineCommand.java index 6afeaeb7..2b5d818e 100644 --- a/src/main/java/net/aufdemrand/denizencore/scripts/commands/core/DefineCommand.java +++ b/src/main/java/net/aufdemrand/denizencore/scripts/commands/core/DefineCommand.java @@ -6,7 +6,11 @@ import net.aufdemrand.denizencore.objects.dObject; import net.aufdemrand.denizencore.scripts.ScriptEntry; import net.aufdemrand.denizencore.scripts.commands.AbstractCommand; +import net.aufdemrand.denizencore.scripts.queues.ScriptQueue; import net.aufdemrand.denizencore.utilities.CoreUtilities; +import net.aufdemrand.denizencore.utilities.data.ActionableDataProvider; +import net.aufdemrand.denizencore.utilities.data.DataAction; +import net.aufdemrand.denizencore.utilities.data.DataActionHelper; import net.aufdemrand.denizencore.utilities.debugging.dB; /** @@ -14,16 +18,31 @@ */ public class DefineCommand extends AbstractCommand { + public static class DefinitionActionProvider extends ActionableDataProvider { + + public ScriptQueue queue; + + @Override + public dObject getValueAt(String keyName) { + return queue.getDefinitionObject(keyName); + } + + @Override + public void setValueAt(String keyName, dObject value) { + queue.addDefinition(keyName, value); + } + } + @Override public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException { for (aH.Argument arg : aH.interpretArguments(scriptEntry.aHArgs)) { if (!scriptEntry.hasObject("definition")) { - if (arg.getValue().equals("!") && arg.hasPrefix()) { - scriptEntry.addObject("remove", new Element("true")); - scriptEntry.addObject("value", new Element("null")); - scriptEntry.addObject("definition", arg.getPrefix().asElement()); + if (arg.raw_value.contains(":")) { + DefinitionActionProvider provider = new DefinitionActionProvider(); + provider.queue = scriptEntry.getResidingQueue(); + scriptEntry.addObject("action", DataActionHelper.parse(provider, arg.raw_value)); } else { scriptEntry.addObject("definition", new Element(CoreUtilities.toLowerCase(arg.getValue()))); @@ -39,7 +58,7 @@ else if (!scriptEntry.hasObject("value")) { } } - if (!scriptEntry.hasObject("definition") || !scriptEntry.hasObject("value")) { + if ((!scriptEntry.hasObject("definition") || !scriptEntry.hasObject("value")) && !scriptEntry.hasObject("action")) { throw new InvalidArgumentsException("Must specify a definition and value!"); } } @@ -50,14 +69,21 @@ public void execute(ScriptEntry scriptEntry) { Element definition = scriptEntry.getElement("definition"); dObject value = scriptEntry.getdObject("value"); Element remove = scriptEntry.getElement("remove"); + Object actionObj = scriptEntry.getObject("action"); + DataAction action = actionObj == null ? null : (DataAction) actionObj; if (scriptEntry.dbCallShouldDebug()) { dB.report(scriptEntry, getName(), aH.debugObj("queue", scriptEntry.getResidingQueue().id) - + definition.debug() - + value.debug() + + (definition == null ? "" : definition.debug()) + + (value == null ? "" : value.debug()) + + (action == null ? "" : action.debug()) + (remove != null ? remove.debug() : "")); } + if (action != null) { + action.execute(); + return; + } if (scriptEntry.hasObject("remove")) { scriptEntry.getResidingQueue().removeDefinition(definition.asString()); } diff --git a/src/main/java/net/aufdemrand/denizencore/utilities/data/ActionableDataProvider.java b/src/main/java/net/aufdemrand/denizencore/utilities/data/ActionableDataProvider.java new file mode 100644 index 00000000..52b7246d --- /dev/null +++ b/src/main/java/net/aufdemrand/denizencore/utilities/data/ActionableDataProvider.java @@ -0,0 +1,19 @@ +package net.aufdemrand.denizencore.utilities.data; + +import net.aufdemrand.denizencore.objects.dObject; + +public abstract class ActionableDataProvider { + + /** + * Return the value object at a key. + * Result should generally be Element or dList. + */ + public abstract dObject getValueAt(String keyName); + + /** + * Set the valueu object to a key. + * Value will be Element or dList. + * null indicates to remove the key. + */ + public abstract void setValueAt(String keyName, dObject value); +} diff --git a/src/main/java/net/aufdemrand/denizencore/utilities/data/DataAction.java b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataAction.java new file mode 100644 index 00000000..e0cdad1f --- /dev/null +++ b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataAction.java @@ -0,0 +1,184 @@ +package net.aufdemrand.denizencore.utilities.data; + +import net.aufdemrand.denizencore.objects.Element; +import net.aufdemrand.denizencore.objects.aH; +import net.aufdemrand.denizencore.objects.dList; +import net.aufdemrand.denizencore.objects.dObject; +import net.aufdemrand.denizencore.utilities.CoreUtilities; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +public class DataAction { + + public ActionableDataProvider provider; + + public DataActionType type; + + public String key; + + /** + * Zero = no index needed. + * Positive number = list index (starting at 1). + */ + public int index = 0; + + public dObject inputValue = null; + + public String debug() { + return aH.debugObj("action", "(" + key + "[" + index + "]:" + type + ":" + inputValue + ")"); + } + + public dList autoList(String key) { + dObject obj = provider.getValueAt(key); + if (obj == null) { + return new dList(); + } + else { + return autoList(dList.getListFor(obj)); + } + } + + public dList autoList(dList list) { + return new dList(list); + } + + public dObject autoDup(dObject object) { + if (object == null) { + return null; + } + if (object instanceof dList) { + return autoList((dList) object); + } + return new Element(object.toString()); + } + + public BigDecimal autoNumber() { + dObject obj = provider.getValueAt(key); + if (index != 0) { + dList subList = dList.getListFor(obj); + if (index < 0 || index > subList.size()) { + return BigDecimal.ZERO; + } + obj = subList.getObject(index - 1); + } + return autoNumber(obj); + } + + public BigDecimal autoNumber(dObject obj) { + if (obj == null) { + return BigDecimal.ZERO; + } + return new BigDecimal(obj.toString()); + } + + public Element autoNumber(BigDecimal decimal) { + return new Element(decimal); + } + + public void autoSet(dObject value) { + if (index != 0) { + dObject obj = provider.getValueAt(key); + dList subList = dList.getListFor(obj); + subList.setObject(index - 1, value); + value = subList; + } + provider.setValueAt(key, value); + } + + public void requiresInputValue() { + if (inputValue == null) { + throw new DataActionException("Input value required for data action " + type + "."); + } + } + + public void execute() { + switch (type) { + case INCREMENT: { + BigDecimal num = autoNumber(); + num = num.add(BigDecimal.ONE); + autoSet(autoNumber(num)); + break; + } + case DECREMENT: { + BigDecimal num = autoNumber(); + num = num.subtract(BigDecimal.ONE); + autoSet(autoNumber(num)); + break; + } + case ADD: { + requiresInputValue(); + BigDecimal num = autoNumber(); + num = num.add(autoNumber(inputValue)); + autoSet(autoNumber(num)); + break; + } + case SUBTRACT: { + requiresInputValue(); + BigDecimal num = autoNumber(); + num = num.subtract(autoNumber(inputValue)); + autoSet(autoNumber(num)); + break; + } + case MULTIPLY: { + requiresInputValue(); + BigDecimal num = autoNumber(); + num = num.multiply(autoNumber(inputValue)); + autoSet(autoNumber(num)); + break; + } + case DIVIDE: { + requiresInputValue(); + BigDecimal num = autoNumber(); + num = num.setScale(15, RoundingMode.HALF_UP); + num = num.divide(autoNumber(inputValue), RoundingMode.HALF_UP); + autoSet(autoNumber(num)); + break; + } + case INSERT: { + requiresInputValue(); + dList list = autoList(key); + list.addObject(inputValue); + provider.setValueAt(key, list); + break; + } + case REMOVE: { + dList list = autoList(key); + if (index != 0) { + list.remove(index - 1); + } + requiresInputValue(); + String findValue = CoreUtilities.toLowerCase(inputValue.toString()); + for (int i = 0; i < list.size(); i++) { + if (CoreUtilities.toLowerCase(list.get(i)).equals(findValue)) { + list.remove(i); + break; + } + } + provider.setValueAt(key, list); + break; + } + case SPLIT: { + requiresInputValue(); + dList list = autoList(key); + list.addObjects(dList.getListFor(inputValue).objectForms); + provider.setValueAt(key, list); + break; + } + case SPLIT_NEW: + requiresInputValue(); + provider.setValueAt(key, autoList(dList.getListFor(inputValue))); + break; + case SET: + requiresInputValue(); + provider.setValueAt(key, autoDup(inputValue)); + break; + case AUTO_SET: + provider.setValueAt(key, new Element(true)); + break; + case CLEAR: + provider.setValueAt(key, null); + break; + } + } +} diff --git a/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionException.java b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionException.java new file mode 100644 index 00000000..f56b9b46 --- /dev/null +++ b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionException.java @@ -0,0 +1,10 @@ +package net.aufdemrand.denizencore.utilities.data; + +public class DataActionException extends RuntimeException { + + private static final long serialVersionUID = 3159123523457793068L; + + public DataActionException(String message) { + super(message); + } +} diff --git a/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionHelper.java b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionHelper.java new file mode 100644 index 00000000..6cff7c9a --- /dev/null +++ b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionHelper.java @@ -0,0 +1,77 @@ +package net.aufdemrand.denizencore.utilities.data; + +import net.aufdemrand.denizencore.objects.Element; +import net.aufdemrand.denizencore.objects.aH; +import net.aufdemrand.denizencore.utilities.CoreUtilities; + +import java.util.List; + +public class DataActionHelper { + + public static DataAction parse(ActionableDataProvider provider, String actionArgument) { + DataAction toReturn = new DataAction(); + toReturn.provider = provider; + List split = CoreUtilities.split(actionArgument, ':', 3); + toReturn.key = split.get(0); + int bracketIndex = toReturn.key.indexOf('['); + if (bracketIndex >= 0) { + String index = toReturn.key.substring(bracketIndex + 1, toReturn.key.lastIndexOf(']')); + toReturn.key = toReturn.key.substring(bracketIndex); + toReturn.index = aH.getIntegerFrom(index); + } + if (split.size() == 1) { + toReturn.type = DataActionType.AUTO_SET; + return toReturn; + } + String action = split.get(1); + if (split.size() == 2) { + if (action.equals("++")) { + toReturn.type = DataActionType.INCREMENT; + } + else if (action.equals("--")) { + toReturn.type = DataActionType.DECREMENT; + } + else if (action.equals("!")) { + toReturn.type = DataActionType.CLEAR; + } + else if (action.equals("<-")) { + toReturn.type = DataActionType.REMOVE; + } + else { + toReturn.type = DataActionType.SET; + toReturn.inputValue = new Element(action); + } + return toReturn; + } + toReturn.inputValue = new Element(split.get(2)); + if (action.equals("->")) { + toReturn.type = DataActionType.INSERT; + } + else if (action.equals("<-")) { + toReturn.type = DataActionType.REMOVE; + } + else if (action.equals("|")) { + toReturn.type = DataActionType.SPLIT; + } + else if (action.equals("!|")) { + toReturn.type = DataActionType.SPLIT_NEW; + } + else if (action.equals("+")) { + toReturn.type = DataActionType.ADD; + } + else if (action.equals("-")) { + toReturn.type = DataActionType.SUBTRACT; + } + else if (action.equals("*")) { + toReturn.type = DataActionType.MULTIPLY; + } + else if (action.equals("/")) { + toReturn.type = DataActionType.DIVIDE; + } + else { + toReturn.type = DataActionType.SET; + toReturn.inputValue = new Element(split.get(1) + ":" + split.get(2)); + } + return toReturn; + } +} diff --git a/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionType.java b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionType.java new file mode 100644 index 00000000..31ac2613 --- /dev/null +++ b/src/main/java/net/aufdemrand/denizencore/utilities/data/DataActionType.java @@ -0,0 +1,59 @@ +package net.aufdemrand.denizencore.utilities.data; + +public enum DataActionType { + + // <--[language] + // @name Data Actions + // @group Useful Lists + // @description + // Several commands function as a way to modify data values, + // including <@link command flag>, <@link command yaml>, and <@link command define>. + // These commands each allow for a set of generic data change operations. + // + // These operations can be used with a syntax like "::" + // For example "mykey:+:5" will add 5 to the value at 'mykey'. + // + // The following actions are available: + // + // Actions that take no input value: + // Increment: '++': raises the value numerically up by 1. + // Decrement: '--': lowers the value numerically down by 1. + // Clear: '!': removes the value entirely. + // + // Actions that take an input value: + // Add: '+': adds the input value to the value at the key. + // Subtract: '-': subtracts the input value from the value at the key. + // Multiply: '*': multiplies the value at the key by the input value. + // Divide: '/': divides the value at the key by the input value. + // List insert: '->': adds the input value as a single new entry in the list (see also 'List split'). + // List remove: '<-': removes the input value from the list. + // List split: '|': splits the input list and adds each value into the list at the key. + // Split to new: '!|': similar to list split, but removes the existing value at the key first. + // + // Special cases: + // In some commands, specifying no action or input value will automatically set the key's value to 'true'. + // Setting a ':' without an action will set the key to the exact value. Be careful to not input a list like this, use 'split to new' instead. + // + // Note that the input may take an index input as well. + // That is, for example: "mykey[3]:--" will decrement the third item in the list at 'mykey'. + // This syntax may also be used to remove the entry at a specified index. + // + // --> + + /* Math */ + INCREMENT, + DECREMENT, + ADD, + SUBTRACT, + MULTIPLY, + DIVIDE, + /* Lists */ + INSERT, + REMOVE, + SPLIT, + SPLIT_NEW, + /* Value */ + SET, + AUTO_SET, + CLEAR +}