diff --git a/src/main/java/com/denizenscript/denizencore/objects/core/DurationTag.java b/src/main/java/com/denizenscript/denizencore/objects/core/DurationTag.java index 739302e1..a3b3d854 100644 --- a/src/main/java/com/denizenscript/denizencore/objects/core/DurationTag.java +++ b/src/main/java/com/denizenscript/denizencore/objects/core/DurationTag.java @@ -7,6 +7,8 @@ import com.denizenscript.denizencore.utilities.CoreConfiguration; import com.denizenscript.denizencore.utilities.CoreUtilities; import com.denizenscript.denizencore.utilities.Deprecations; +import com.denizenscript.denizencore.utilities.data.Actionable; +import com.denizenscript.denizencore.utilities.data.DataActionException; import com.denizenscript.denizencore.utilities.debugging.Debug; import com.denizenscript.denizencore.tags.Attribute; import com.denizenscript.denizencore.tags.TagContext; @@ -14,7 +16,7 @@ /** * Durations are a convenient way to get a 'unit of time' within Denizen. */ -public class DurationTag implements ObjectTag { +public class DurationTag implements ObjectTag, Actionable { // <--[ObjectType] // @name DurationTag @@ -560,4 +562,22 @@ public String formatted(boolean words) { } return (isNegative ? "negative " : "") + timeString.trim(); } + + @Override + public DurationTag operationAdd(ObjectTag value, TagContext context) { + DurationTag toAdd = value.asType(DurationTag.class, context); + if (toAdd == null) { + throw new DataActionException("Cannot add non-duration to duration!"); + } + return new DurationTag(this.seconds + toAdd.seconds); + } + + @Override + public DurationTag operationSub(ObjectTag value, TagContext context) { + DurationTag toSub = value.asType(DurationTag.class, context); + if (toSub == null) { + throw new DataActionException("Cannot subtract non-duration from duration!"); + } + return new DurationTag(this.seconds - toSub.seconds); + } } diff --git a/src/main/java/com/denizenscript/denizencore/objects/core/ElementTag.java b/src/main/java/com/denizenscript/denizencore/objects/core/ElementTag.java index e26f9d98..3a651064 100644 --- a/src/main/java/com/denizenscript/denizencore/objects/core/ElementTag.java +++ b/src/main/java/com/denizenscript/denizencore/objects/core/ElementTag.java @@ -6,6 +6,7 @@ import com.denizenscript.denizencore.tags.*; import com.denizenscript.denizencore.tags.core.EscapeTagUtil; import com.denizenscript.denizencore.utilities.*; +import com.denizenscript.denizencore.utilities.data.Actionable; import com.denizenscript.denizencore.utilities.debugging.Debug; import java.io.UnsupportedEncodingException; @@ -24,7 +25,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ElementTag implements ObjectTag { +public class ElementTag implements ObjectTag, Actionable { // NOTE: Explicitly no example value // <--[ObjectType] @@ -2623,5 +2624,40 @@ public boolean advancedMatches(String matcher, TagContext context) { }; } + @Override + public ElementTag operationAdd(ObjectTag value, TagContext context) { + BigDecimal toAdd = parseBigDecimal(value); + return new ElementTag(asBigDecimal().add(toAdd)); + } + + @Override + public ElementTag operationSub(ObjectTag value, TagContext context) { + BigDecimal toSubtract = parseBigDecimal(value); + return new ElementTag(asBigDecimal().subtract(toSubtract)); + } + + @Override + public ElementTag operationMul(ObjectTag value, TagContext context) { + BigDecimal toMultiply = parseBigDecimal(value); + return new ElementTag(asBigDecimal().multiply(toMultiply)); + } + + @Override + public ElementTag operationDiv(ObjectTag value, TagContext context) { + BigDecimal toDivide = parseBigDecimal(value).setScale(15, RoundingMode.HALF_UP); + return new ElementTag(asBigDecimal().divide(toDivide, RoundingMode.HALF_UP)); + } + + public static BigDecimal parseBigDecimal(ObjectTag object) { + if (object == null) { + return BigDecimal.ZERO; + } + try { + return new BigDecimal(object.toString()); + } catch (NumberFormatException e) { + return BigDecimal.ZERO; + } + } + public static final Pattern UNACCENTED_PATTERN = Pattern.compile("[\\u0300-\\u036f]"); } diff --git a/src/main/java/com/denizenscript/denizencore/objects/core/TimeTag.java b/src/main/java/com/denizenscript/denizencore/objects/core/TimeTag.java index 8c3cd6fc..b3bf5525 100644 --- a/src/main/java/com/denizenscript/denizencore/objects/core/TimeTag.java +++ b/src/main/java/com/denizenscript/denizencore/objects/core/TimeTag.java @@ -10,6 +10,8 @@ import com.denizenscript.denizencore.tags.TagContext; import com.denizenscript.denizencore.utilities.CoreUtilities; import com.denizenscript.denizencore.utilities.Deprecations; +import com.denizenscript.denizencore.utilities.data.Actionable; +import com.denizenscript.denizencore.utilities.data.DataActionException; import com.denizenscript.denizencore.utilities.debugging.Debug; import java.time.*; @@ -20,7 +22,7 @@ import java.util.List; import java.util.Locale; -public class TimeTag implements ObjectTag, Adjustable, FlaggableObject { +public class TimeTag implements ObjectTag, Adjustable, FlaggableObject, Actionable { // <--[ObjectType] // @name TimeTag @@ -751,4 +753,22 @@ public void applyProperty(Mechanism mechanism) { public void adjust(Mechanism mechanism) { tagProcessor.processMechanism(this, mechanism); } + + @Override + public TimeTag operationAdd(ObjectTag value, TagContext context) { + DurationTag toAdd = value.asType(DurationTag.class, context); + if (toAdd == null) { + throw new DataActionException("Cannot add non-duration to time!"); + } + return new TimeTag(this.millis() + toAdd.getMillis(), this.instant.getZone()); + } + + @Override + public TimeTag operationSub(ObjectTag value, TagContext context) { + DurationTag toSub = value.asType(DurationTag.class, context); + if (toSub == null) { + throw new DataActionException("Cannot subtract non-duration from time!"); + } + return new TimeTag(this.millis() - toSub.getMillis(), this.instant.getZone()); + } } diff --git a/src/main/java/com/denizenscript/denizencore/objects/core/VectorObject.java b/src/main/java/com/denizenscript/denizencore/objects/core/VectorObject.java index 2e8a43f0..c6526e3f 100644 --- a/src/main/java/com/denizenscript/denizencore/objects/core/VectorObject.java +++ b/src/main/java/com/denizenscript/denizencore/objects/core/VectorObject.java @@ -1,13 +1,20 @@ package com.denizenscript.denizencore.objects.core; +import com.denizenscript.denizencore.DenizenCore; +import com.denizenscript.denizencore.DenizenImplementation; import com.denizenscript.denizencore.objects.ObjectTag; import com.denizenscript.denizencore.tags.ObjectTagProcessor; +import com.denizenscript.denizencore.tags.TagContext; import com.denizenscript.denizencore.utilities.CoreUtilities; +import com.denizenscript.denizencore.utilities.data.Actionable; +import com.denizenscript.denizencore.utilities.debugging.Debug; + +import java.util.Vector; /** * Represents an object that contains X/Y/Z 3D Vector. */ -public interface VectorObject extends ObjectTag { +public interface VectorObject extends ObjectTag, Actionable { // <--[ObjectType] // @name VectorObject @@ -291,29 +298,54 @@ static void register(Class type, ObjectTagProcessor // --> processor.registerTag(QuaternionTag.class, type, "quaternion_between_vectors", (attribute, object, other) -> { double dot = object.dot(other); - if (dot < -0.9999f) - { + if (dot < -0.9999f) { double absX = Math.abs(object.getX()); double absY = Math.abs(object.getY()); double absZ = Math.abs(object.getZ()); - if (absX < absY && absX < absZ) - { + if (absX < absY && absX < absZ) { return new QuaternionTag(0, -object.getZ(), object.getY(), 0).normalized(); } - else if (absY < absZ) - { + else if (absY < absZ) { return new QuaternionTag(-object.getZ(), 0, object.getX(), 0).normalized(); } - else - { + else { return new QuaternionTag(-object.getY(), object.getX(), 0, 0).normalized(); } } - else - { + else { VectorObject axis = object.crossProduct(other); return new QuaternionTag(axis.getX(), axis.getY(), axis.getZ(), dot + 1).normalized(); } }); } + + @Override + default VectorObject operationAdd(ObjectTag value, TagContext context) { + VectorObject toAdd = DenizenCore.implementation.vectorize(value, context); + VectorObject toReturn = duplicate(); + toReturn.setX(getX() + toAdd.getX()); + toReturn.setY(getY() + toAdd.getY()); + toReturn.setZ(getZ() + toAdd.getZ()); + return toReturn; + } + + @Override + default VectorObject operationSub(ObjectTag value, TagContext context) { + VectorObject toSub = DenizenCore.implementation.vectorize(value, context); + VectorObject toReturn = duplicate(); + toReturn.setX(getX() - toSub.getX()); + toReturn.setY(getY() - toSub.getY()); + toReturn.setZ(getZ() - toSub.getZ()); + return toReturn; + } + + @Override + default VectorObject operationMul(ObjectTag value, TagContext context) { + return this.duplicate().multipliedBy(value.asElement().asDouble()); + } + + @Override + default VectorObject operationDiv(ObjectTag value, TagContext context) { + return this.duplicate().multipliedBy(1.0 / value.asElement().asDouble()); + } } diff --git a/src/main/java/com/denizenscript/denizencore/utilities/data/Actionable.java b/src/main/java/com/denizenscript/denizencore/utilities/data/Actionable.java new file mode 100644 index 00000000..4aa45458 --- /dev/null +++ b/src/main/java/com/denizenscript/denizencore/utilities/data/Actionable.java @@ -0,0 +1,23 @@ +package com.denizenscript.denizencore.utilities.data; + +import com.denizenscript.denizencore.objects.ObjectTag; +import com.denizenscript.denizencore.tags.TagContext; + +public interface Actionable { + + default T operationAdd(ObjectTag value, TagContext context) { + throw new DataActionException("This object does not support adding operations."); + } + + default T operationSub(ObjectTag value, TagContext context) { + throw new DataActionException("This object does not support subtracting operations."); + } + + default T operationMul(ObjectTag value, TagContext context) { + throw new DataActionException("This object does not support multiplying operations."); + } + + default T operationDiv(ObjectTag value, TagContext context) { + throw new DataActionException("This object does not support dividing operations."); + } +} diff --git a/src/main/java/com/denizenscript/denizencore/utilities/data/DataAction.java b/src/main/java/com/denizenscript/denizencore/utilities/data/DataAction.java index c4e77f2c..1356cdd7 100644 --- a/src/main/java/com/denizenscript/denizencore/utilities/data/DataAction.java +++ b/src/main/java/com/denizenscript/denizencore/utilities/data/DataAction.java @@ -6,9 +6,9 @@ import com.denizenscript.denizencore.objects.core.ListTag; import com.denizenscript.denizencore.objects.ObjectTag; import com.denizenscript.denizencore.utilities.Deprecations; +import com.denizenscript.denizencore.utilities.debugging.Debug; import java.math.BigDecimal; -import java.math.RoundingMode; public class DataAction { @@ -35,80 +35,6 @@ public String toString() { return "(" + keyDebug + ":" + type + ":" + inputValue + ")"; } - public ListTag autoList(String key, TagContext context) { - ObjectTag obj = provider.getValueAt(key); - if (obj == null) { - return new ListTag(); - } - else { - return autoList(ListTag.getListFor(obj, context)); - } - } - - public ListTag autoList(ListTag list) { - return new ListTag(list); - } - - public ObjectTag autoDup(ObjectTag object) { - if (object == null) { - return null; - } - if (object instanceof ListTag) { - return autoList((ListTag) object); - } - return object.duplicate(); - } - - public BigDecimal autoNumber(TagContext context) { - ObjectTag obj = provider.getValueAt(key); - if (index != 0) { - ListTag subList = ListTag.getListFor(obj, context); - if (index < 0 || index > subList.size()) { - if (index == Integer.MAX_VALUE && !subList.isEmpty()) { - obj = subList.getObject(subList.size() - 1); - } - else { - return BigDecimal.ZERO; - } - } - else { - obj = subList.getObject(index - 1); - } - } - try { - return autoNumber(obj); - } - catch (NumberFormatException ex) { - return BigDecimal.ZERO; - } - } - - public BigDecimal autoNumber(ObjectTag obj) { - if (obj == null) { - return BigDecimal.ZERO; - } - return new BigDecimal(obj.toString()); - } - - public ElementTag autoNumber(BigDecimal decimal) { - return new ElementTag(decimal); - } - - public void autoSet(ObjectTag value, TagContext context) { - if (index != 0) { - ObjectTag obj = provider.getValueAt(key); - ListTag subList = ListTag.getListFor(obj, context); - if (index == Integer.MAX_VALUE) { - subList.setObject(subList.isEmpty() ? 0 : (subList.size() - 1), value); - } - else { - 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 + "."); @@ -116,60 +42,30 @@ public void requiresInputValue() { } public void execute(TagContext context) { + // Special operators switch (type) { - case INCREMENT: { - BigDecimal num = autoNumber(context); - num = num.add(BigDecimal.ONE); - autoSet(autoNumber(num), context); - break; - } - case DECREMENT: { - BigDecimal num = autoNumber(context); - num = num.subtract(BigDecimal.ONE); - autoSet(autoNumber(num), context); - break; - } - case ADD: { - requiresInputValue(); - BigDecimal num = autoNumber(context); - num = num.add(autoNumber(inputValue)); - autoSet(autoNumber(num), context); - break; - } - case SUBTRACT: { - requiresInputValue(); - BigDecimal num = autoNumber(context); - num = num.subtract(autoNumber(inputValue)); - autoSet(autoNumber(num), context); - break; - } - case MULTIPLY: { - requiresInputValue(); - BigDecimal num = autoNumber(context); - num = num.multiply(autoNumber(inputValue)); - autoSet(autoNumber(num), context); - break; + case INCREMENT -> { + BigDecimal base = ElementTag.parseBigDecimal(getBase(context)); + setResult(new ElementTag(base.add(BigDecimal.ONE)), context); + return; } - case DIVIDE: { - requiresInputValue(); - BigDecimal num = autoNumber(context); - num = num.setScale(15, RoundingMode.HALF_UP); - num = num.divide(autoNumber(inputValue), RoundingMode.HALF_UP); - autoSet(autoNumber(num), context); - break; + case DECREMENT -> { + BigDecimal base = ElementTag.parseBigDecimal(getBase(context)); + setResult(new ElementTag(base.subtract(BigDecimal.ONE)), context); + return; } - case INSERT: { + case INSERT -> { requiresInputValue(); ListTag list = autoList(key, context); list.addObject(inputValue); provider.setValueAt(key, list); - break; + return; } - case REMOVE: { + case REMOVE -> { ListTag list = autoList(key, context); if (index != 0) { if (index == Integer.MAX_VALUE && !list.isEmpty()) { - list.remove(list.size() - 1); + list.removeLast(); } else { list.remove(index - 1); @@ -186,30 +82,78 @@ public void execute(TagContext context) { } } provider.setValueAt(key, list); - break; + return; } - case SPLIT: { + case SPLIT -> { requiresInputValue(); ListTag list = autoList(key, context); list.addObjects(ListTag.getListFor(inputValue, context).objectForms); provider.setValueAt(key, list); - break; + return; } - case SPLIT_NEW: + case SPLIT_NEW -> { Deprecations.splitNewDataAction.warn(context); requiresInputValue(); - provider.setValueAt(key, autoList(ListTag.getListFor(inputValue, context))); - break; - case SET: + provider.setValueAt(key, new ListTag(ListTag.getListFor(inputValue, context))); + return; + } + case SET -> { requiresInputValue(); - autoSet(autoDup(inputValue), context); - break; - case AUTO_SET: + if (inputValue == null) { + setResult(null, context); + } + else if (inputValue instanceof ListTag listTag) { + setResult(new ListTag(listTag), context); + } + else { + setResult(inputValue.duplicate(), context); + } + return; + } + case AUTO_SET -> { provider.setValueAt(key, new ElementTag(true)); - break; - case CLEAR: + return; + } + case CLEAR -> { provider.setValueAt(key, null); - break; + return; + } + } + // Abstract operators + requiresInputValue(); + ObjectTag base = getBase(context); + if (!(base instanceof Actionable)) { + Debug.echoError("Cannot perform data action on non-actionable object '" + base.identify() + "'."); + return; + } + Actionable actionable = (Actionable) base; + switch (type) { + case ADD -> setResult(actionable.operationAdd(inputValue, context), context); + case SUBTRACT -> setResult(actionable.operationSub(inputValue, context), context); + case MULTIPLY -> setResult(actionable.operationMul(inputValue, context), context); + case DIVIDE -> setResult(actionable.operationDiv(inputValue, context), context); + } + } + + public ListTag autoList(String key, TagContext context) { + ObjectTag obj = provider.getValueAt(key); + return obj == null ? new ListTag() : new ListTag(ListTag.getListFor(obj, context)); + } + + public ObjectTag getBase(TagContext context) { + if (index == 0) { + return CoreUtilities.fixType(provider.getValueAt(key), context); + } + ListTag list = ListTag.getListFor(provider.getValueAt(key), context); + return list.getObject((index == Integer.MAX_VALUE ? list.size() : index) - 1); + } + + public void setResult(ObjectTag result, TagContext context) { + if (index != 0) { + ListTag list = ListTag.getListFor(provider.getValueAt(key), context); + list.setObject((index == Integer.MAX_VALUE ? list.size() : index) - 1, result); + result = list; } + provider.setValueAt(key, result); } } diff --git a/src/main/java/com/denizenscript/denizencore/utilities/data/DataActionHelper.java b/src/main/java/com/denizenscript/denizencore/utilities/data/DataActionHelper.java index ec5816f0..314a7a99 100644 --- a/src/main/java/com/denizenscript/denizencore/utilities/data/DataActionHelper.java +++ b/src/main/java/com/denizenscript/denizencore/utilities/data/DataActionHelper.java @@ -31,6 +31,9 @@ public static void parseKey(DataAction action, String key) { if (CoreUtilities.equalsIgnoreCase(index, "last")) { action.index = Integer.MAX_VALUE; } + else if (CoreUtilities.equalsIgnoreCase(index, "first")) { + action.index = 1; + } else if (ArgumentHelper.matchesInteger(index)) { action.index = Integer.parseInt(index); } @@ -53,56 +56,34 @@ public static DataAction parse(ActionableDataProvider provider, String actionArg String action = split.get(1); if (split.size() == 2) { switch (action) { - case "++": - toReturn.type = DataActionType.INCREMENT; - break; - case "--": - toReturn.type = DataActionType.DECREMENT; - break; - case "!": - toReturn.type = DataActionType.CLEAR; - break; - case "<-": - toReturn.type = DataActionType.REMOVE; - break; - default: + case "++" -> toReturn.type = DataActionType.INCREMENT; + case "--" -> toReturn.type = DataActionType.DECREMENT; + case "!" -> toReturn.type = DataActionType.CLEAR; + case "<-" -> toReturn.type = DataActionType.REMOVE; + default -> { toReturn.type = DataActionType.SET; toReturn.inputValue = ObjectFetcher.pickObjectFor(action, context); - break; + } } return toReturn; } toReturn.inputValue = new ElementTag(split.get(2)); switch (action) { - case "->": + case "->" -> { toReturn.inputValue = ObjectFetcher.pickObjectFor(split.get(2), context); toReturn.type = DataActionType.INSERT; - break; - case "<-": - toReturn.type = DataActionType.REMOVE; - break; - case "|": - toReturn.type = DataActionType.SPLIT; - break; - case "!|": - toReturn.type = DataActionType.SPLIT_NEW; - break; - case "+": - toReturn.type = DataActionType.ADD; - break; - case "-": - toReturn.type = DataActionType.SUBTRACT; - break; - case "*": - toReturn.type = DataActionType.MULTIPLY; - break; - case "/": - toReturn.type = DataActionType.DIVIDE; - break; - default: + } + case "<-" -> toReturn.type = DataActionType.REMOVE; + case "|" -> toReturn.type = DataActionType.SPLIT; + case "!|" -> toReturn.type = DataActionType.SPLIT_NEW; + case "+" -> toReturn.type = DataActionType.ADD; + case "-" -> toReturn.type = DataActionType.SUBTRACT; + case "*" -> toReturn.type = DataActionType.MULTIPLY; + case "/" -> toReturn.type = DataActionType.DIVIDE; + default -> { toReturn.type = DataActionType.SET; toReturn.inputValue = ObjectFetcher.pickObjectFor(split.get(1) + ":" + split.get(2), context); - break; + } } return toReturn; }