diff --git a/eco-api/src/main/java/com/willfp/eco/core/items/Items.java b/eco-api/src/main/java/com/willfp/eco/core/items/Items.java index 15998a8e..6a762323 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/items/Items.java +++ b/eco-api/src/main/java/com/willfp/eco/core/items/Items.java @@ -22,8 +22,6 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -45,11 +43,6 @@ public final class Items { */ private static final List ARG_PARSERS = new ArrayList<>(); - /** - * Regex for quotes in args. - */ - private static final Pattern QUOTE_REGEX = Pattern.compile("([^\"]\\S*|\".+?\")\\s*"); - /** * Register a new custom item. * @@ -125,22 +118,7 @@ public final class Items { return new EmptyTestableItem(); } - - List argBuilder = new ArrayList<>(); - Matcher matcher = QUOTE_REGEX.matcher(key); - while (matcher.find()) { - argBuilder.add(matcher.group(1)); - } - - argBuilder.replaceAll(string -> { - if (string.startsWith("\"") && string.endsWith("\"")) { - return string.substring(1, string.length() - 1); - } else { - return string; - } - }); - - String[] args = argBuilder.toArray(new String[0]); + String[] args = parseLookupString(key); if (args.length == 0) { return new EmptyTestableItem(); @@ -259,6 +237,54 @@ public final class Items { } } + /** + * Parse lookup string into arguments. + *

+ * Handles quoted strings for names. + * + * @param lookup The lookup string. + * @return An array of arguments to be processed. + * @author Shawn (https://stackoverflow.com/questions/70606170/split-a-list-on-spaces-and-group-quoted-characters/70606653#70606653) + */ + @NotNull + public static String[] parseLookupString(@NotNull final String lookup) { + char[] chars = lookup.toCharArray(); + List arguments = new ArrayList<>(); + StringBuilder argumentBuilder = new StringBuilder(); + for (int i = 0; i < chars.length; i++) { + if (chars[i] == ' ') { + /* + Take the current value of the argument builder, append it to the + list of found arguments, and then clear it for the next argument. + */ + arguments.add(argumentBuilder.toString()); + argumentBuilder.setLength(0); + } else if (chars[i] == '"') { + /* + Work until the next unescaped quote to handle quotes with + spaces in them - assumes the input string is well-formatted + */ + for (i++; chars[i] != '"'; i++) { + /* + If the found quote is escaped, ignore it in the parsing + */ + if (chars[i] == '\\') { + i++; + } + argumentBuilder.append(chars[i]); + } + } else { + /* + If it's a regular character, just append it to the current argument. + */ + argumentBuilder.append(chars[i]); + } + } + arguments.add(argumentBuilder.toString()); // Adds the last argument to the arguments. + return arguments.toArray(new String[0]); + } + + /** * Get a Testable Item from an ItemStack. *

diff --git a/eco-api/src/test/java/ItemsTest.java b/eco-api/src/test/java/ItemsTest.java new file mode 100644 index 00000000..e0f48a61 --- /dev/null +++ b/eco-api/src/test/java/ItemsTest.java @@ -0,0 +1,17 @@ +import com.willfp.eco.core.items.Items; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ItemsTest { + @Test + public void testParserLookupStrings() { + Assertions.assertArrayEquals( + Items.parseLookupString("diamond_sword name:\"test name\""), + new String[]{"diamond_sword", "name:test name"} + ); + Assertions.assertArrayEquals( + Items.parseLookupString("stick 1 name:\"The \\\"Holy\\\" Stick\""), + new String[]{"stick", "1", "name:The \"Holy\" Stick"} + ); + } +}