From ad99ca1723a84b859eb95c1e25ff377d0cd10c80 Mon Sep 17 00:00:00 2001 From: Hannes Greule Date: Sat, 2 Jan 2021 17:32:07 +0100 Subject: [PATCH] Use WE Expressions instead of js evaluation (#2941) * Implement thread-safe expression evaluation * Update `{args}` to `plot` automatically * Stringify more money/balance/price occurrences with EconHandler#format --- .../bukkit/util/BukkitEconHandler.java | 6 ++ .../com/plotsquared/core/command/Auto.java | 14 +-- .../com/plotsquared/core/command/Buy.java | 8 +- .../com/plotsquared/core/command/Claim.java | 16 ++-- .../com/plotsquared/core/command/Delete.java | 13 ++- .../plotsquared/core/command/MainCommand.java | 50 +++++------ .../com/plotsquared/core/command/Merge.java | 17 ++-- .../components/ComponentPresetManager.java | 2 +- .../com/plotsquared/core/plot/PlotArea.java | 13 ++- .../plotsquared/core/util/EconHandler.java | 15 +++- .../com/plotsquared/core/util/Expression.java | 85 ------------------- .../plotsquared/core/util/PlotExpression.java | 70 +++++++++++++++ 12 files changed, 157 insertions(+), 152 deletions(-) delete mode 100644 Core/src/main/java/com/plotsquared/core/util/Expression.java create mode 100644 Core/src/main/java/com/plotsquared/core/util/PlotExpression.java diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEconHandler.java b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEconHandler.java index df5eb2f56..a60818d98 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEconHandler.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/util/BukkitEconHandler.java @@ -36,6 +36,7 @@ import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.plugin.RegisteredServiceProvider; +import org.checkerframework.checker.nullness.qual.NonNull; @Singleton public class BukkitEconHandler extends EconHandler { @@ -84,6 +85,11 @@ import org.bukkit.plugin.RegisteredServiceProvider; return plotArea.useEconomy(); } + @Override + public @NonNull String format(double balance) { + return this.econ.format(balance); + } + @Override public boolean isSupported() { return true; diff --git a/Core/src/main/java/com/plotsquared/core/command/Auto.java b/Core/src/main/java/com/plotsquared/core/command/Auto.java index c32c39843..9ffded70b 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Auto.java +++ b/Core/src/main/java/com/plotsquared/core/command/Auto.java @@ -46,8 +46,8 @@ import com.plotsquared.core.plot.world.PlotAreaManager; import com.plotsquared.core.services.plots.AutoService; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.EventDispatcher; -import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.AutoClaimFinishTask; import com.plotsquared.core.util.task.RunnableVal; import com.plotsquared.core.util.task.TaskManager; @@ -276,24 +276,24 @@ public class Auto extends SubCommand { } } if (this.econHandler != null && plotarea.useEconomy()) { - Expression costExp = plotarea.getPrices().get("claim"); - double cost = costExp.evaluate((double) (Settings.Limit.GLOBAL ? + PlotExpression costExp = plotarea.getPrices().get("claim"); + double cost = costExp.evaluate(Settings.Limit.GLOBAL ? player.getPlotCount() : - player.getPlotCount(plotarea.getWorldName()))); + player.getPlotCount(plotarea.getWorldName())); cost = (size_x * size_z) * cost; if (cost > 0d) { if (!force && this.econHandler.getMoney(player) < cost) { player.sendMessage( TranslatableCaption.of("economy.cannot_afford_plot"), - Template.of("money", String.valueOf(cost)), - Template.of("balance", String.valueOf(this.econHandler.getMoney(player))) + Template.of("money", this.econHandler.format(cost)), + Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player))) ); return true; } this.econHandler.withdrawMoney(player, cost); player.sendMessage( TranslatableCaption.of("economy.removed_balance"), - Template.of("money", String.valueOf(cost)) + Template.of("money", this.econHandler.format(cost)) ); } } diff --git a/Core/src/main/java/com/plotsquared/core/command/Buy.java b/Core/src/main/java/com/plotsquared/core/command/Buy.java index 24d21653b..400b7db98 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Buy.java +++ b/Core/src/main/java/com/plotsquared/core/command/Buy.java @@ -92,15 +92,15 @@ public class Buy extends Command { } checkTrue(this.econHandler.getMoney(player) >= price, TranslatableCaption.of("economy.cannot_afford_plot"), - Template.of("money", String.valueOf(price)), - Template.of("balance", String.valueOf(this.econHandler.getMoney(player)))); + Template.of("money", this.econHandler.format(price)), + Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player)))); this.econHandler.withdrawMoney(player, price); // Failure // Success confirm.run(this, () -> { player.sendMessage( TranslatableCaption.of("economy.removed_balance"), - Template.of("money", String.valueOf(price)) + Template.of("money", this.econHandler.format(price)) ); this.econHandler.depositMoney(PlotSquared.platform().playerManager().getOfflinePlayer(plot.getOwnerAbs()), price); @@ -111,7 +111,7 @@ public class Buy extends Command { TranslatableCaption.of("economy.plot_sold"), Template.of("plot", plot.getId().toString()), Template.of("player", player.getName()), - Template.of("price", String.valueOf(price)) + Template.of("price", this.econHandler.format(price)) ); } PlotFlag plotFlag = plot.getFlagContainer().getFlag(PriceFlag.class); diff --git a/Core/src/main/java/com/plotsquared/core/command/Claim.java b/Core/src/main/java/com/plotsquared/core/command/Claim.java index e0f2075fd..5948dd748 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Claim.java +++ b/Core/src/main/java/com/plotsquared/core/command/Claim.java @@ -42,8 +42,8 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.EventDispatcher; -import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.TaskManager; import net.kyori.adventure.text.minimessage.Template; import org.slf4j.Logger; @@ -136,20 +136,22 @@ public class Claim extends SubCommand { } } if (this.econHandler.isEnabled(area) && !force) { - Expression costExr = area.getPrices().get("claim"); - double cost = costExr.evaluate((double) currentPlots); + PlotExpression costExr = area.getPrices().get("claim"); + double cost = costExr.evaluate(currentPlots); if (cost > 0d) { if (this.econHandler.getMoney(player) < cost) { player.sendMessage( TranslatableCaption.of("economy.cannot_afford_plot"), - Template.of("money", String.valueOf(cost)) + Template.of("money", this.econHandler.format(cost)), + Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player))) ); - } + return false; + } this.econHandler.withdrawMoney(player, cost); player.sendMessage( TranslatableCaption.of("economy.removed_balance"), - Template.of("money", String.valueOf(cost)), - Template.of("balance", String.valueOf(this.econHandler.getMoney(player))) + Template.of("money", this.econHandler.format(cost)), + Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player))) ); } } diff --git a/Core/src/main/java/com/plotsquared/core/command/Delete.java b/Core/src/main/java/com/plotsquared/core/command/Delete.java index 93070215b..419e00710 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Delete.java +++ b/Core/src/main/java/com/plotsquared/core/command/Delete.java @@ -26,24 +26,23 @@ package com.plotsquared.core.command; import com.google.inject.Inject; -import com.plotsquared.core.events.TeleportCause; -import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.events.Result; +import com.plotsquared.core.events.TeleportCause; import com.plotsquared.core.location.Location; +import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.EventDispatcher; -import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.TaskManager; import net.kyori.adventure.text.minimessage.Template; import javax.annotation.Nonnull; -import javax.annotation.Nullable; @CommandDeclaration(command = "delete", @@ -106,13 +105,13 @@ public class Delete extends SubCommand { boolean result = plot.getPlotModificationManager().deletePlot(player, () -> { plot.removeRunning(); if (this.econHandler.isEnabled(plotArea)) { - Expression valueExr = plotArea.getPrices().get("sell"); - double value = plots.size() * valueExr.evaluate((double) currentPlots); + PlotExpression valueExr = plotArea.getPrices().get("sell"); + double value = plots.size() * valueExr.evaluate(currentPlots); if (value > 0d) { this.econHandler.depositMoney(player, value); player.sendMessage( TranslatableCaption.of("economy.added_balance"), - Template.of("money", String.valueOf(value)) + Template.of("money", this.econHandler.format(value)) ); } } diff --git a/Core/src/main/java/com/plotsquared/core/command/MainCommand.java b/Core/src/main/java/com/plotsquared/core/command/MainCommand.java index 1a4f2371b..36fac50f3 100644 --- a/Core/src/main/java/com/plotsquared/core/command/MainCommand.java +++ b/Core/src/main/java/com/plotsquared/core/command/MainCommand.java @@ -27,10 +27,10 @@ package com.plotsquared.core.command; import com.google.inject.Injector; import com.plotsquared.core.PlotSquared; -import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.configuration.Settings; import com.plotsquared.core.configuration.caption.TranslatableCaption; import com.plotsquared.core.location.Location; +import com.plotsquared.core.permissions.Permission; import com.plotsquared.core.player.ConsolePlayer; import com.plotsquared.core.player.MetaDataAccess; import com.plotsquared.core.player.PlayerMetaDataKeys; @@ -38,8 +38,8 @@ import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.util.EconHandler; -import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.task.RunnableVal2; import com.plotsquared.core.util.task.RunnableVal3; import org.slf4j.Logger; @@ -190,11 +190,10 @@ public class MainCommand extends Command { CmdConfirm.addPending(player, cmd.getUsage(), () -> { PlotArea area = player.getApplicablePlotArea(); if (area != null && econHandler.isEnabled(area)) { - Expression priceEval = + PlotExpression priceEval = area.getPrices().get(cmd.getFullId()); - Double price = priceEval != null ? priceEval.evaluate(0d) : 0d; - if (price != null - && econHandler.getMoney(player) < price) { + double price = priceEval != null ? priceEval.evaluate(0d) : 0d; + if (econHandler.getMoney(player) < price) { if (failure != null) { failure.run(); } @@ -207,17 +206,15 @@ public class MainCommand extends Command { }); return; } - if (econHandler != null) { - PlotArea area = player.getApplicablePlotArea(); - if (area != null) { - Expression priceEval = area.getPrices().get(cmd.getFullId()); - Double price = priceEval != null ? priceEval.evaluate(0d) : 0d; - if (price != 0d && econHandler.getMoney(player) < price) { - if (failure != null) { - failure.run(); - } - return; + PlotArea area = player.getApplicablePlotArea(); + if (area != null && econHandler.isEnabled(area)) { + PlotExpression priceEval = area.getPrices().get(cmd.getFullId()); + double price = priceEval != null ? priceEval.evaluate(0d) : 0d; + if (price != 0d && econHandler.getMoney(player) < price) { + if (failure != null) { + failure.run(); } + return; } } if (success != null) { @@ -277,19 +274,16 @@ public class MainCommand extends Command { if ("f".equals(args[0].substring(1))) { confirm = new RunnableVal3() { @Override public void run(Command cmd, Runnable success, Runnable failure) { - if (PlotSquared.platform().econHandler() != null) { - PlotArea area = player.getApplicablePlotArea(); - if (area != null) { - Expression priceEval = - area.getPrices().get(cmd.getFullId()); - Double price = priceEval != null ? priceEval.evaluate(0d) : 0d; - if (price != 0d - && PlotSquared.platform().econHandler().getMoney(player) < price) { - if (failure != null) { - failure.run(); - } - return; + if (area != null && PlotSquared.platform().econHandler().isEnabled(area)) { + PlotExpression priceEval = + area.getPrices().get(cmd.getFullId()); + double price = priceEval != null ? priceEval.evaluate(0d) : 0d; + if (price != 0d + && PlotSquared.platform().econHandler().getMoney(player) < price) { + if (failure != null) { + failure.run(); } + return; } } if (success != null) { diff --git a/Core/src/main/java/com/plotsquared/core/command/Merge.java b/Core/src/main/java/com/plotsquared/core/command/Merge.java index 5a299b8cc..e369fd653 100644 --- a/Core/src/main/java/com/plotsquared/core/command/Merge.java +++ b/Core/src/main/java/com/plotsquared/core/command/Merge.java @@ -39,8 +39,8 @@ import com.plotsquared.core.plot.Plot; import com.plotsquared.core.plot.PlotArea; import com.plotsquared.core.util.EconHandler; import com.plotsquared.core.util.EventDispatcher; -import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.Permissions; +import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.StringMan; import net.kyori.adventure.text.minimessage.Template; @@ -162,8 +162,8 @@ public class Merge extends SubCommand { return false; } final PlotArea plotArea = plot.getArea(); - Expression priceExr = plotArea.getPrices().getOrDefault("merge", null); - final double price = priceExr == null ? 0d : priceExr.evaluate((double) size); + PlotExpression priceExr = plotArea.getPrices().getOrDefault("merge", null); + final double price = priceExr == null ? 0d : priceExr.evaluate(size); UUID uuid = player.getUUID(); if (direction == Direction.ALL) { @@ -184,7 +184,8 @@ public class Merge extends SubCommand { this.econHandler.withdrawMoney(player, price); player.sendMessage( TranslatableCaption.of("economy.removed_balance"), - Template.of("money", String.valueOf(price)) + Template.of("money", this.econHandler.format(price)), + Template.of("balance", this.econHandler.format(this.econHandler.getMoney(player))) ); } player.sendMessage(TranslatableCaption.of("merge.success_merge")); @@ -205,7 +206,7 @@ public class Merge extends SubCommand { && this.econHandler.getMoney(player) < price) { player.sendMessage( TranslatableCaption.of("economy.cannot_afford_merge"), - Template.of("money", String.valueOf(price)) + Template.of("money", this.econHandler.format(price)) ); return false; } @@ -228,7 +229,7 @@ public class Merge extends SubCommand { this.econHandler.withdrawMoney(player, price); player.sendMessage( TranslatableCaption.of("economy.removed_balance"), - Template.of("money", String.valueOf(price)) + Template.of("money", this.econHandler.format(price)) ); } player.sendMessage(TranslatableCaption.of("merge.success_merge")); @@ -268,14 +269,14 @@ public class Merge extends SubCommand { if (!force && this.econHandler.getMoney(player) < price) { player.sendMessage( TranslatableCaption.of("economy.cannot_afford_merge"), - Template.of("money", String.valueOf(price)) + Template.of("money", this.econHandler.format(price)) ); return; } this.econHandler.withdrawMoney(player, price); player.sendMessage( TranslatableCaption.of("economy.removed_balance"), - Template.of("money", String.valueOf(price)) + Template.of("money", this.econHandler.format(price)) ); } player.sendMessage(TranslatableCaption.of("merge.success_merge")); diff --git a/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java b/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java index 04ec5934c..681c3a036 100644 --- a/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java +++ b/Core/src/main/java/com/plotsquared/core/components/ComponentPresetManager.java @@ -186,7 +186,7 @@ public class ComponentPresetManager { } else { econHandler.withdrawMoney(getPlayer(), componentPreset.getCost()); getPlayer().sendMessage(TranslatableCaption.of("economy.removed_balance"), - Template.of("money", componentPreset.getCost() + "")); + Template.of("money", econHandler.format(componentPreset.getCost()))); } } diff --git a/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java b/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java index 89cbec088..ee83bc9f6 100644 --- a/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java +++ b/Core/src/main/java/com/plotsquared/core/plot/PlotArea.java @@ -57,8 +57,8 @@ import com.plotsquared.core.plot.flag.implementations.DoneFlag; import com.plotsquared.core.plot.flag.types.DoubleFlag; import com.plotsquared.core.queue.GlobalBlockQueue; import com.plotsquared.core.queue.QueueCoordinator; -import com.plotsquared.core.util.Expression; import com.plotsquared.core.util.MathMan; +import com.plotsquared.core.util.PlotExpression; import com.plotsquared.core.util.RegionUtil; import com.plotsquared.core.util.StringMan; import com.sk89q.worldedit.math.BlockVector2; @@ -134,7 +134,7 @@ public abstract class PlotArea { private int maxBuildHeight = 256; private int minBuildHeight = 1; private GameMode gameMode = GameModes.CREATIVE; - private Map> prices = new HashMap<>(); + private Map prices = new HashMap<>(); private List schematics = new ArrayList<>(); private boolean roadFlags = false; private boolean worldBorder = false; @@ -306,7 +306,12 @@ public abstract class PlotArea { if (this.useEconomy) { this.prices = new HashMap<>(); for (String key : priceSection.getKeys(false)) { - this.prices.put(key, Expression.doubleExpression(priceSection.getString(key))); + String raw = priceSection.getString(key); + if (raw.contains("{args}")) { + raw = raw.replace("{args}", "plots"); + priceSection.set(key, raw); // update if replaced + } + this.prices.put(key, PlotExpression.compile(raw, "plots")); } } this.plotChat = config.getBoolean("chat.enabled"); @@ -1315,7 +1320,7 @@ public abstract class PlotArea { return this.gameMode; } - public Map> getPrices() { + public Map getPrices() { return this.prices; } diff --git a/Core/src/main/java/com/plotsquared/core/util/EconHandler.java b/Core/src/main/java/com/plotsquared/core/util/EconHandler.java index 28b78ea6d..326e95383 100644 --- a/Core/src/main/java/com/plotsquared/core/util/EconHandler.java +++ b/Core/src/main/java/com/plotsquared/core/util/EconHandler.java @@ -29,7 +29,7 @@ import com.plotsquared.core.player.ConsolePlayer; import com.plotsquared.core.player.OfflinePlotPlayer; import com.plotsquared.core.player.PlotPlayer; import com.plotsquared.core.plot.PlotArea; -import com.sk89q.worldedit.EditSession; +import org.checkerframework.checker.nullness.qual.NonNull; public abstract class EconHandler { @@ -75,6 +75,14 @@ public abstract class EconHandler { */ public abstract boolean isEnabled(PlotArea plotArea); + /** + * Formats the given balance into a human-readable number. + * + * @param balance the balance to format. + * @return the balance as formatted string. + */ + public abstract @NonNull String format(double balance); + /** * Returns whether economy is supported by the server or not. * @@ -114,6 +122,11 @@ public abstract class EconHandler { return false; } + @Override + public @NonNull String format(double balance) { + return ""; + } + @Override public boolean isSupported() { return false; diff --git a/Core/src/main/java/com/plotsquared/core/util/Expression.java b/Core/src/main/java/com/plotsquared/core/util/Expression.java deleted file mode 100644 index 6fcf1daa1..000000000 --- a/Core/src/main/java/com/plotsquared/core/util/Expression.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * _____ _ _ _____ _ - * | __ \| | | | / ____| | | - * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | - * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | - * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | - * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| - * | | - * |_| - * PlotSquared plot management system for Minecraft - * Copyright (C) 2021 IntellectualSites - * - * This program 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, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 this program. If not, see . - */ -package com.plotsquared.core.util; - -import com.plotsquared.core.command.DebugExec; -import com.plotsquared.core.command.MainCommand; -import com.plotsquared.core.configuration.Settings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.script.ScriptException; - -public abstract class Expression { - - private static final Logger logger = LoggerFactory.getLogger("P2/" + Expression.class.getSimpleName()); - - public static Expression constant(final U value) { - return new Expression() { - @Override public U evaluate(U arg) { - return value; - } - }; - } - - public static Expression linearDouble(final Double value) { - return new Expression() { - @Override public Double evaluate(Double arg) { - return (arg * value); - } - }; - } - - public static Expression doubleExpression(final String expression) { - try { - return constant(Double.parseDouble(expression)); - } catch (NumberFormatException ignore) { - } - if (expression.endsWith("*{arg}")) { - try { - return linearDouble( - Double.parseDouble(expression.substring(0, expression.length() - 6))); - } catch (NumberFormatException ignore) { - } - } - return new Expression() { - @Override public Double evaluate(Double arg) { - DebugExec exec = (DebugExec) MainCommand.getInstance().getCommand(DebugExec.class); - try { - return (Double) exec.getEngine().eval(expression.replace("{arg}", "" + arg)); - } catch (ScriptException e) { - if (Settings.DEBUG) { - logger.info("Invalid expression: {}", expression); - } - e.printStackTrace(); - } - return 0d; - } - }; - } - - public abstract T evaluate(T arg); -} diff --git a/Core/src/main/java/com/plotsquared/core/util/PlotExpression.java b/Core/src/main/java/com/plotsquared/core/util/PlotExpression.java new file mode 100644 index 000000000..9e2f0723d --- /dev/null +++ b/Core/src/main/java/com/plotsquared/core/util/PlotExpression.java @@ -0,0 +1,70 @@ +/* + * _____ _ _ _____ _ + * | __ \| | | | / ____| | | + * | |__) | | ___ | |_| (___ __ _ _ _ __ _ _ __ ___ __| | + * | ___/| |/ _ \| __|\___ \ / _` | | | |/ _` | '__/ _ \/ _` | + * | | | | (_) | |_ ____) | (_| | |_| | (_| | | | __/ (_| | + * |_| |_|\___/ \__|_____/ \__, |\__,_|\__,_|_| \___|\__,_| + * | | + * |_| + * PlotSquared plot management system for Minecraft + * Copyright (C) 2021 IntellectualSites + * + * This program 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, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ +package com.plotsquared.core.util; + +import com.sk89q.worldedit.internal.expression.Expression; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * An expression that can be evaluated. + * Evaluation is thread-safe. + * This is a wrapper for {@link Expression}. + */ +public class PlotExpression { + private final Expression expression; + private final Object lock = new Object(); + + /** + * Compiles an expression from a string. + * + * @param expression the expression to compile. + * @param variableNames the variables that can be set in {@link #evaluate(double...)}. + * @return the compiled expression. + */ + public static @NonNull PlotExpression compile(final @NonNull String expression, + final @NonNull String @NonNull ... variableNames) { + return new PlotExpression(expression, variableNames); + } + + private PlotExpression(final @NonNull String rawExpression, final @NonNull String @NonNull [] variableNames) { + this.expression = Expression.compile(rawExpression, variableNames); + } + + /** + * Evaluates the expression with the given variable values. + * + * @param values the values to set the variables to. + * @return the result of the evaluation. + */ + public double evaluate(double... values) { + double evaluate; + // synchronization is likely the best option in terms of memory and cpu consumption + synchronized (this.lock) { + evaluate = this.expression.evaluate(values); + } + return evaluate; + } +}