Add custom args parser to allow spaces in arguments

Fixes #96 where worlds with spaces could not be
used with chunkmaster.

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/109/head
trivernis 4 years ago
parent f15dc646f6
commit aad4143e8a
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -1,6 +1,7 @@
package net.trivernis.chunkmaster.commands package net.trivernis.chunkmaster.commands
import net.trivernis.chunkmaster.Chunkmaster import net.trivernis.chunkmaster.Chunkmaster
import net.trivernis.chunkmaster.lib.ArgParser
import net.trivernis.chunkmaster.lib.Subcommand import net.trivernis.chunkmaster.lib.Subcommand
import org.bukkit.Server import org.bukkit.Server
import org.bukkit.command.Command import org.bukkit.command.Command
@ -11,6 +12,7 @@ import org.bukkit.command.TabCompleter
class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val server: Server) : CommandExecutor, class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val server: Server) : CommandExecutor,
TabCompleter { TabCompleter {
private val commands = HashMap<String, Subcommand>() private val commands = HashMap<String, Subcommand>()
private val argParser = ArgParser()
init { init {
registerCommands() registerCommands()
@ -36,7 +38,9 @@ class CommandChunkmaster(private val chunkmaster: Chunkmaster, private val serve
/** /**
* /chunkmaster command to handle all commands * /chunkmaster command to handle all commands
*/ */
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean { override fun onCommand(sender: CommandSender, command: Command, label: String, bukkitArgs: Array<out String>): Boolean {
val args = argParser.parseArguments(bukkitArgs.joinToString(" "))
if (args.isNotEmpty()) { if (args.isNotEmpty()) {
if (sender.hasPermission("chunkmaster.${args[0].toLowerCase()}")) { if (sender.hasPermission("chunkmaster.${args[0].toLowerCase()}")) {
return if (commands.containsKey(args[0])) { return if (commands.containsKey(args[0])) {

@ -0,0 +1,77 @@
package net.trivernis.chunkmaster.lib
/**
* Better argument parser for command arguments
*/
class ArgParser {
private var input = ""
private var position = 0
private var currentChar = ' '
/**
* Parses arguments from a string and respects quotes
*/
fun parseArguments(arguments: String): List<String> {
if (arguments.isEmpty()) {
return emptyList()
}
input = arguments
position = 0
currentChar = input[position]
val args = ArrayList<String>()
var arg = ""
while (!endReached()) {
nextCharacter()
if (currentChar.isWhitespace()) {
if (arg.isNotBlank()) {
args.add(arg)
}
arg = ""
} else if (currentChar == '"') {
if (arg.isNotBlank()) {
args.add(arg)
}
arg = parseString()
if (arg.isNotBlank()) {
args.add(arg)
}
arg = ""
} else {
arg += currentChar
}
}
if (arg.isNotBlank()) {
args.add(arg)
}
return args
}
/**
* Parses an enquoted string
*/
private fun parseString(): String {
var output = ""
while (!endReached()) {
nextCharacter()
if (currentChar == '"') {
break
}
output += currentChar
}
return output
}
private fun nextCharacter() {
if (!endReached()) {
currentChar = input[position++]
}
}
private fun endReached(): Boolean {
return position >= input.length
}
}

@ -0,0 +1,49 @@
package net.trivernis.chunkmaster.lib
import io.kotest.matchers.shouldBe
import org.junit.Test
class ArgParserTest {
var argParser = ArgParser()
@Test
fun `it parses arguments`() {
argParser.parseArguments("first second third forth").shouldBe(listOf("first", "second", "third", "forth"))
}
@Test
fun `it parses quoted arguments as one argument`() {
argParser.parseArguments("first \"second with space\" third").shouldBe(listOf("first", "second with space", "third"))
argParser.parseArguments("\"first\" \"second\" \"third\"").shouldBe(listOf("first", "second", "third"))
}
@Test
fun `it parses single arguments`() {
argParser.parseArguments("one").shouldBe(listOf("one"))
argParser.parseArguments("\"one\"").shouldBe(listOf("one"))
}
@Test
fun `it parses no arguments`() {
argParser.parseArguments("").shouldBe(emptyList())
}
@Test
fun `it parses just whitespace as no arguments`() {
argParser.parseArguments(" ").shouldBe(emptyList())
argParser.parseArguments("\t\t").shouldBe(emptyList())
}
@Test
fun `it parses arguments with weird whitespace`() {
argParser.parseArguments(" first second \t third \n forth ").shouldBe(listOf("first", "second", "third", "forth"))
}
@Test
fun `it deals predictable with malformed input`() {
argParser.parseArguments("first \"second third fourth").shouldBe(listOf("first", "second third fourth"))
argParser.parseArguments("\"first second \"third\" fourth").shouldBe(listOf("first second ", "third", " fourth"))
argParser.parseArguments("first second third fourth\"").shouldBe(listOf("first", "second", "third", "fourth"))
argParser.parseArguments("\"").shouldBe(emptyList())
}
}

@ -1,8 +1,8 @@
package net.trivernis.chunkmaster package net.trivernis.chunkmaster.lib
import io.kotest.matchers.string.shouldNotBeEmpty import io.kotest.matchers.string.shouldNotBeEmpty
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import net.trivernis.chunkmaster.lib.LanguageManager import net.trivernis.chunkmaster.Chunkmaster
import org.bukkit.configuration.file.FileConfiguration import org.bukkit.configuration.file.FileConfiguration
import org.junit.Test import org.junit.Test
Loading…
Cancel
Save