diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java index 8631d732..6a7d1b76 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/LaunchServer.java @@ -18,9 +18,9 @@ import ru.gravit.launchserver.auth.provider.AuthProvider; import ru.gravit.launchserver.auth.provider.RejectAuthProvider; import ru.gravit.launchserver.binary.*; -import ru.gravit.launchserver.command.handler.CommandHandler; -import ru.gravit.launchserver.command.handler.JLineCommandHandler; -import ru.gravit.launchserver.command.handler.StdCommandHandler; +import ru.gravit.utils.command.CommandHandler; +import ru.gravit.utils.command.JLineCommandHandler; +import ru.gravit.utils.command.StdCommandHandler; import ru.gravit.launchserver.config.*; import ru.gravit.launchserver.manangers.*; import ru.gravit.launchserver.manangers.hook.AuthHookManager; @@ -349,12 +349,13 @@ public LaunchServer(Path dir, String[] args) throws IOException, InvalidKeySpecE Class.forName("jline.Terminal"); // JLine2 available - localCommandHandler = new JLineCommandHandler(this); + localCommandHandler = new JLineCommandHandler(); LogHelper.info("JLine2 terminal enabled"); } catch (ClassNotFoundException ignored) { - localCommandHandler = new StdCommandHandler(this, true); + localCommandHandler = new StdCommandHandler(true); LogHelper.warning("JLine2 isn't in classpath, using std"); } + ru.gravit.launchserver.command.handler.CommandHandler.registerCommands(localCommandHandler); commandHandler = localCommandHandler; // Set key pair diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/Command.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/Command.java index a69b023a..22817021 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/Command.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/Command.java @@ -1,28 +1,12 @@ package ru.gravit.launchserver.command; import ru.gravit.launchserver.LaunchServer; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.helper.VerifyHelper; import java.util.UUID; -public abstract class Command { - - protected static String parseUsername(String username) throws CommandException { - try { - return VerifyHelper.verifyUsername(username); - } catch (IllegalArgumentException e) { - throw new CommandException(e.getMessage()); - } - } - - - protected static UUID parseUUID(String s) throws CommandException { - try { - return UUID.fromString(s); - } catch (IllegalArgumentException ignored) { - throw new CommandException(String.format("Invalid UUID: '%s'", s)); - } - } +public abstract class Command extends ru.gravit.utils.command.Command { protected final LaunchServer server; @@ -31,19 +15,4 @@ protected static UUID parseUUID(String s) throws CommandException { protected Command(LaunchServer server) { this.server = server; } - - - public abstract String getArgsDescription(); // " [optional]" - - - public abstract String getUsageDescription(); - - - public abstract void invoke(String... args) throws Exception; - - - protected final void verifyArgs(String[] args, int min) throws CommandException { - if (args.length < min) - throw new CommandException("Command usage: " + getArgsDescription()); - } } diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UUIDToUsernameCommand.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UUIDToUsernameCommand.java index 918a5e7f..75f7e774 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UUIDToUsernameCommand.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UUIDToUsernameCommand.java @@ -2,7 +2,7 @@ import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.helper.LogHelper; import java.io.IOException; diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UsernameToUUIDCommand.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UsernameToUUIDCommand.java index 24d226ac..ac3afaad 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UsernameToUUIDCommand.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/auth/UsernameToUUIDCommand.java @@ -2,7 +2,7 @@ import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.helper.LogHelper; import java.io.IOException; diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/basic/HelpCommand.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/basic/HelpCommand.java index 34dd6282..f4fb699f 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/basic/HelpCommand.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/basic/HelpCommand.java @@ -1,13 +1,13 @@ package ru.gravit.launchserver.command.basic; import ru.gravit.launchserver.LaunchServer; -import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.Command; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.helper.LogHelper; import java.util.Map.Entry; -public final class HelpCommand extends Command { +public final class HelpCommand extends ru.gravit.launchserver.command.Command { private static void printCommand(String name, Command command) { String args = command.getArgsDescription(); LogHelper.subInfo("%s %s - %s", name, args == null ? "[nothing]" : args, command.getUsageDescription()); diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/CommandHandler.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/CommandHandler.java index 1da94327..d92c4b2c 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/CommandHandler.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/CommandHandler.java @@ -2,7 +2,7 @@ import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.CommandException; import ru.gravit.launchserver.command.auth.*; import ru.gravit.launchserver.command.basic.*; import ru.gravit.launchserver.command.dump.DumpEntryCacheCommand; @@ -13,6 +13,7 @@ import ru.gravit.launchserver.command.modules.LoadModuleCommand; import ru.gravit.launchserver.command.modules.ModulesCommand; import ru.gravit.launchserver.command.service.*; +import ru.gravit.utils.helper.CommonHelper; import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.VerifyHelper; @@ -22,191 +23,59 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public abstract class CommandHandler implements Runnable { - private static String[] parse(CharSequence line) throws CommandException { - boolean quoted = false; - boolean wasQuoted = false; - - // Read line char by char - Collection result = new LinkedList<>(); - StringBuilder builder = new StringBuilder(100); - for (int i = 0; i <= line.length(); i++) { - boolean end = i >= line.length(); - char ch = end ? '\0' : line.charAt(i); - - // Maybe we should read next argument? - if (end || !quoted && Character.isWhitespace(ch)) { - if (end && quoted) - throw new CommandException("Quotes wasn't closed"); - - // Empty args are ignored (except if was quoted) - if (wasQuoted || builder.length() > 0) - result.add(builder.toString()); - - // Reset file builder - wasQuoted = false; - builder.setLength(0); - continue; - } - - // Append next char - switch (ch) { - case '"': // "abc"de, "abc""de" also allowed - quoted = !quoted; - wasQuoted = true; - break; - case '\\': // All escapes, including spaces etc - if (i + 1 >= line.length()) - throw new CommandException("Escape character is not specified"); - char next = line.charAt(i + 1); - builder.append(next); - i++; - break; - default: // Default char, simply append - builder.append(ch); - break; - } - } - - // Return result as array - return result.toArray(new String[0]); - } - - private final Map commands = new ConcurrentHashMap<>(32); - - protected CommandHandler(LaunchServer server) { +public abstract class CommandHandler extends ru.gravit.utils.command.CommandHandler { + public static void registerCommands(ru.gravit.utils.command.CommandHandler handler) + { + LaunchServer server = LaunchServer.server; // Register basic commands - registerCommand("help", new HelpCommand(server)); - registerCommand("version", new VersionCommand(server)); - registerCommand("build", new BuildCommand(server)); - registerCommand("stop", new StopCommand(server)); - registerCommand("restart", new RestartCommand(server)); - registerCommand("rebind", new RebindCommand(server)); - registerCommand("debug", new DebugCommand(server)); - registerCommand("clear", new ClearCommand(server)); - registerCommand("gc", new GCCommand(server)); - registerCommand("proguardClean", new ProguardCleanCommand(server)); - registerCommand("proguardDictRegen", new RegenProguardDictCommand(server)); - registerCommand("proguardMappingsRemove", new RemoveMappingsProguardCommand(server)); - registerCommand("logConnections", new LogConnectionsCommand(server)); - registerCommand("loadModule", new LoadModuleCommand(server)); - registerCommand("modules", new ModulesCommand(server)); - registerCommand("test", new TestCommand(server)); + handler.registerCommand("help", new HelpCommand(server)); + handler.registerCommand("version", new VersionCommand(server)); + handler.registerCommand("build", new BuildCommand(server)); + handler.registerCommand("stop", new StopCommand(server)); + handler.registerCommand("restart", new RestartCommand(server)); + handler.registerCommand("rebind", new RebindCommand(server)); + handler.registerCommand("debug", new DebugCommand(server)); + handler.registerCommand("clear", new ClearCommand(server)); + handler.registerCommand("gc", new GCCommand(server)); + handler.registerCommand("proguardClean", new ProguardCleanCommand(server)); + handler.registerCommand("proguardDictRegen", new RegenProguardDictCommand(server)); + handler.registerCommand("proguardMappingsRemove", new RemoveMappingsProguardCommand(server)); + handler.registerCommand("logConnections", new LogConnectionsCommand(server)); + handler.registerCommand("loadModule", new LoadModuleCommand(server)); + handler.registerCommand("modules", new ModulesCommand(server)); + handler.registerCommand("test", new TestCommand(server)); // Register sync commands - registerCommand("indexAsset", new IndexAssetCommand(server)); - registerCommand("unindexAsset", new UnindexAssetCommand(server)); - registerCommand("downloadAsset", new DownloadAssetCommand(server)); - registerCommand("downloadClient", new DownloadClientCommand(server)); - registerCommand("syncBinaries", new SyncBinariesCommand(server)); - registerCommand("syncUpdates", new SyncUpdatesCommand(server)); - registerCommand("syncProfiles", new SyncProfilesCommand(server)); + handler.registerCommand("indexAsset", new IndexAssetCommand(server)); + handler.registerCommand("unindexAsset", new UnindexAssetCommand(server)); + handler.registerCommand("downloadAsset", new DownloadAssetCommand(server)); + handler.registerCommand("downloadClient", new DownloadClientCommand(server)); + handler.registerCommand("syncBinaries", new SyncBinariesCommand(server)); + handler.registerCommand("syncUpdates", new SyncUpdatesCommand(server)); + handler.registerCommand("syncProfiles", new SyncProfilesCommand(server)); // Register auth commands - registerCommand("auth", new AuthCommand(server)); - registerCommand("usernameToUUID", new UsernameToUUIDCommand(server)); - registerCommand("uuidToUsername", new UUIDToUsernameCommand(server)); - registerCommand("ban", new BanCommand(server)); - registerCommand("unban", new UnbanCommand(server)); + handler.registerCommand("auth", new AuthCommand(server)); + handler.registerCommand("usernameToUUID", new UsernameToUUIDCommand(server)); + handler.registerCommand("uuidToUsername", new UUIDToUsernameCommand(server)); + handler.registerCommand("ban", new BanCommand(server)); + handler.registerCommand("unban", new UnbanCommand(server)); //Register dump commands - registerCommand("dumpSessions", new DumpSessionsCommand(server)); - registerCommand("dumpEntryCache", new DumpEntryCacheCommand(server)); + handler.registerCommand("dumpSessions", new DumpSessionsCommand(server)); + handler.registerCommand("dumpEntryCache", new DumpEntryCacheCommand(server)); //Register service commands - registerCommand("reload", new ReloadCommand(server)); - registerCommand("reloadAll", new ReloadAllCommand(server)); - registerCommand("reloadList", new ReloadListCommand(server)); - registerCommand("config", new ConfigCommand(server)); - registerCommand("configHelp", new ConfigHelpCommand(server)); - registerCommand("configList", new ConfigListCommand(server)); - registerCommand("swapAuthProvider", new SwapAuthProviderCommand(server)); - registerCommand("serverStatus", new ServerStatusCommand(server)); - registerCommand("checkInstall", new CheckInstallCommand(server)); - registerCommand("multi", new MultiCommand(server)); - registerCommand("getModulus", new GetModulusCommand(server)); - } - - - public abstract void bell() throws IOException; - - - public abstract void clear() throws IOException; - - - public final Map commandsMap() { - return Collections.unmodifiableMap(commands); - } - - - public final void eval(String line, boolean bell) { - LogHelper.info("Command '%s'", line); - - // Parse line to tokens - String[] args; - try { - args = parse(line); - } catch (Exception e) { - LogHelper.error(e); - return; - } - - // Evaluate command - eval(args, bell); - } - - - public final void eval(String[] args, boolean bell) { - if (args.length == 0) - return; - - // Measure start time and invoke command - Instant startTime = Instant.now(); - try { - lookup(args[0]).invoke(Arrays.copyOfRange(args, 1, args.length)); - } catch (Exception e) { - LogHelper.error(e); - } - - // Bell if invocation took > 1s - Instant endTime = Instant.now(); - if (bell && Duration.between(startTime, endTime).getSeconds() >= 5) - try { - bell(); - } catch (IOException e) { - LogHelper.error(e); - } - } - - - public final Command lookup(String name) throws CommandException { - Command command = commands.get(name); - if (command == null) - throw new CommandException(String.format("Unknown command: '%s'", name)); - return command; - } - - - public abstract String readLine() throws IOException; - - private void readLoop() throws IOException { - for (String line = readLine(); line != null; line = readLine()) - eval(line, true); - } - - - public final void registerCommand(String name, Command command) { - VerifyHelper.verifyIDName(name); - VerifyHelper.putIfAbsent(commands, name, Objects.requireNonNull(command, "command"), - String.format("Command has been already registered: '%s'", name)); - } - - @Override - public final void run() { - try { - readLoop(); - } catch (IOException e) { - LogHelper.error(e); - } + handler.registerCommand("reload", new ReloadCommand(server)); + handler.registerCommand("reloadAll", new ReloadAllCommand(server)); + handler.registerCommand("reloadList", new ReloadListCommand(server)); + handler.registerCommand("config", new ConfigCommand(server)); + handler.registerCommand("configHelp", new ConfigHelpCommand(server)); + handler.registerCommand("configList", new ConfigListCommand(server)); + handler.registerCommand("swapAuthProvider", new SwapAuthProviderCommand(server)); + handler.registerCommand("serverStatus", new ServerStatusCommand(server)); + handler.registerCommand("checkInstall", new CheckInstallCommand(server)); + handler.registerCommand("multi", new MultiCommand(server)); + handler.registerCommand("getModulus", new GetModulusCommand(server)); } } diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/DownloadClientCommand.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/DownloadClientCommand.java index 2d0a5a88..7886f1f3 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/DownloadClientCommand.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/DownloadClientCommand.java @@ -4,7 +4,7 @@ import ru.gravit.launcher.profiles.ClientProfile.Version; import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.HttpDownloader; import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.LogHelper; diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/IndexAssetCommand.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/IndexAssetCommand.java index 4dfd6710..0d220e84 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/IndexAssetCommand.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/IndexAssetCommand.java @@ -4,7 +4,7 @@ import com.google.gson.JsonObject; import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.SecurityHelper; diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/UnindexAssetCommand.java b/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/UnindexAssetCommand.java index 9bb0c797..b74f7c9d 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/UnindexAssetCommand.java +++ b/LaunchServer/src/main/java/ru/gravit/launchserver/command/hash/UnindexAssetCommand.java @@ -5,7 +5,7 @@ import com.google.gson.JsonParser; import ru.gravit.launchserver.LaunchServer; import ru.gravit.launchserver.command.Command; -import ru.gravit.launchserver.command.CommandException; +import ru.gravit.utils.command.CommandException; import ru.gravit.utils.helper.IOHelper; import ru.gravit.utils.helper.LogHelper; diff --git a/libLauncher/build.gradle b/libLauncher/build.gradle index 0e527171..e5db09f7 100644 --- a/libLauncher/build.gradle +++ b/libLauncher/build.gradle @@ -3,5 +3,6 @@ dependencies { compileOnly 'org.fusesource.jansi:jansi:1.17.1' + compileOnly 'jline:jline:2.14.6' compile 'com.google.code.gson:gson:2.8.5' } diff --git a/libLauncher/src/main/java/ru/gravit/utils/command/Command.java b/libLauncher/src/main/java/ru/gravit/utils/command/Command.java new file mode 100644 index 00000000..7e594d50 --- /dev/null +++ b/libLauncher/src/main/java/ru/gravit/utils/command/Command.java @@ -0,0 +1,40 @@ +package ru.gravit.utils.command; + +import ru.gravit.utils.helper.VerifyHelper; + +import java.util.UUID; + +public abstract class Command { + + + protected static String parseUsername(String username) throws CommandException { + try { + return VerifyHelper.verifyUsername(username); + } catch (IllegalArgumentException e) { + throw new CommandException(e.getMessage()); + } + } + + + protected static UUID parseUUID(String s) throws CommandException { + try { + return UUID.fromString(s); + } catch (IllegalArgumentException ignored) { + throw new CommandException(String.format("Invalid UUID: '%s'", s)); + } + } + + public abstract String getArgsDescription(); // " [optional]" + + + public abstract String getUsageDescription(); + + + public abstract void invoke(String... args) throws Exception; + + + protected final void verifyArgs(String[] args, int min) throws CommandException { + if (args.length < min) + throw new CommandException("Command usage: " + getArgsDescription()); + } +} diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/CommandException.java b/libLauncher/src/main/java/ru/gravit/utils/command/CommandException.java similarity index 90% rename from LaunchServer/src/main/java/ru/gravit/launchserver/command/CommandException.java rename to libLauncher/src/main/java/ru/gravit/utils/command/CommandException.java index 514a4571..ce2009b1 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/CommandException.java +++ b/libLauncher/src/main/java/ru/gravit/utils/command/CommandException.java @@ -1,4 +1,4 @@ -package ru.gravit.launchserver.command; +package ru.gravit.utils.command; public final class CommandException extends Exception { private static final long serialVersionUID = -6588814993972117772L; diff --git a/libLauncher/src/main/java/ru/gravit/utils/command/CommandHandler.java b/libLauncher/src/main/java/ru/gravit/utils/command/CommandHandler.java new file mode 100644 index 00000000..5d59e4cc --- /dev/null +++ b/libLauncher/src/main/java/ru/gravit/utils/command/CommandHandler.java @@ -0,0 +1,104 @@ +package ru.gravit.utils.command; + +import ru.gravit.utils.helper.CommonHelper; +import ru.gravit.utils.helper.LogHelper; +import ru.gravit.utils.helper.VerifyHelper; + +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public abstract class CommandHandler implements Runnable { + private final Map commands = new ConcurrentHashMap<>(32); + + public final void eval(String line, boolean bell) { + LogHelper.info("Command '%s'", line); + + // Parse line to tokens + String[] args; + try { + args = CommonHelper.parseCommand(line); + } catch (Exception e) { + LogHelper.error(e); + return; + } + + // Evaluate command + eval(args, bell); + } + + + public final void eval(String[] args, boolean bell) { + if (args.length == 0) + return; + + // Measure start time and invoke command + Instant startTime = Instant.now(); + try { + lookup(args[0]).invoke(Arrays.copyOfRange(args, 1, args.length)); + } catch (Exception e) { + LogHelper.error(e); + } + + // Bell if invocation took > 1s + Instant endTime = Instant.now(); + if (bell && Duration.between(startTime, endTime).getSeconds() >= 5) + try { + bell(); + } catch (IOException e) { + LogHelper.error(e); + } + } + + + public final Command lookup(String name) throws CommandException { + Command command = commands.get(name); + if (command == null) + throw new CommandException(String.format("Unknown command: '%s'", name)); + return command; + } + + + public abstract String readLine() throws IOException; + + private void readLoop() throws IOException { + for (String line = readLine(); line != null; line = readLine()) + eval(line, true); + } + + + public final void registerCommand(String name, Command command) { + VerifyHelper.verifyIDName(name); + VerifyHelper.putIfAbsent(commands, name, Objects.requireNonNull(command, "command"), + String.format("Command has been already registered: '%s'", name)); + } + + @Override + public final void run() { + try { + readLoop(); + } catch (IOException e) { + LogHelper.error(e); + } + } + + + + public abstract void bell() throws IOException; + + + public abstract void clear() throws IOException; + + + public final Map commandsMap() { + return Collections.unmodifiableMap(commands); + } + + + +} diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/JLineCommandHandler.java b/libLauncher/src/main/java/ru/gravit/utils/command/JLineCommandHandler.java similarity index 86% rename from LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/JLineCommandHandler.java rename to libLauncher/src/main/java/ru/gravit/utils/command/JLineCommandHandler.java index 039afe3d..c3909646 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/JLineCommandHandler.java +++ b/libLauncher/src/main/java/ru/gravit/utils/command/JLineCommandHandler.java @@ -1,7 +1,6 @@ -package ru.gravit.launchserver.command.handler; +package ru.gravit.utils.command; import jline.console.ConsoleReader; -import ru.gravit.launchserver.LaunchServer; import ru.gravit.utils.helper.LogHelper; import ru.gravit.utils.helper.LogHelper.Output; @@ -23,8 +22,8 @@ public void println(String message) { private final ConsoleReader reader; - public JLineCommandHandler(LaunchServer server) throws IOException { - super(server); + public JLineCommandHandler() throws IOException { + super(); // Set reader reader = new ConsoleReader(); diff --git a/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/StdCommandHandler.java b/libLauncher/src/main/java/ru/gravit/utils/command/StdCommandHandler.java similarity index 75% rename from LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/StdCommandHandler.java rename to libLauncher/src/main/java/ru/gravit/utils/command/StdCommandHandler.java index 7f77b039..a7ae1a06 100644 --- a/LaunchServer/src/main/java/ru/gravit/launchserver/command/handler/StdCommandHandler.java +++ b/libLauncher/src/main/java/ru/gravit/utils/command/StdCommandHandler.java @@ -1,6 +1,5 @@ -package ru.gravit.launchserver.command.handler; +package ru.gravit.utils.command; -import ru.gravit.launchserver.LaunchServer; import ru.gravit.utils.helper.IOHelper; import java.io.BufferedReader; @@ -9,8 +8,8 @@ public final class StdCommandHandler extends CommandHandler { private final BufferedReader reader; - public StdCommandHandler(LaunchServer server, boolean readCommands) { - super(server); + public StdCommandHandler(boolean readCommands) { + super(); reader = readCommands ? IOHelper.newReader(System.in) : null; } diff --git a/libLauncher/src/main/java/ru/gravit/utils/helper/CommonHelper.java b/libLauncher/src/main/java/ru/gravit/utils/helper/CommonHelper.java index bc7d21f7..5f7548e3 100644 --- a/libLauncher/src/main/java/ru/gravit/utils/helper/CommonHelper.java +++ b/libLauncher/src/main/java/ru/gravit/utils/helper/CommonHelper.java @@ -1,10 +1,13 @@ package ru.gravit.utils.helper; import ru.gravit.launcher.LauncherAPI; +import ru.gravit.utils.command.CommandException; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; +import java.util.Collection; +import java.util.LinkedList; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -68,4 +71,53 @@ public static String replace(String source, String... params) { private CommonHelper() { } + + public static String[] parseCommand(CharSequence line) throws CommandException { + boolean quoted = false; + boolean wasQuoted = false; + + // Read line char by char + Collection result = new LinkedList<>(); + StringBuilder builder = new StringBuilder(100); + for (int i = 0; i <= line.length(); i++) { + boolean end = i >= line.length(); + char ch = end ? '\0' : line.charAt(i); + + // Maybe we should read next argument? + if (end || !quoted && Character.isWhitespace(ch)) { + if (end && quoted) + throw new CommandException("Quotes wasn't closed"); + + // Empty args are ignored (except if was quoted) + if (wasQuoted || builder.length() > 0) + result.add(builder.toString()); + + // Reset file builder + wasQuoted = false; + builder.setLength(0); + continue; + } + + // Append next char + switch (ch) { + case '"': // "abc"de, "abc""de" also allowed + quoted = !quoted; + wasQuoted = true; + break; + case '\\': // All escapes, including spaces etc + if (i + 1 >= line.length()) + throw new CommandException("Escape character is not specified"); + char next = line.charAt(i + 1); + builder.append(next); + i++; + break; + default: // Default char, simply append + builder.append(ch); + break; + } + } + + // Return result as array + return result.toArray(new String[0]); + } }