From c308ed5526d8311041ece5b30905cdf5b5bc5e4d Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Wed, 26 Apr 2023 00:14:19 +0200 Subject: [PATCH 1/2] Start work on new condition editor panel --- .../java/com/github/manolo8/darkbot/Bot.java | 24 +-- .../darkbot/config/actions/Condition.java | 13 +- .../config/actions/LegacyCondition.java | 15 ++ .../config/actions/SyntaxException.java | 18 ++- .../manolo8/darkbot/config/actions/Value.java | 12 +- .../actions/conditions/AbstractCondition.java | 27 ++-- .../actions/conditions/AfterCondition.java | 22 ++- .../actions/conditions/AllCondition.java | 7 +- .../actions/conditions/AnyCondition.java | 7 +- .../actions/conditions/EqualCondition.java | 17 ++- .../conditions/HasEffectCondition.java | 13 +- .../conditions/HasFormationCondition.java | 13 +- .../actions/conditions/NoneCondition.java | 7 +- .../conditions/NumericalCondition.java | 15 +- .../actions/conditions/OneCondition.java | 7 +- .../conditions/TargetTypeCondition.java | 13 +- .../actions/conditions/UntilCondition.java | 16 +- .../config/actions/parser/ParseResult.java | 8 + .../config/actions/parser/ValueParser.java | 1 + .../actions/tree/ConditionEditorPanel.java | 104 +++++++++++++ .../config/actions/tree/DocumentReader.java | 119 +++++++++++++++ .../config/actions/tree/ParseTreeModel.java | 22 +++ .../config/actions/tree/ParsingNode.java | 137 ++++++++++++++++++ .../actions/values/BooleanConstant.java | 11 +- .../gui/tree/editors/ConditionEditor.java | 23 +-- .../manolo8/darkbot/gui/utils/UIUtils.java | 1 + .../highlight/BorderHighlightPainter.java | 39 +++++ .../gui/utils/highlight/HighlightHandler.java | 49 +++++++ .../gui/utils/highlight/Locatable.java | 8 + 29 files changed, 655 insertions(+), 113 deletions(-) create mode 100644 src/main/java/com/github/manolo8/darkbot/config/actions/LegacyCondition.java create mode 100644 src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java create mode 100644 src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java create mode 100644 src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParseTreeModel.java create mode 100644 src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java create mode 100644 src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/BorderHighlightPainter.java create mode 100644 src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/HighlightHandler.java create mode 100644 src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/Locatable.java diff --git a/src/main/java/com/github/manolo8/darkbot/Bot.java b/src/main/java/com/github/manolo8/darkbot/Bot.java index b8d822b62..1cae194c6 100644 --- a/src/main/java/com/github/manolo8/darkbot/Bot.java +++ b/src/main/java/com/github/manolo8/darkbot/Bot.java @@ -33,6 +33,20 @@ public static void main(String[] args) throws IOException { LogUtils.setupLogOutput(); + setupUI(); + + LibSetup.setupLibraries(); + StartupParams params = new StartupParams(args); + + System.out.println("Starting DarkBot " + Main.VERSION); + //noinspection ThrowableNotThrown + Runtime.getRuntime().addShutdownHook(new Thread(() -> + new Throwable("DarkBot shutdown peacefully!").printStackTrace())); + + SwingUtilities.invokeLater(() -> new Main(params)); + } + + public static void setupUI() { try { UIManager.put("MenuItem.selectionType", "underline"); UIManager.getFont("Label.font"); // Prevents a linux crash @@ -63,16 +77,6 @@ public static void main(String[] args) throws IOException { } catch (UnsupportedLookAndFeelException e) { e.printStackTrace(); } - - LibSetup.setupLibraries(); - StartupParams params = new StartupParams(args); - - System.out.println("Starting DarkBot " + Main.VERSION); - //noinspection ThrowableNotThrown - Runtime.getRuntime().addShutdownHook(new Thread(() -> - new Throwable("DarkBot shutdown peacefully!").printStackTrace())); - - SwingUtilities.invokeLater(() -> new Main(params)); } @SuppressWarnings("removal") diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/Condition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/Condition.java index f781f4a9f..0d2e1a523 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/Condition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/Condition.java @@ -4,21 +4,18 @@ import eu.darkbot.api.PluginAPI; import org.jetbrains.annotations.NotNull; +@Deprecated public interface Condition extends Value, eu.darkbot.api.config.types.Condition { - @Override - default @NotNull eu.darkbot.api.config.types.Condition.Result get(PluginAPI pluginAPI) { - Result result = get(pluginAPI.requireInstance(Main.class)); - - return eu.darkbot.api.config.types.Condition.Result.values()[result.ordinal()]; - } - /** * @deprecated Use {@link #get(PluginAPI)} instead */ @Deprecated - @NotNull Result get(Main main); + default @NotNull Result get(Main main) { + return Result.values()[get(main.pluginAPI).ordinal()]; + } + @Deprecated enum Result { ALLOW, DENY, ABSTAIN; diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/LegacyCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/LegacyCondition.java new file mode 100644 index 000000000..854e81a9f --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/LegacyCondition.java @@ -0,0 +1,15 @@ +package com.github.manolo8.darkbot.config.actions; + +/** + * This class exists PURELY to avoid an import mess. + * Condition implementations need to keep implementing darkbot's Condition (interface) + * for backwards-compat with older plugins, but if they implement darkbot's Condition directly, + * Condition.Result is darkbot's condition instead of the API's. To solve this, they will now + * implement LegacyCondition so that ConditionResult is the api's condition. + *
+ * This will be all removed in a future release, when legacy plugins stop using legacy conditions and have + * fully migrated to the API's condition type. + */ +@SuppressWarnings("deprecation") +public interface LegacyCondition extends Condition { +} diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java b/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java index af6a4882b..5fd5506db 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java @@ -8,11 +8,12 @@ import java.util.List; import java.util.Objects; -public class SyntaxException extends Exception { +public class SyntaxException extends RuntimeException { private static final String[] EMPTY = new String[]{}; private final String at; + private final int atIdx; private final String[] chars; private final List> metadatas; private boolean singleMeta = false; @@ -21,6 +22,14 @@ public SyntaxException(String message, String at) { this(message, at, (List>) null, (String[]) null); } + public SyntaxException(String message, int atIdx, String... chars) { + super(message); + this.at = ""; + this.atIdx = atIdx; + this.chars = chars == null ? EMPTY : chars; + this.metadatas = Collections.emptyList(); + } + public > SyntaxException(String message, String at, Class metadatas) { this(message, at, (List>) null, Arrays.stream(metadatas.getEnumConstants()) .map(Objects::toString).toArray(String[]::new)); @@ -34,6 +43,7 @@ public SyntaxException(String message, String at, Values.Meta meta, String... public SyntaxException(String message, String at, List> metas, String... chars) { super(message); this.at = at == null ? "" : at; + this.atIdx = -1; this.chars = chars == null ? EMPTY : chars; this.metadatas = metas == null ? Collections.emptyList() : metas; } @@ -42,6 +52,12 @@ public String getAt() { return at; } + public int getIdx(String originalString) { + if (atIdx != -1) return atIdx; + + return Math.max(0, originalString.lastIndexOf(at)); + } + public @NotNull String[] getExpected() { return chars; } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/Value.java b/src/main/java/com/github/manolo8/darkbot/config/actions/Value.java index 2c9ea1106..a09e4f53b 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/Value.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/Value.java @@ -1,20 +1,26 @@ package com.github.manolo8.darkbot.config.actions; import com.github.manolo8.darkbot.Main; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.Nullable; public interface Value { - @Nullable R get(Main main); + @Nullable R get(Main api); static T get(Value val, Main main) { if (val == null) return null; return val.get(main); } - static boolean allows(Value val, Main main) { - Condition.Result res = get(val, main); + static boolean allows(Value val, PluginAPI api) { + Condition.Result res = get(val, api); return res != null && res.allows(); } + static T get(Value val, PluginAPI api) { + return get(val, api.requireInstance(Main.class)); + } + } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java index 36063a3af..a0345346b 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java @@ -1,41 +1,44 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.ValueData; import com.github.manolo8.darkbot.config.actions.parser.ParseResult; import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; -import com.github.manolo8.darkbot.config.actions.parser.Values; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; -public abstract class AbstractCondition implements Condition, Parser { +import static eu.darkbot.api.config.types.Condition.Result.ABSTAIN; +import static eu.darkbot.api.config.types.Condition.Result.ALLOW; +import static eu.darkbot.api.config.types.Condition.Result.DENY; + +public abstract class AbstractCondition implements LegacyCondition, Parser { - public int min, max; public List children; public AbstractCondition() { children = new ArrayList<>(); } - public Result getValue(Main main, int min, int max) { + public Condition.Result getValue(PluginAPI api, int min, int max) { int[] states = new int[]{0, 0, 0}; for (int i = 0; i < children.size(); i++) { - states[children.get(i).get(main).ordinal()]++; + states[children.get(i).get(api).ordinal()]++; if ((states[1] + states[2] > children.size() - min && states[0] + states[1] > 0) /* Can't reach min anymore */ || states[0] > max /* Allow bigger than max */) { - return Condition.Result.DENY; + return DENY; } if (states[0] >= min && states[0] + (children.size() - 1 - i) >= max) { /* Won't go bigger than max */ - return Condition.Result.ALLOW; + return ALLOW; } } - return states[0] >= min ? Condition.Result.ALLOW : Condition.Result.ABSTAIN; + return states[0] >= min ? ALLOW : ABSTAIN; } protected String name() { @@ -52,10 +55,8 @@ public String parse(String str) throws SyntaxException { boolean hasNext; do { ParseResult pr = ValueParser.parse(str, Result.class); - if (!(pr.value instanceof Condition)) - throw new SyntaxException("Error: Expected boolean condition", str, Values.getMeta(getClass())); - children.add((Condition) pr.value); + children.add(pr.asCondition(str, getClass())); str = pr.leftover.trim(); hasNext = !str.isEmpty() && str.charAt(0) == ','; diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java index 286934b80..f3247d3d2 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java @@ -1,36 +1,32 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; -import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; import com.github.manolo8.darkbot.config.actions.parser.ParseResult; import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; import com.github.manolo8.darkbot.config.actions.values.NumberConstant; -import com.github.manolo8.darkbot.core.entities.Ship; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; -import java.util.Locale; - @ValueData(name = "after", description = "Returns true if inner condition is true after the specified time in seconds", example = "after(7.5, condition)") -public class AfterCondition implements Condition, Parser { +public class AfterCondition implements LegacyCondition, Parser { public long time; - public Value condition; + public Condition condition; private transient Long allowTime = null; @Override - public @NotNull Condition.Result get(Main main) { - Result res = Value.get(condition, main); - if (res == null) return Result.ABSTAIN; + public @NotNull Condition.Result get(PluginAPI api) { + Condition.Result res = condition.get(api); allowTime = res.allows() ? allowTime != null ? allowTime : System.currentTimeMillis() + time : null; - return Result.fromBoolean(allowTime != null && System.currentTimeMillis() > allowTime); + return Condition.Result.fromBoolean(allowTime != null && System.currentTimeMillis() > allowTime); } @@ -48,7 +44,7 @@ public String parse(String str) throws SyntaxException { str = ParseUtil.separate(params, getClass(), ","); ParseResult pr = ValueParser.parse(str, Result.class); - condition = pr.value; + condition = pr.asCondition(str, getClass()); return ParseUtil.separate(pr.leftover.trim(), getClass(), ")"); } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AllCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AllCondition.java index 697405033..ab2474949 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AllCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AllCondition.java @@ -1,15 +1,16 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; import com.github.manolo8.darkbot.config.actions.ValueData; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @ValueData(name = "all", description = "Returns true if all child conditions return true", example = "all(a, b)") public class AllCondition extends AbstractCondition { @Override - public @NotNull Result get(Main main) { - return super.getValue(main, children.size(), children.size()); + public @NotNull Condition.Result get(PluginAPI api) { + return super.getValue(api, children.size(), children.size()); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AnyCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AnyCondition.java index 1104a126b..cf38bb66d 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AnyCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AnyCondition.java @@ -1,15 +1,16 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; import com.github.manolo8.darkbot.config.actions.ValueData; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @ValueData(name = "any", description = "Returns true if any child conditions return true", example = "any(a, b)") public class AnyCondition extends AbstractCondition { @Override - public @NotNull Result get(Main main) { - return super.getValue(main, 1, children.size()); + public @NotNull Condition.Result get(PluginAPI api) { + return super.getValue(api, 1, children.size()); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java index de1f90ae2..0240fba04 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java @@ -1,7 +1,6 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; @@ -10,27 +9,29 @@ import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; import com.github.manolo8.darkbot.utils.ReflectionUtils; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @ValueData(name = "equal", description = "Returns true if both parameters are the same", example = "equal(a, b)") -public class EqualCondition implements Condition, Parser { +public class EqualCondition implements LegacyCondition, Parser { private Boolean isComparable; private Value a, b; @Override - public @NotNull Result get(Main main) { - Object objA = Value.get(a, main), objB = Value.get(b, main); - if (objA == null || objB == null) return Result.ABSTAIN; + public @NotNull Condition.Result get(PluginAPI api) { + Object objA = Value.get(a, api), objB = Value.get(b, api); + if (objA == null || objB == null) return Condition.Result.ABSTAIN; if (isComparable == null) isComparable = isComparable(objA, objB); if (isComparable) //noinspection unchecked - return Result.fromBoolean(((Comparable) objA).compareTo(objB) == 0); - return Result.fromBoolean(objA.equals(objB)); + return Condition.Result.fromBoolean(((Comparable) objA).compareTo(objB) == 0); + return Condition.Result.fromBoolean(objA.equals(objB)); } @Override diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java index 60ab01de1..1946170cc 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java @@ -1,7 +1,6 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; @@ -10,22 +9,24 @@ import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; import com.github.manolo8.darkbot.core.entities.Ship; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; import java.util.Locale; @ValueData(name = "has-effect", description = "Checks if a ship has an effect", example = "has-effect(effect, ship)") -public class HasEffectCondition implements Condition, Parser { +public class HasEffectCondition implements LegacyCondition, Parser { public Effect effect; public Value ship; @Override - public @NotNull Condition.Result get(Main main) { + public @NotNull Condition.Result get(PluginAPI api) { Ship sh; - if ((effect == null) || (sh = Value.get(ship, main)) == null) return Result.ABSTAIN; + if ((effect == null) || (sh = Value.get(ship, api)) == null) return Condition.Result.ABSTAIN; - return Result.fromBoolean(main.effectManager.hasEffect(sh, effect.id)); + return Condition.Result.fromBoolean(sh.hasEffect(effect.id)); } public enum Effect { diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java index 774817d5f..4d364e2c0 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java @@ -1,7 +1,6 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; @@ -10,22 +9,24 @@ import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; import com.github.manolo8.darkbot.core.entities.Ship; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; import java.util.Locale; @ValueData(name = "has-formation", description = "Checks if a ship has a formation", example = "has-formation(formation, ship)") -public class HasFormationCondition implements Condition, Parser { +public class HasFormationCondition implements LegacyCondition, Parser { public Formation formation; public Value ship; @Override - public @NotNull Condition.Result get(Main main) { + public @NotNull Condition.Result get(PluginAPI main) { Ship sh; - if ((formation == null) || (sh = Value.get(ship, main)) == null) return Result.ABSTAIN; + if ((formation == null) || (sh = Value.get(ship, main)) == null) return Condition.Result.ABSTAIN; - return Result.fromBoolean(sh.formationId == formation.id); + return Condition.Result.fromBoolean(sh.formationId == formation.id); } public enum Formation { diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NoneCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NoneCondition.java index abedcd0b1..b967bcd0f 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NoneCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NoneCondition.java @@ -1,7 +1,8 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; import com.github.manolo8.darkbot.config.actions.ValueData; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @ValueData(name = "none", description = "Returns true if no child conditions return true", example = "none(a, b)") @@ -10,8 +11,8 @@ public class NoneCondition extends AbstractCondition { public NoneCondition() {} @Override - public @NotNull Result get(Main main) { - return super.getValue(main, 0, 0); + public @NotNull Condition.Result get(PluginAPI api) { + return super.getValue(api, 0, 0); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java index 3dceae8ef..27f8a2d1f 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java @@ -1,7 +1,6 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; @@ -9,19 +8,21 @@ import com.github.manolo8.darkbot.config.actions.parser.ParseResult; import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; import java.util.function.BiPredicate; @ValueData(name = "if", description = "Compares two numbers", example = "if(a > b)") -public class NumericalCondition implements Condition, Parser { +public class NumericalCondition implements LegacyCondition, Parser { public Value a; public Operation operation; public Value b; @Override - public @NotNull Condition.Result get(Main main) { + public @NotNull Condition.Result get(PluginAPI main) { Number numA, numB; if ((numA = Value.get(a, main)) == null || (numB = Value.get(b, main)) == null || operation == null) return Condition.Result.ABSTAIN; @@ -68,6 +69,7 @@ public String parse(String str) throws SyntaxException { ParseResult prA = ValueParser.parse(str, Number.class); a = prA.value; str = prA.leftover.trim(); + if (str.startsWith(",")) str = str.substring(1).trim(); int chars = Math.min(str.length(), str.length() > 1 && str.charAt(1) == '=' ? 2 : 1); @@ -76,7 +78,10 @@ public String parse(String str) throws SyntaxException { if (operation == null) throw new SyntaxException("Unknown operation '" + op + "'", str, Operation.class); - ParseResult prB = ValueParser.parse(str.substring(chars), Number.class); + str = str.substring(chars).trim(); + if (str.startsWith(",")) str = str.substring(1).trim(); + + ParseResult prB = ValueParser.parse(str, Number.class); b = prB.value; return ParseUtil.separate(prB.leftover.trim(), getClass(), ")"); } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/OneCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/OneCondition.java index 842f124d2..49f86ebdb 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/OneCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/OneCondition.java @@ -1,15 +1,16 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; import com.github.manolo8.darkbot.config.actions.ValueData; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @ValueData(name = "one", description = "Returns true if exactly one child condition return true", example = "one(a, b)") public class OneCondition extends AbstractCondition { @Override - public @NotNull Result get(Main main) { - return super.getValue(main, 1, 1); + public @NotNull Condition.Result get(PluginAPI api) { + return super.getValue(api, 1, 1); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java index 49528e977..c21d58a34 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java @@ -1,7 +1,6 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; @@ -10,6 +9,8 @@ import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; import com.github.manolo8.darkbot.core.entities.Ship; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import eu.darkbot.api.game.entities.Npc; import eu.darkbot.api.game.other.EntityInfo.Diplomacy; import org.jetbrains.annotations.NotNull; @@ -17,17 +18,17 @@ import java.util.Locale; @ValueData(name = "has-relation", description = "Checks the target type", example = "has-relation(npc, target())") -public class TargetTypeCondition implements Condition, Parser { +public class TargetTypeCondition implements LegacyCondition, Parser { private TargetType type; private Value ship; @Override - public @NotNull Condition.Result get(Main main) { + public @NotNull Condition.Result get(PluginAPI api) { if (type == null) { - return Result.ABSTAIN; + return Condition.Result.ABSTAIN; } - return Result.fromBoolean(matches(Value.get(ship, main))); + return Condition.Result.fromBoolean(matches(Value.get(ship, api))); } private boolean matches(Ship target) { diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/UntilCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/UntilCondition.java index 4b73d884f..a06de0f78 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/UntilCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/UntilCondition.java @@ -1,26 +1,26 @@ package com.github.manolo8.darkbot.config.actions.conditions; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @ValueData(name = "until", description = "Returns true if the first matches until the second one turns true", example = "until(a, b)") -public class UntilCondition implements Condition { +public class UntilCondition implements LegacyCondition { - public Value from, until; + public Value from, until; private transient boolean current = false; @Override - public @NotNull Condition.Result get(Main main) { - if (Value.allows(current ? until : from, main)) current = !current; + public @NotNull Condition.Result get(PluginAPI api) { + if (Value.allows(current ? until : from, api)) current = !current; - return Result.fromBoolean(current); + return Condition.Result.fromBoolean(current); } - @Override public String toString() { return "until(" + from + ", " + until + ")"; diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java index 63a649f4a..024e2055c 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java @@ -1,6 +1,8 @@ package com.github.manolo8.darkbot.config.actions.parser; +import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; +import eu.darkbot.api.config.types.Condition; public class ParseResult { public final Value value; @@ -12,4 +14,10 @@ public ParseResult(Value value, Class type, String leftover) { this.type = type; this.leftover = leftover; } + + public > Condition asCondition(String original, Class caller) throws SyntaxException { + if (!(value instanceof Condition)) + throw new SyntaxException("Error: Expected boolean condition", original, Values.getMeta(caller)); + return (Condition) value; + } } \ No newline at end of file diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java index 21cb22f98..fff18493d 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java @@ -8,6 +8,7 @@ public class ValueParser { + @SuppressWarnings("deprecation") public static Condition parseCondition(String str) throws SyntaxException { ParseResult result = parse(str, Condition.Result.class); if (!result.leftover.trim().isEmpty()) diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java new file mode 100644 index 000000000..9965bc5c3 --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java @@ -0,0 +1,104 @@ +package com.github.manolo8.darkbot.config.actions.tree; + +import com.github.manolo8.darkbot.Bot; +import com.github.manolo8.darkbot.config.actions.SyntaxException; +import com.github.manolo8.darkbot.gui.tree.editors.ConditionEditor; +import com.github.manolo8.darkbot.gui.utils.GeneralDocumentListener; +import com.github.manolo8.darkbot.gui.utils.UIUtils; +import com.github.manolo8.darkbot.gui.utils.highlight.BorderHighlightPainter; +import com.github.manolo8.darkbot.gui.utils.highlight.HighlightHandler; +import com.github.manolo8.darkbot.gui.utils.highlight.Locatable; +import eu.darkbot.util.Popups; + +import javax.swing.*; +import javax.swing.text.DefaultHighlighter; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreeNode; +import javax.swing.tree.TreePath; +import java.awt.*; + +/** + * In-testing new condition editor + */ +public class ConditionEditorPanel extends JPanel { + + private final ConditionEditor textArea = new ConditionEditor(); + private final DocumentReader reader = new DocumentReader(textArea.getDocument()); + + private final ParsingNode root = new ParsingNode(); + private final ParseTreeModel treeModel = new ParseTreeModel(root); + private final JTree tree = new ConditionTree(treeModel); + + private final HighlightHandler locationHighlight = + new HighlightHandler(textArea, new BorderHighlightPainter(UIUtils.BLUE_HIGHLIGHT)); + private final HighlightHandler errorHighlight = + new HighlightHandler(textArea, new DefaultHighlighter.DefaultHighlightPainter(UIUtils.RED_HIGHLIGHT)); + + + public ConditionEditorPanel(String initialText) { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setPreferredSize(new Dimension(800, 500)); + + add(new JScrollPane(textArea)); + add(new JScrollPane(tree)); + + textArea.getDocument().addDocumentListener((GeneralDocumentListener) e -> { + try { + reader.reset(); + root.parse(reader); + errorHighlight.remove(); + } catch (SyntaxException ex) { + errorHighlight.setHighlight(ex.getIdx(""), textArea.getDocument().getLength()); + } + + treeModel.updateListeners(); + for (int i = 0; i < tree.getRowCount(); i++) { + TreeNode node = (TreeNode) tree.getPathForRow(i).getLastPathComponent(); + if (node instanceof ParsingNode && !((ParsingNode) node).isSmall()) tree.expandRow(i); + } + }); + + // Sync tree selection to editor highlight + tree.addTreeSelectionListener(event -> { + TreeNode node = (TreeNode) event.getPath().getLastPathComponent(); + if (node instanceof Locatable) locationHighlight.setHighlight((Locatable) node); + }); + + // Sync editor caret to tree highlight + textArea.addCaretListener(e -> { + int a = e.getDot(), b = e.getMark(); + ParsingNode node = root.getAtPosition(Math.min(a, b), Math.max(a, b)); + if (node != null) tree.setSelectionPath(new TreePath(treeModel.getPathToRoot(node))); + }); + + textArea.setText(initialText); + textArea.init(); + } + + private static class ConditionTree extends JTree { + public ConditionTree(TreeModel model) { + super(model); + } + + + // Simple custom node rendering + @Override + public String convertValueToText(Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { + return value instanceof ParsingNode ? ((ParsingNode) value).getPrettyPrinted() : value.toString(); + } + + } + + // Condition editor testing + public static void main(String[] args) { + Bot.setupUI(); + UIManager.put("Tree.paintLines", true); + Popups.of("Condition editor", new ConditionEditorPanel( + "any(\n" + + " if(hp-type(hp-percent, health(hero())) <= percent(35)), \n" + + " all(none(has-effect(leech, hero())), if(hp-type(shield-percent, health(hero())) <= percent(10))))")) + .border(BorderFactory.createEmptyBorder()) + .showAsync(); + } + +} diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java new file mode 100644 index 000000000..583bbb1d5 --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java @@ -0,0 +1,119 @@ +package com.github.manolo8.darkbot.config.actions.tree; + +import com.github.manolo8.darkbot.config.actions.SyntaxException; +import lombok.SneakyThrows; + +import javax.swing.text.Document; +import javax.swing.text.GapContent; +import javax.swing.text.PlainDocument; +import javax.swing.text.Position; +import javax.swing.text.Segment; +import java.util.regex.Pattern; + +public class DocumentReader { + // "\\\\" compiles to string literal \\, which is one regex escaped \ + private static final Pattern ESCAPES_REGEX = Pattern.compile("\\\\(.)"); + + private final Segment BUFFER = new Segment(); + + private final Document document; + private int nextIdx; + + @SneakyThrows + public DocumentReader(String string) { + this.document = new PlainDocument(new GapContent(string.length())); + this.document.insertString(0, string, null); + } + + public DocumentReader(Document document) { + this.document = document; + } + + /** + * Reads a text (arbitrary string) as defined below: + * Any character is eligible, except for '(' ',' ')' and any whitespace char. + * You may use a quoted string "string" for any char except " being eligible. + * Either of them may be escaped by using \, and \ can be escaped by \\. + * @return The next word read in full. + */ + public String readText() { + boolean quoted = pollIf('"'); + + for (int i = nextIdx; i < document.getLength(); i++) { + char ch = charAt(i); + if (ch == '\\') { + // Skip the next char, it's been escaped + i++; + } else if (quoted ? ch == '"' : ch == '(' || ch == ',' || ch == ')' || ch == ' ') { + if (nextIdx == i) + throw new SyntaxException("Invalid text, expected at least one char", nextIdx); + + String result = ESCAPES_REGEX.matcher(substring(nextIdx, i)).replaceAll("$1"); + nextIdx = i + (quoted ? 1 : 0); + return result; + } + } + + if (quoted) { + throw new SyntaxException("Unfinished quoted string", nextIdx, "\""); + } + + String remaining = substring(nextIdx, document.getLength()); + nextIdx = document.getLength(); + return remaining; + } + + public char peekNext() { + if (nextIdx >= document.getLength()) return '\04'; // EOF + return charAt(nextIdx); + } + + @SneakyThrows + public Position getPosition() { + return document.createPosition(nextIdx); + } + + public void skipWhitespace() { + for (int i = nextIdx; i < document.getLength(); i++) { + if (!Character.isWhitespace(charAt(i))) { + nextIdx = i; + break; + } + } + } + + /** + * Peek at the next char ignoring whitespace, if it's {@param ch}, poll it and return true + * @param ch the char to maybe expect next + * @return true if the next char is ch, and it was skipped, false otherwise + */ + public boolean pollIf(char ch) { + for (int i = nextIdx; i < document.getLength(); i++) { + char currChar = charAt(i); + if (currChar == ch) { + nextIdx = i + 1; + return true; + } else if (!Character.isWhitespace(currChar)) { + return false; + } + } + return false; + } + + public void reset() { + this.nextIdx = 0; + } + + @SneakyThrows + public String substring(int start, int end) { + return document.getText(start, end - start); + } + + @SneakyThrows + public char charAt(int pos) { + document.getText(pos, 1, BUFFER); + return BUFFER.charAt(0); + } + + +} diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParseTreeModel.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParseTreeModel.java new file mode 100644 index 000000000..2d4e4b5b3 --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParseTreeModel.java @@ -0,0 +1,22 @@ +package com.github.manolo8.darkbot.config.actions.tree; + +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; + +public class ParseTreeModel extends DefaultTreeModel { + + public ParseTreeModel(ParsingNode root) { + super(root); + } + + public void updateListeners() { + fireTreeStructureChanged(this, null, null, null); + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + + } + +} + diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java new file mode 100644 index 000000000..81d08cdba --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java @@ -0,0 +1,137 @@ +package com.github.manolo8.darkbot.config.actions.tree; + +import com.github.manolo8.darkbot.config.actions.SyntaxException; +import com.github.manolo8.darkbot.gui.utils.highlight.Locatable; +import lombok.AccessLevel; +import lombok.Getter; + +import javax.swing.text.Position; +import javax.swing.tree.TreeNode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +public class ParsingNode implements TreeNode, Locatable { + private final ParsingNode parent; + + private Position start; + private String function = ""; + private List children; + private Position end; + + @Getter(AccessLevel.NONE) + private int totalChildren; + + public ParsingNode() { + this(null); + } + + private ParsingNode(ParsingNode parent) { + this.parent = parent; + } + + public void parse(DocumentReader str) { + str.skipWhitespace(); + start = str.getPosition(); + this.function = str.readText(); + + // Standalone leaf node! a raw value with no (), eg the VAL in: + // string(VAL) or has-effect(VAL, hero()) + if (!str.pollIf('(')) { + this.children = null; + this.totalChildren = 0; + } else { + this.function = this.function.trim(); + this.children = new ArrayList<>(); + + // 0 parameter function + if (!str.pollIf(')')) { + while (true) { + ParsingNode node = new ParsingNode(this); + node.parse(str); + children.add(node); + + if (str.pollIf(')')) break; + if (str.pollIf(',') || str.pollIf(' ')) continue; + + throw new SyntaxException( + "Expected one of ')' or ',' but found '" + str.peekNext() + "'", + str.getPosition().getOffset()); + } + } + totalChildren = children.stream().mapToInt(ch -> ch.totalChildren + 1).sum(); + } + end = str.getPosition(); + } + + public boolean isSmall() { + return totalChildren <= 7; + } + + public String getPrettyPrinted() { + return function + " " + ( + children == null ? "" : + children.isEmpty() ? "()" : + !isSmall() ? "(...)" : + children.stream().map(ParsingNode::toString) + .collect(Collectors.joining(", ", "( ", " )"))); + } + + public String toString() { + return escape(function) + (children == null ? "" : + children.stream().map(ParsingNode::toString) + .collect(Collectors.joining(", ", "(", ")"))); + } + + private static String escape(String val) { + for (char c : val.toCharArray()) { + if (c == '(' || c == ',' || c == ')' || c == ' ') + return '"' + val.replaceAll("([\\\\\"])", "\\$1") + '"'; + } + return val; + } + + public ParsingNode getAtPosition(int min, int max) { + if (children == null) return this; + + for (ParsingNode param : children) { + if (param.getStart().getOffset() <= min && param.getEnd().getOffset() >= max) { + return param.getAtPosition(min, max); + } + } + return this; + } + + @Override + public ParsingNode getChildAt(int childIndex) { + return children.get(childIndex); + } + + @Override + public int getChildCount() { + return children.size(); + } + + @Override + public int getIndex(TreeNode node) { + return children.indexOf((ParsingNode) node); + } + + @Override + public boolean getAllowsChildren() { + return true; + } + + @Override + public boolean isLeaf() { + return children == null; + } + + @Override + public Enumeration children() { + return Collections.enumeration(children); + } +} diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java index 0cba866d7..ed12a400e 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java @@ -1,22 +1,23 @@ package com.github.manolo8.darkbot.config.actions.values; -import com.github.manolo8.darkbot.Main; -import com.github.manolo8.darkbot.config.actions.Condition; +import com.github.manolo8.darkbot.config.actions.LegacyCondition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.ValueData; import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.Values; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @ValueData(name = "boolean", description = "Boolean constant", example = "boolean(true)") -public class BooleanConstant implements Condition, Parser { +public class BooleanConstant implements LegacyCondition, Parser { private Boolean value; @Override - public @NotNull Result get(Main main) { - return Result.fromBoolean(value); + public @NotNull Condition.Result get(PluginAPI api) { + return Condition.Result.fromBoolean(value); } @Override diff --git a/src/main/java/com/github/manolo8/darkbot/gui/tree/editors/ConditionEditor.java b/src/main/java/com/github/manolo8/darkbot/gui/tree/editors/ConditionEditor.java index b4c1952f2..c682ab126 100644 --- a/src/main/java/com/github/manolo8/darkbot/gui/tree/editors/ConditionEditor.java +++ b/src/main/java/com/github/manolo8/darkbot/gui/tree/editors/ConditionEditor.java @@ -39,7 +39,7 @@ public ConditionEditor() { addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { - if (!popup.isOpen()) updateDisplay(); + if (init && !popup.isOpen()) updateDisplay(); } }); } @@ -63,6 +63,10 @@ public void setText(String text) { super.setText(text); } + public void init() { + this.init = true; + } + @Override public Condition getEditorValue() { return condition; @@ -90,32 +94,33 @@ public Condition updateDisplay() { // Don't re-parse, use cached exception if (text.equals(lastParsed)) { - handleSyntaxEx(lastEx); + handleSyntaxEx(lastEx, lastParsed); return condition; } lastParsed = text; + String toParse = getText(); try { - Condition cond = ValueParser.parseCondition(getText()); - handleSyntaxEx(lastEx = null); + Condition cond = ValueParser.parseCondition(toParse); + handleSyntaxEx(lastEx = null, toParse); return cond; } catch (SyntaxException e) { - handleSyntaxEx(lastEx = e); + handleSyntaxEx(lastEx = e, toParse); return null; } } - private void handleSyntaxEx(SyntaxException e) { + private void handleSyntaxEx(SyntaxException e, String parsedText) { if (e == null) { - setHighlight(0, getText().length(), true); + setHighlight(0, parsedText.length(), true); popup.update(null, 0, null); return; } - int s = getText().lastIndexOf(e.getAt()), start = Math.max(0, s); + int start = e.getIdx(parsedText); - setHighlight(start, getText().length(), getText().isEmpty()); + setHighlight(start, parsedText.length(), parsedText.isEmpty()); try { Rectangle rect = getUI().modelToView(this, start); diff --git a/src/main/java/com/github/manolo8/darkbot/gui/utils/UIUtils.java b/src/main/java/com/github/manolo8/darkbot/gui/utils/UIUtils.java index df9e295ac..1092cc93a 100644 --- a/src/main/java/com/github/manolo8/darkbot/gui/utils/UIUtils.java +++ b/src/main/java/com/github/manolo8/darkbot/gui/utils/UIUtils.java @@ -18,6 +18,7 @@ public class UIUtils { RED = Color.decode("#6E2B28"), RED_HIGHLIGHT = new Color(0x40FF1300, true), GREEN_HIGHLIGHT = new Color(0x1A00FF00, true), + BLUE_HIGHLIGHT = new Color(0xFF0088DD, true), BACKGROUND = UIManager.getColor("Viewport.background"), // Normal background of things BORDER = UIManager.getColor("Component.borderColor"), // Normal border of things TAB_HIGLIGHT = UIManager.getColor("TabbedPane.underlineColor"); diff --git a/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/BorderHighlightPainter.java b/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/BorderHighlightPainter.java new file mode 100644 index 000000000..bc97a46d8 --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/BorderHighlightPainter.java @@ -0,0 +1,39 @@ +package com.github.manolo8.darkbot.gui.utils.highlight; + +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultHighlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.Position; +import javax.swing.text.View; +import java.awt.*; + +/** + * Simple highlight painter that draws a border around the highlight + */ +public class BorderHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { + + public BorderHighlightPainter(Color color) { + super(color); + } + + public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds, JTextComponent c, View view) { + g.setColor(getColor()); + + Rectangle r; + if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) { + // Contained in view, can just use bounds. + r = bounds instanceof Rectangle ? (Rectangle) bounds : bounds.getBounds(); + } else { + try { + Shape shape = view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds); + r = shape instanceof Rectangle ? (Rectangle) shape : shape.getBounds(); + } catch (BadLocationException e) { + r = null; + } + } + + if (r != null) g.drawRect(r.x, r.y, r.width - 1, r.height - 1); + return r; + } + +} diff --git a/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/HighlightHandler.java b/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/HighlightHandler.java new file mode 100644 index 000000000..95a51fd0a --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/HighlightHandler.java @@ -0,0 +1,49 @@ +package com.github.manolo8.darkbot.gui.utils.highlight; + +import javax.swing.text.BadLocationException; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.Position; + +public class HighlightHandler { + + private final JTextComponent text; + private final Highlighter.HighlightPainter painter; + private Object highlight; + + public HighlightHandler(JTextComponent text, Highlighter.HighlightPainter painter) { + this.text = text; + this.painter = painter; + } + + public void setHighlight(Locatable node) { + int start = -1, end = -1; + if (node != null) { + Position startPos = node.getStart(); + if (startPos != null) start = startPos.getOffset(); + + Position endPos = node.getEnd(); + if (node.getEnd() != null) end = endPos.getOffset(); + } + setHighlight(start, end); + } + + public void setHighlight(int start, int end) { + if (start != -1 && end != -1) { + try { + if (highlight == null) highlight = text.getHighlighter().addHighlight(start, end, painter); + else text.getHighlighter().changeHighlight(highlight, start, end); + return; + } catch (BadLocationException ignored) {} + } + remove(); + } + + public void remove() { + if (highlight != null) { + text.getHighlighter().removeHighlight(highlight); + highlight = null; + } + } + +} diff --git a/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/Locatable.java b/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/Locatable.java new file mode 100644 index 000000000..4492ca603 --- /dev/null +++ b/src/main/java/com/github/manolo8/darkbot/gui/utils/highlight/Locatable.java @@ -0,0 +1,8 @@ +package com.github.manolo8.darkbot.gui.utils.highlight; + +import javax.swing.text.Position; + +public interface Locatable { + Position getStart(); + Position getEnd(); +} From 8ef518aa55ab3fc9cd3e40d92243a9436bb92fa4 Mon Sep 17 00:00:00 2001 From: Pablo Herrera Date: Mon, 11 Sep 2023 03:41:13 +0200 Subject: [PATCH 2/2] Refactor all parsing to use parsing node --- .../darkbot/config/actions/Parser.java | 4 +- .../config/actions/SyntaxException.java | 65 ++++++++++++------ .../actions/conditions/AbstractCondition.java | 19 ++---- .../actions/conditions/AfterCondition.java | 17 ++--- .../actions/conditions/EqualCondition.java | 16 ++--- .../conditions/HasEffectCondition.java | 25 +++---- .../conditions/HasFormationCondition.java | 25 +++---- .../conditions/NumericalCondition.java | 31 +++------ .../conditions/TargetTypeCondition.java | 27 +++----- .../config/actions/parser/ParseResult.java | 11 +--- .../config/actions/parser/ParseUtil.java | 31 --------- .../config/actions/parser/ValueParser.java | 66 +++++++++++-------- .../darkbot/config/actions/parser/Values.java | 9 +-- .../actions/tree/ConditionEditorPanel.java | 2 +- .../config/actions/tree/DocumentReader.java | 5 ++ .../config/actions/tree/ParsingNode.java | 37 ++++++++++- .../actions/values/BooleanConstant.java | 16 ++--- .../actions/values/HealthTypeValue.java | 25 +++---- .../actions/values/LocationConstant.java | 16 ++--- .../config/actions/values/MapConstant.java | 20 +++--- .../config/actions/values/NumberConstant.java | 19 ++---- .../actions/values/PercentConstant.java | 16 ++--- .../config/actions/values/StatTypeValue.java | 37 ++++------- .../config/actions/values/StringConstant.java | 18 ++--- .../gui/tree/editors/ConditionEditor.java | 4 +- .../gui/utils/highlight/Locatable.java | 21 ++++++ 26 files changed, 270 insertions(+), 312 deletions(-) delete mode 100644 src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseUtil.java diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/Parser.java b/src/main/java/com/github/manolo8/darkbot/config/actions/Parser.java index 30224a758..9c8de277f 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/Parser.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/Parser.java @@ -1,5 +1,7 @@ package com.github.manolo8.darkbot.config.actions; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; + public interface Parser { - String parse(String str) throws SyntaxException; + void parse(ParsingNode node) throws SyntaxException; } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java b/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java index 5fd5506db..5ceee56de 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/SyntaxException.java @@ -1,31 +1,69 @@ package com.github.manolo8.darkbot.config.actions; import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.gui.utils.highlight.Locatable; +import lombok.Getter; +import lombok.experimental.Delegate; import org.jetbrains.annotations.NotNull; +import javax.swing.text.Position; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; -public class SyntaxException extends RuntimeException { +public class SyntaxException extends RuntimeException implements Locatable { private static final String[] EMPTY = new String[]{}; - private final String at; - private final int atIdx; + @Delegate + private final Locatable loc; private final String[] chars; private final List> metadatas; + @Getter private boolean singleMeta = false; + public SyntaxException(String message, Locatable loc, String... chars) { + super(message); + this.loc = loc; + this.chars = chars == null ? EMPTY : chars; + this.metadatas = Collections.emptyList(); + } + + public SyntaxException(String message, Locatable loc, Values.Meta meta, String... chars) { + this(message, loc, meta == null ? null : Collections.singletonList(meta), chars); + singleMeta = true; + } + + public SyntaxException(String message, Locatable loc, List> metas, String... chars) { + super(message); + this.loc = loc; + this.chars = chars == null ? EMPTY : chars; + this.metadatas = metas == null ? Collections.emptyList() : metas; + } + + public > SyntaxException(String message, Locatable loc, Class metadatas) { + this(message, loc, (List>) null, Arrays.stream(metadatas.getEnumConstants()) + .map(Objects::toString).toArray(String[]::new)); + } + public SyntaxException(String message, String at) { this(message, at, (List>) null, (String[]) null); } public SyntaxException(String message, int atIdx, String... chars) { super(message); - this.at = ""; - this.atIdx = atIdx; + this.loc = new Locatable() { + @Override + public Position getStart() { + return () -> atIdx; + } + + @Override + public Position getEnd() { + return null; + } + }; this.chars = chars == null ? EMPTY : chars; this.metadatas = Collections.emptyList(); } @@ -42,20 +80,13 @@ public SyntaxException(String message, String at, Values.Meta meta, String... public SyntaxException(String message, String at, List> metas, String... chars) { super(message); - this.at = at == null ? "" : at; - this.atIdx = -1; + this.loc = null; this.chars = chars == null ? EMPTY : chars; this.metadatas = metas == null ? Collections.emptyList() : metas; } - public String getAt() { - return at; - } - - public int getIdx(String originalString) { - if (atIdx != -1) return atIdx; - - return Math.max(0, originalString.lastIndexOf(at)); + public int getAt() { + return loc.getStart().getOffset(); } public @NotNull String[] getExpected() { @@ -66,8 +97,4 @@ public int getIdx(String originalString) { return metadatas; } - public boolean isSingleMeta() { - return singleMeta; - } - } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java index a0345346b..d96eb69ea 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AbstractCondition.java @@ -4,9 +4,8 @@ import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; @@ -51,18 +50,10 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - boolean hasNext; - do { - ParseResult pr = ValueParser.parse(str, Result.class); - - children.add(pr.asCondition(str, getClass())); - str = pr.leftover.trim(); - - hasNext = !str.isEmpty() && str.charAt(0) == ','; - str = ParseUtil.separate(str, getClass(), ",", ")"); - } while (hasNext); - return str; + public void parse(ParsingNode node) throws SyntaxException { + for (ParsingNode child : node.getChildren()) { + children.add(ValueParser.parseCondition(child)); + } } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java index f3247d3d2..365958f5c 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/AfterCondition.java @@ -4,9 +4,8 @@ import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.config.actions.values.NumberConstant; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; @@ -36,16 +35,10 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); - time = (long) (NumberConstant.parseNumber(params[0], str, getClass()).doubleValue() * 1000); - - str = ParseUtil.separate(params, getClass(), ","); - - ParseResult pr = ValueParser.parse(str, Result.class); - condition = pr.asCondition(str, getClass()); - - return ParseUtil.separate(pr.leftover.trim(), getClass(), ")"); + time = (long) (NumberConstant.parseNumber(node.getParam(0), getClass()).doubleValue() * 1000); + condition = ValueParser.parseCondition(node.getParam(1)); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java index 0240fba04..c67c475e6 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/EqualCondition.java @@ -6,8 +6,8 @@ import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.utils.ReflectionUtils; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; @@ -51,18 +51,14 @@ private boolean isComparable(Object a, Object b) { } @Override - public String parse(String str) throws SyntaxException { - ParseResult prA = ValueParser.parse(str); - a = prA.value; - - str = ParseUtil.separate(prA.leftover.trim(), getClass(), ","); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); - ParseResult prB = ValueParser.parse(str, prA.type); - b = prB.value; + ParseResult prA = ValueParser.parseGeneric(node.getParam(0)); + a = prA.value; + b = ValueParser.parse(node.getParam(1), prA.type); isComparable = null; - - return ParseUtil.separate(prB.leftover.trim(), getClass(), ")"); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java index 1946170cc..412b13267 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasEffectCondition.java @@ -5,9 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.core.entities.Ship; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; @@ -74,11 +73,12 @@ public String toString() { return name().toLowerCase(Locale.ROOT).replace("_", "-"); } - public static Effect of(String operation) { + public static Effect of(ParsingNode node) { + String effect = node.getString(); for (Effect ef : Effect.values()) { - if (ef.toString().equals(operation)) return ef; + if (ef.toString().equals(effect)) return ef; } - return null; + throw new SyntaxException("Unknown effect: '" + effect + "'", node, Effect.class); } } @@ -89,17 +89,10 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); - effect = Effect.of(params[0].trim()); - if (effect == null) - throw new SyntaxException("Unknown effect: '" + params[0] + "'", str, Effect.class); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); - str = ParseUtil.separate(params, getClass(), ","); - - ParseResult pr = ValueParser.parse(str, Ship.class); - ship = pr.value; - - return ParseUtil.separate(pr.leftover.trim(), getClass(), ")"); + effect = Effect.of(node.getParam(0)); + ship = ValueParser.parse(node.getParam(1), Ship.class); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java index 4d364e2c0..b7f496fff 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/HasFormationCondition.java @@ -5,9 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.core.entities.Ship; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; @@ -60,11 +59,12 @@ public String toString() { return name().toLowerCase(Locale.ROOT).replace("_", "-"); } - public static Formation of(String operation) { + public static Formation of(ParsingNode node) { + String formation = node.getString(); for (Formation f : Formation.values()) { - if (f.toString().equals(operation)) return f; + if (f.toString().equals(formation)) return f; } - return null; + throw new SyntaxException("Unknown formation: '" + formation + "'", node, Formation.class); } } @@ -74,17 +74,10 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); - formation = Formation.of(params[0].trim()); - if (formation == null) - throw new SyntaxException("Unknown formation: '" + params[0] + "'", str, Formation.class); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); - str = ParseUtil.separate(params, getClass(), ","); - - ParseResult pr = ValueParser.parse(str, Ship.class); - ship = pr.value; - - return ParseUtil.separate(pr.leftover.trim(), getClass(), ")"); + formation = Formation.of(node.getParam(0)); + ship = ValueParser.parse(node.getParam(1), Ship.class); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java index 27f8a2d1f..922c9864d 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/NumericalCondition.java @@ -5,9 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @@ -50,11 +49,12 @@ public String toString() { return display; } - public static Operation of(String operation) { + public static Operation of(ParsingNode node) { + String operation = node.getString(); for (Operation op : Operation.values()) { if (op.toString().equals(operation)) return op; } - return null; + throw new SyntaxException("Unknown operation '" + operation + "'", node, Operation.class); } } @@ -65,24 +65,11 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - ParseResult prA = ValueParser.parse(str, Number.class); - a = prA.value; - str = prA.leftover.trim(); - if (str.startsWith(",")) str = str.substring(1).trim(); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(3, getClass()); - int chars = Math.min(str.length(), str.length() > 1 && str.charAt(1) == '=' ? 2 : 1); - - String op = str.substring(0, chars); - operation = Operation.of(op); - if (operation == null) - throw new SyntaxException("Unknown operation '" + op + "'", str, Operation.class); - - str = str.substring(chars).trim(); - if (str.startsWith(",")) str = str.substring(1).trim(); - - ParseResult prB = ValueParser.parse(str, Number.class); - b = prB.value; - return ParseUtil.separate(prB.leftover.trim(), getClass(), ")"); + a = ValueParser.parse(node.getParam(0), Number.class); + operation = Operation.of(node.getParam(1)); + a = ValueParser.parse(node.getParam(2), Number.class); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java index c21d58a34..3a3723311 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/conditions/TargetTypeCondition.java @@ -5,9 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.core.entities.Ship; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; @@ -62,11 +61,12 @@ public String toString() { return name().toLowerCase(Locale.ROOT).replace("_", "-"); } - public static TargetType of(String targetType) { + public static TargetType of(ParsingNode node) { + String targetType = node.getString(); for (TargetType tt : TargetType.values()) { if (tt.toString().equals(targetType)) return tt; } - return null; + throw new SyntaxException("Unknown type: '" + targetType + "'", node, TargetType.class); } } @@ -76,20 +76,9 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); - - type = TargetType.of(params[0].trim()); - - if (type == null) { - throw new SyntaxException("Unknown has-relation: '" + params[0] + "'", str, TargetType.class); - } - - str = ParseUtil.separate(params, getClass(), ","); - - ParseResult pr = ValueParser.parse(str, Ship.class); - ship = pr.value; - - return ParseUtil.separate(pr.leftover.trim(), getClass(), ")"); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); + type = TargetType.of(node.getParam(0)); + ship = ValueParser.parse(node.getParam(1), Ship.class); } } \ No newline at end of file diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java index 024e2055c..8a8bcbd8f 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseResult.java @@ -1,23 +1,14 @@ package com.github.manolo8.darkbot.config.actions.parser; -import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; -import eu.darkbot.api.config.types.Condition; public class ParseResult { public final Value value; public final Class type; - public final String leftover; - public ParseResult(Value value, Class type, String leftover) { + public ParseResult(Value value, Class type) { this.value = value; this.type = type; - this.leftover = leftover; } - public > Condition asCondition(String original, Class caller) throws SyntaxException { - if (!(value instanceof Condition)) - throw new SyntaxException("Error: Expected boolean condition", original, Values.getMeta(caller)); - return (Condition) value; - } } \ No newline at end of file diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseUtil.java b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseUtil.java deleted file mode 100644 index c9719aeb9..000000000 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ParseUtil.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.manolo8.darkbot.config.actions.parser; - -import com.github.manolo8.darkbot.config.actions.SyntaxException; -import com.github.manolo8.darkbot.config.actions.Value; - -import java.util.Arrays; - -public class ParseUtil { - - public static String separate(String[] parts, Class> val, String... expected) throws SyntaxException { - return separate(parts, Values.getMeta(val), expected); - } - - public static String separate(String[] parts, Values.Meta meta, String... expected) throws SyntaxException { - if (parts.length != 2) - throw new SyntaxException("Missing separator in '" + meta.getName() + "'", "", meta, expected); - return parts[1].trim(); - } - - public static String separate(String str, Class> val, String... expected) throws SyntaxException{ - return separate(str, Values.getMeta(val), expected); - } - - public static String separate(String str, Values.Meta meta, String... expected) throws SyntaxException{ - String match = Arrays.stream(expected).filter(str::startsWith).findFirst().orElse(null); - if (str.isEmpty() || match == null) - throw new SyntaxException("Missing separator in '" + meta.getName() + "'", str, meta, expected); - return str.substring(match.length()).trim(); - } - -} diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java index fff18493d..35bb2560a 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/ValueParser.java @@ -1,53 +1,67 @@ package com.github.manolo8.darkbot.config.actions.parser; -import com.github.manolo8.darkbot.config.actions.Condition; import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; +import com.github.manolo8.darkbot.config.actions.tree.DocumentReader; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; +import com.github.manolo8.darkbot.gui.utils.highlight.Locatable; import com.github.manolo8.darkbot.utils.ReflectionUtils; +import eu.darkbot.api.config.types.Condition; + +import javax.swing.text.Document; public class ValueParser { - @SuppressWarnings("deprecation") - public static Condition parseCondition(String str) throws SyntaxException { - ParseResult result = parse(str, Condition.Result.class); - if (!result.leftover.trim().isEmpty()) - throw new SyntaxException("Unused characters after end", result.leftover); - return (Condition) result.value; + public static Condition parseCondition(String str) { + return parseCondition(new DocumentReader(str)); } - public static Value parseValue(String str) throws SyntaxException { - ParseResult result = parse(str); - if (!result.leftover.trim().isEmpty()) - throw new SyntaxException("Unused characters after end", result.leftover); - return result.value; + public static Condition parseCondition(Document document) { + return parseCondition(new DocumentReader(document)); } - public static ParseResult parse(String str) throws SyntaxException { - return parse(str, Object.class); + public static Condition parseCondition(DocumentReader reader) throws SyntaxException { + reader.reset(); + ParsingNode root = new ParsingNode(reader); + return parseCondition(root); } - public static ParseResult parse(String str, Class type) throws SyntaxException { - String[] parts = str.trim().split(" *\\( *", 2); + public static Condition parseCondition(ParsingNode node) { + // Technically it's a Value that is implemented by api Condition. + return (Condition) parseImpl(node, com.github.manolo8.darkbot.config.actions.Condition.Result.class).value; + } - Values.Meta vm = Values.getMeta(parts[0].trim(), str, type); + public static Value parse(ParsingNode node, Class type) { + return parseImpl(node, type).value; + } + + public static ParseResult parseGeneric(ParsingNode node) { + return parseImpl(node, Object.class); + } - str = ParseUtil.separate(parts, vm, "("); + private static ParseResult parseImpl(ParsingNode node, Class type) { + Values.Meta vm = Values.getMeta(node, type); Value val = ReflectionUtils.createInstance(vm.clazz); - if (val instanceof Parser) str = ((Parser) val).parse(str); + if (val instanceof Parser) ((Parser) val).parse(node); else { - for (Values.Param param : vm.params) { - ParseResult pr = parse(str, param.type); - ReflectionUtils.set(param.field, val, pr.value); - - str = ParseUtil.separate(pr.leftover.trim(), vm, param == vm.params[vm.params.length - 1] ? ")" : ","); + if (vm.params.length > node.getChildCount()) { + int p = node.getEnd().getOffset() - 1; + throw new SyntaxException("Missing separator in '" + node.getFunction() + "'", Locatable.of(p, p), vm, ","); + } else if (vm.params.length < node.getChildCount()) { + throw new SyntaxException("Too many parameters, expected " + vm.params.length, Locatable.of( + node.getChildAt(vm.params.length).getStart().getOffset(), + node.getChildAt(node.getChildCount() - 1).getEnd().getOffset()), vm); } - if (vm.params.length == 0) str = ParseUtil.separate(str, vm, ")"); + int idx = 0; + for (Values.Param param : vm.params) { + ReflectionUtils.set(param.field, val, parseImpl(node.getParam(idx++), param.type).value); + } } - return new ParseResult<>(val, vm.type, str); + return new ParseResult<>(val, vm.type); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/Values.java b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/Values.java index b811fd802..e6ff68599 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/parser/Values.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/parser/Values.java @@ -15,6 +15,7 @@ import com.github.manolo8.darkbot.config.actions.conditions.OneCondition; import com.github.manolo8.darkbot.config.actions.conditions.TargetTypeCondition; import com.github.manolo8.darkbot.config.actions.conditions.UntilCondition; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.config.actions.values.BooleanConstant; import com.github.manolo8.darkbot.config.actions.values.DistanceValue; import com.github.manolo8.darkbot.config.actions.values.HealthTypeValue; @@ -96,18 +97,18 @@ public static > Meta getMeta(Class type) throws SyntaxE throw new SyntaxException("Error: failed to find value meta for " + type.getSimpleName(), null); } - public static Meta getMeta(String name, String ex, Class type) throws SyntaxException { + public static Meta getMeta(ParsingNode node, Class type) throws SyntaxException { //noinspection unchecked - Meta meta = (Meta) VALUES.get(name); + Meta meta = (Meta) VALUES.get(node.getFunction()); List> params = VALUES.values().stream() .filter(v -> type.isAssignableFrom(v.type)) .collect(Collectors.toList()); if (meta == null) - throw new SyntaxException("Unknown value type '" + name + "'", ex, params); + throw new SyntaxException("Unknown value type '" + node.getFunction() + "'", node, params); if (!type.isAssignableFrom(meta.type)) - throw new SyntaxException("Invalid type, expected '" + type.getSimpleName() + "'", ex, params); + throw new SyntaxException("Invalid type, expected '" + type.getSimpleName() + "'", node, params); return meta; } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java index 9965bc5c3..8d08341a5 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ConditionEditorPanel.java @@ -48,7 +48,7 @@ public ConditionEditorPanel(String initialText) { root.parse(reader); errorHighlight.remove(); } catch (SyntaxException ex) { - errorHighlight.setHighlight(ex.getIdx(""), textArea.getDocument().getLength()); + errorHighlight.setHighlight(ex.getAt(), textArea.getDocument().getLength()); } treeModel.updateListeners(); diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java index 583bbb1d5..120d63e63 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/DocumentReader.java @@ -63,6 +63,11 @@ public String readText() { return remaining; } + public void done() { + if (nextIdx < document.getLength()) + throw new SyntaxException("Unused characters after end", nextIdx); + } + public char peekNext() { if (nextIdx >= document.getLength()) return '\04'; // EOF return charAt(nextIdx); diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java index 81d08cdba..320333072 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/tree/ParsingNode.java @@ -1,6 +1,8 @@ package com.github.manolo8.darkbot.config.actions.tree; import com.github.manolo8.darkbot.config.actions.SyntaxException; +import com.github.manolo8.darkbot.config.actions.Value; +import com.github.manolo8.darkbot.config.actions.parser.Values; import com.github.manolo8.darkbot.gui.utils.highlight.Locatable; import lombok.AccessLevel; import lombok.Getter; @@ -26,7 +28,14 @@ public class ParsingNode implements TreeNode, Locatable { private int totalChildren; public ParsingNode() { - this(null); + parent = null; + } + + public ParsingNode(DocumentReader reader) { + this(); + reader.reset(); + parse(reader); + reader.done(); } private ParsingNode(ParsingNode parent) { @@ -86,7 +95,7 @@ public String toString() { .collect(Collectors.joining(", ", "(", ")"))); } - private static String escape(String val) { + public static String escape(String val) { for (char c : val.toCharArray()) { if (c == '(' || c == ',' || c == ')' || c == ' ') return '"' + val.replaceAll("([\\\\\"])", "\\$1") + '"'; @@ -105,6 +114,28 @@ public ParsingNode getAtPosition(int min, int max) { return this; } + public void requireParamSize(int size, Class> type) { + if ((children == null ? -1 : children.size()) != size) { + String curr = children == null ? "none" : String.valueOf(getChildCount()); + String expected = size == -1 ? "none" : String.valueOf(size); + throw new SyntaxException("Invalid parameter count, found " + curr + " but expected " + expected, this, Values.getMeta(type)); + } + } + + public ParsingNode getParam(int idx) { + if (idx >= getChildCount()) throw new SyntaxException("Missing parameter #" + (idx + 1), this); + return children.get(idx); + } + + public String getParamStr(int idx) { + return getParam(idx).getString(); + } + + public String getString() { + if (children != null) throw new SyntaxException("Expected no parameters", this); + return function; + } + @Override public ParsingNode getChildAt(int childIndex) { return children.get(childIndex); @@ -112,7 +143,7 @@ public ParsingNode getChildAt(int childIndex) { @Override public int getChildCount() { - return children.size(); + return children == null ? 0 : children.size(); } @Override diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java index ed12a400e..83c942368 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/BooleanConstant.java @@ -4,8 +4,8 @@ import com.github.manolo8.darkbot.config.actions.Parser; import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import eu.darkbot.api.PluginAPI; import eu.darkbot.api.config.types.Condition; import org.jetbrains.annotations.NotNull; @@ -21,11 +21,9 @@ public class BooleanConstant implements LegacyCondition, Parser { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split("\\)", 2); - - value = parse(params[0].trim(), str); - return ParseUtil.separate(params, getClass(), ")"); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(1, getClass()); + value = parseBoolean(node.getParam(0)); } @Override @@ -33,13 +31,13 @@ public String toString() { return "boolean(" + value + ")"; } - private Boolean parse(String bool, String ex) throws SyntaxException { - switch (bool) { + private Boolean parseBoolean(ParsingNode node) throws SyntaxException { + switch (node.getString()) { case "null": return null; case "true": return true; case "false": return false; default: - throw new SyntaxException("Failed to parse boolean '" + bool + "'", ex, Values.getMeta(getClass()), "true", "false"); + throw new SyntaxException("Failed to parse boolean '" + node.getString() + "'", node, Values.getMeta(getClass()), "true", "false"); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/HealthTypeValue.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/HealthTypeValue.java index 72577ac94..bcf761cf0 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/HealthTypeValue.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/HealthTypeValue.java @@ -5,10 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseResult; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.ValueParser; -import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.core.objects.itf.HealthHolder; import java.util.Locale; @@ -46,11 +44,12 @@ public String toString() { return name().toLowerCase(Locale.ROOT).replace("_", "-"); } - public static HealthType of(String healthType) { + public static HealthType of(ParsingNode node) { + String healthType = node.getString(); for (HealthType ht : HealthType.values()) { if (ht.toString().equals(healthType)) return ht; } - return null; + throw new SyntaxException("Unknown hp-type: '" + healthType + "'", node, HealthType.class); } } @@ -60,17 +59,9 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); - healthType = HealthType.of(params[0].trim()); - if (healthType == null) - throw new SyntaxException("Unknown hp-type: '" + params[0] + "'", str, HealthType.class); - - str = ParseUtil.separate(params, getClass(), ","); - - ParseResult pr = ValueParser.parse(str, HealthHolder.class); - health = pr.value; - - return ParseUtil.separate(pr.leftover.trim(), getClass(), ")"); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); + healthType = HealthType.of(node.getParam(0)); + health = ValueParser.parse(node.getParam(1), HealthHolder.class); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/LocationConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/LocationConstant.java index ed2e1d565..006186bc0 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/LocationConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/LocationConstant.java @@ -5,8 +5,7 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; -import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.core.utils.Location; import org.jetbrains.annotations.Nullable; @@ -26,17 +25,12 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); - - double x = NumberConstant.parseNumber(params[0], str, getClass()).doubleValue(); - - params = (str = ParseUtil.separate(params, getClass(), ",")).split("\\)", 2); - double y = NumberConstant.parseNumber(params[0], str, getClass()).doubleValue(); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); + double x = NumberConstant.parseNumber(node.getParam(0), getClass()).doubleValue(); + double y = NumberConstant.parseNumber(node.getParam(1), getClass()).doubleValue(); location = new Location(x, y); - - return ParseUtil.separate(params, getClass(), ")"); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/MapConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/MapConstant.java index 2b10a6bdf..0e79fdeed 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/MapConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/MapConstant.java @@ -5,8 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import com.github.manolo8.darkbot.core.manager.HeroManager; import com.github.manolo8.darkbot.core.manager.StarManager; import com.github.manolo8.darkbot.core.objects.Map; @@ -35,28 +35,26 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split("\\)", 2); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(1, getClass()); + String name = node.getParamStr(0); - params[0] = params[0].trim().toLowerCase(Locale.ROOT); map = StarManager.getAllMaps().stream() .filter(m -> m.id > 0) - .filter(m -> m.name.equalsIgnoreCase(params[0]) || - (m.shortName != null && m.shortName.equalsIgnoreCase(params[0]))) + .filter(m -> m.name.equalsIgnoreCase(name) || + (m.shortName != null && m.shortName.equalsIgnoreCase(name))) .findFirst().orElse(null); if (map == null) - throw new SyntaxException("Unknown map: '" + params[0] + "'", str, Values.getMeta(getClass()), + throw new SyntaxException("Unknown map: '" + name + "'", node.getParam(0), Values.getMeta(getClass()), StarManager.getAllMaps().stream() .filter(m -> m.id > 0) - .filter(m -> HeroManager.instance.map == m || (params[0].isEmpty() ? + .filter(m -> HeroManager.instance.map == m || (name.isEmpty() ? ACCESSIBLE_MAPS.contains(m.name) : - m.name.toLowerCase(Locale.ROOT).contains(params[0]))) + m.name.toLowerCase(Locale.ROOT).contains(name))) .map(m -> m.shortName != null ? m.shortName : m.name) .distinct() .toArray(String[]::new)); - - return ParseUtil.separate(params, getClass(), ")"); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/NumberConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/NumberConstant.java index be38de54d..6e93ed6bd 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/NumberConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/NumberConstant.java @@ -5,8 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -39,21 +39,16 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split("\\)", 2); - - number = parseNumber(params[0], str, getClass()); - return ParseUtil.separate(params, getClass(), ")"); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(1, getClass()); + number = parseNumber(node.getParam(0), getClass()); } - public static Number parseNumber(String val, String str, Class> type) throws SyntaxException { - if (val.isEmpty()) - throw new SyntaxException("Empty number, add digits", str, Values.getMeta(type)); - + public static Number parseNumber(ParsingNode node, Class> type) throws SyntaxException { try { - return NUMBER_FORMAT.parse(val); + return NUMBER_FORMAT.parse(node.getString()); } catch (ParseException e) { - throw new SyntaxException("Failed to parse number '" + val + "'", str, Values.getMeta(type)); + throw new SyntaxException("Failed to parse number '" + node.getFunction() + "'", node, Values.getMeta(type)); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/PercentConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/PercentConstant.java index faa789e24..4045a3e95 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/PercentConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/PercentConstant.java @@ -5,8 +5,8 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; import com.github.manolo8.darkbot.config.actions.parser.Values; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; @ValueData(name = "percent", description = "Creates a percent constant", example = "percent(5)") public class PercentConstant implements Value, Parser { @@ -24,18 +24,14 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split("\\)", 2); - - if (params[0].isEmpty()) - throw new SyntaxException("Empty percent, add digits", str, Values.getMeta(getClass())); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(1, getClass()); + ParsingNode val = node.getParam(0); try { - percent = Double.parseDouble(params[0]) / 100; + percent = Double.parseDouble(val.getString()) / 100; } catch (NumberFormatException e) { - throw new SyntaxException("Failed to parse percent '" + params[0] + "'", str, Values.getMeta(getClass())); + throw new SyntaxException("Failed to parse percent '" + val.getFunction() + "'", val, Values.getMeta(getClass())); } - - return ParseUtil.separate(params, getClass(), ")"); } } diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/StatTypeValue.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/StatTypeValue.java index 9c6d3165e..0ed5b5096 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/StatTypeValue.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/StatTypeValue.java @@ -5,7 +5,7 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; import eu.darkbot.api.game.stats.Stats; import eu.darkbot.api.managers.StatsAPI; import org.jetbrains.annotations.Nullable; @@ -35,11 +35,12 @@ public class StatTypeValue implements Value, Parser { } } - private Stats.General getKeyFromString(String key) { + private Stats.General getKeyFromString(ParsingNode node) { + String key = node.getString(); for (Stats.General stat : Stats.General.values()) { if (stat.name().equalsIgnoreCase(key)) return stat; } - return null; + throw new SyntaxException("Unknown stat-type: '" + key + "'", node, Stats.General.class); } public enum StatData { @@ -54,11 +55,12 @@ public String toString() { return name().toLowerCase(Locale.ROOT); } - public static StatData of(String sd) { - for (StatData statData : StatData.values()) { - if (statData.toString().equalsIgnoreCase(sd)) return statData; + public static StatData of(ParsingNode node) { + String statData = node.getString(); + for (StatData sd : StatData.values()) { + if (sd.toString().equalsIgnoreCase(statData)) return sd; } - return null; + throw new SyntaxException("Unknown stat data: '" + statData + "'", node, StatData.class); } } @@ -68,25 +70,12 @@ public String toString() { } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split(" *, *", 2); + public void parse(ParsingNode node) throws SyntaxException { + node.requireParamSize(2, getClass()); - key = getKeyFromString(params[0].trim()); + key = getKeyFromString(node.getParam(0)); + dataType = StatData.of(node.getParam(1)); stat = null; - - if (key == null) { - throw new SyntaxException("Unknown stat-type: '" + params[0] + "'", str, Stats.General.class); - } - - params = ParseUtil.separate(params, getClass(), ",").split("\\)", 2); - - dataType = StatData.of(params[0].trim()); - - if (dataType == null) { - throw new SyntaxException("Unknown data-type: '" + params[0] + "'", params[0], StatData.class); - } - - return ParseUtil.separate(params, getClass(), ")"); } } \ No newline at end of file diff --git a/src/main/java/com/github/manolo8/darkbot/config/actions/values/StringConstant.java b/src/main/java/com/github/manolo8/darkbot/config/actions/values/StringConstant.java index 4d52c5ee7..109efc707 100644 --- a/src/main/java/com/github/manolo8/darkbot/config/actions/values/StringConstant.java +++ b/src/main/java/com/github/manolo8/darkbot/config/actions/values/StringConstant.java @@ -5,9 +5,9 @@ import com.github.manolo8.darkbot.config.actions.SyntaxException; import com.github.manolo8.darkbot.config.actions.Value; import com.github.manolo8.darkbot.config.actions.ValueData; -import com.github.manolo8.darkbot.config.actions.parser.ParseUtil; +import com.github.manolo8.darkbot.config.actions.tree.ParsingNode; -@ValueData(name = "string", description = "String constant", example = "string(hello)") +@ValueData(name = "string", description = "String constant", example = "string(\"hello\")") public class StringConstant implements Value, Parser { public String string; @@ -19,19 +19,13 @@ public String get(Main main) { @Override public String toString() { - return "string(" + string.replace(")", "\\)") + ")"; + return "string(" + ParsingNode.escape(string) + ")"; } @Override - public String parse(String str) throws SyntaxException { - String[] params = str.split("(? start; + } + + @Override + public Position getEnd() { + return () -> end; + } + } }