X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fxdcc%2Fui%2Fstdin%2FCommandReader.java;h=2d0a35292a0f525fc70aba6b274dcf366b063586;hb=57fa2048f94ac1975ed4a417fbc0150dd9277f00;hp=b77ac2cc72279b9e3e6f1ab391cb73435437e3dd;hpb=e77221f564760b9870463d84e241c04a65126466;p=xudocci.git diff --git a/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java b/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java index b77ac2c..2d0a352 100644 --- a/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java +++ b/src/main/java/net/pterodactylus/xdcc/ui/stdin/CommandReader.java @@ -17,28 +17,36 @@ package net.pterodactylus.xdcc.ui.stdin; +import static com.google.common.collect.FluentIterable.from; +import static java.util.Arrays.asList; +import static net.pterodactylus.xdcc.ui.stdin.Ansi.bold; +import static net.pterodactylus.xdcc.ui.stdin.Ansi.green; +import static net.pterodactylus.xdcc.ui.stdin.Ansi.red; +import static net.pterodactylus.xdcc.ui.stdin.Command.TO_NAME; + import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.io.Writer; +import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Set; -import net.pterodactylus.irc.DccReceiver; +import net.pterodactylus.irc.util.MessageCleaner; import net.pterodactylus.xdcc.core.Core; import net.pterodactylus.xdcc.core.event.DownloadFailed; import net.pterodactylus.xdcc.core.event.DownloadFinished; import net.pterodactylus.xdcc.core.event.DownloadStarted; -import net.pterodactylus.xdcc.data.Bot; +import net.pterodactylus.xdcc.core.event.GenericMessage; +import net.pterodactylus.xdcc.core.event.MessageReceived; import net.pterodactylus.xdcc.data.Download; -import net.pterodactylus.xdcc.data.Pack; +import net.pterodactylus.xdcc.util.io.DuplicateLineSuppressingWriter; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import com.google.common.base.Joiner; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; -import com.google.common.primitives.Ints; import com.google.common.util.concurrent.AbstractExecutionThreadService; /** @@ -48,14 +56,15 @@ import com.google.common.util.concurrent.AbstractExecutionThreadService; */ public class CommandReader extends AbstractExecutionThreadService { - /** The core being controlled. */ - private final Core core; + /** The commands to process. */ + private final Collection commands; /** The reader to read commands from. */ private final BufferedReader reader; /** The writer to write the results to. */ - private final Writer writer; + private final DuplicateLineSuppressingWriter writer; + private final Collection failedDownloads; /** * Creates a new command reader. @@ -67,10 +76,24 @@ public class CommandReader extends AbstractExecutionThreadService { * @param writer * The write to write results to */ - public CommandReader(Core core, Reader reader, Writer writer) { - this.core = core; + public CommandReader(Core core, Reader reader, Writer writer, Collection failedDownloads) { this.reader = new BufferedReader(reader); - this.writer = writer; + this.writer = new DuplicateLineSuppressingWriter(writer); + this.failedDownloads = failedDownloads; + + /* initialize commands. */ + ImmutableList.Builder commandBuilder = ImmutableList.builder(); + commandBuilder.add(new ListDownloadsCommand(core)); + commandBuilder.add(new SearchCommand(core)); + commandBuilder.add(new DownloadCommand(core)); + commandBuilder.add(new StatsCommand(core)); + commandBuilder.add(new ListConnectionsCommand(core)); + commandBuilder.add(new AbortDownloadCommand(core)); + commandBuilder.add(new DisconnectCommand(core)); + commandBuilder.add(new FailedDownloadsCommand(failedDownloads)); + commandBuilder.add(new RestartCommand(core, failedDownloads)); + commandBuilder.add(new ResearchCommand(core)); + commands = commandBuilder.build(); } // @@ -81,78 +104,28 @@ public class CommandReader extends AbstractExecutionThreadService { protected void run() throws Exception { String lastLine = ""; String line; - final List lastResult = Lists.newArrayList(); + net.pterodactylus.xdcc.ui.stdin.State state = new net.pterodactylus.xdcc.ui.stdin.State(); while ((line = reader.readLine()) != null) { + line = line.trim(); if (line.equals("")) { line = lastLine; } + writer.reset(); String[] words = line.split(" +"); - if (words[0].equalsIgnoreCase("search")) { - lastResult.clear(); - for (Bot bot : core.bots()) { - for (Pack pack : bot) { - boolean found = true; - for (int wordIndex = 1; wordIndex < words.length; ++wordIndex) { - if (words[wordIndex].startsWith("-") && pack.name().toLowerCase().contains(words[wordIndex].toLowerCase().substring(1))) { - found = false; - break; - } - if (!words[wordIndex].startsWith("-") && !pack.name().toLowerCase().contains(words[wordIndex].toLowerCase())) { - found = false; - break; - } - } - if (found) { - lastResult.add(new Result(bot, pack)); - } - } - } - Collections.sort(lastResult); - int counter = 0; - for (Result result : lastResult) { - writer.write(String.format("[%d] %s (%s) from %s (#%s) on %s\n", counter++, result.pack().name(), result.pack().size(), result.bot().name(), result.pack().id(), result.bot().network().name())); - } - writer.write("End of Search.\n"); - } else if (words[0].equalsIgnoreCase("dcc")) { - int counter = 0; - for (DccReceiver dccReceiver : core.dccReceivers()) { - writer.write(String.format("[%d] %s (%s, ", counter++, dccReceiver.filename(), dccReceiver.size())); - if (dccReceiver.isRunning()) { - writer.write(String.format("%.1f%%, %s", dccReceiver.progress() * 100.0 / dccReceiver.size(), f(dccReceiver.currentRate()))); - } else { - if (dccReceiver.progress() >= dccReceiver.size()) { - writer.write(String.format("complete, %s", f(dccReceiver.overallRate()))); - } else { - writer.write(String.format("aborted at %.1f%%, %s", dccReceiver.progress() * 100.0 / dccReceiver.size(), f(dccReceiver.currentRate()))); - } - } - writer.write("/s)\n"); - } - writer.write("End of DCCs.\n"); - } else if (words[0].equalsIgnoreCase("get")) { - Integer index = Ints.tryParse(words[1]); - if ((index != null) && (index < lastResult.size())) { - core.fetch(lastResult.get(index).bot(), lastResult.get(index).pack()); - } - } else if (words[0].equalsIgnoreCase("stats")) { - int configuredChannelsCount = core.channels().size(); - int joinedChannelsCount = core.joinedChannels().size(); - int extraChannelsCount = core.extraChannels().size(); - Collection bots = core.bots(); - Set packNames = Sets.newHashSet(); - int packsCount = 0; - for (Bot bot : bots) { - packsCount += bot.packs().size(); - for (Pack pack : bot) { - packNames.add(pack.name()); - } - } - - writer.write(String.format("%d channels (%d joined, %d extra), %d bots offering %d packs (%d unique).\n", configuredChannelsCount, joinedChannelsCount, extraChannelsCount, bots.size(), packsCount, packNames.size())); + String commandName = words[0]; + Collection eligibleCommands = findEligibleCommands(commandName); + if (eligibleCommands.isEmpty()) { + writeLine(String.format("Invalid command: %s, valid: %s.", commandName, Joiner.on(' ').join(from(commands).transform(TO_NAME)))); + } else if (eligibleCommands.size() > 1) { + writeLine(String.format("Commands: %s.", Joiner.on(' ').join(from(eligibleCommands).transform(TO_NAME)))); + } else { + Command command = eligibleCommands.iterator().next(); + List parameters = from(asList(words)).skip(1).toList(); + state = command.execute(state, parameters, writer); + writer.flush(); } lastLine = line; - writer.flush(); } } @@ -170,8 +143,7 @@ public class CommandReader extends AbstractExecutionThreadService { public void downloadStarted(DownloadStarted downloadStarted) { Download download = downloadStarted.download(); try { - writer.write(String.format("Download of %s (from %s, %s) has started.\n", download.filename(), download.bot().name(), download.bot().network().name())); - writer.flush(); + writeLine(String.format("Download of %s (from %s, %s) has started.", bold(download.pack().name()), download.bot().name(), download.bot().network().name())); } catch (IOException ioe1) { /* ignore. */ } @@ -186,14 +158,24 @@ public class CommandReader extends AbstractExecutionThreadService { @Subscribe public void downloadFinished(DownloadFinished downloadFinished) { Download download = downloadFinished.download(); + removeFailedDownloads(download.pack().name()); try { - writer.write(String.format("Download of %s (from %s, %s) has finished, at %s/s.\n", download.filename(), download.bot().name(), download.bot().network().name(), f(download.dccReceiver().overallRate()))); - writer.flush(); + writeLine(green(String.format("Download of %s (from %s, %s) has finished, at %s/s.", download.pack().name(), download.bot().name(), download.bot().network().name(), f(download.dccReceiver().overallRate())))); } catch (IOException ioe1) { /* ignore. */ } } + private void removeFailedDownloads(String name) { + List failedDownloadsToRemove = new ArrayList<>(); + for (Download failedDownload : failedDownloads) { + if (failedDownload.pack().name().equals(name)) { + failedDownloadsToRemove.add(failedDownload); + } + } + failedDownloads.removeAll(failedDownloadsToRemove); + } + /** * Called when a download fails. * @@ -203,9 +185,39 @@ public class CommandReader extends AbstractExecutionThreadService { @Subscribe public void downloadFailed(DownloadFailed downloadFailed) { Download download = downloadFailed.download(); + failedDownloads.add(download); + try { + writeLine(red(String.format("Download of %s (from %s, %s) has failed at %.1f%% and %s/s.", download.filename(), download.bot().name(), download.bot().network().name(), download.dccReceiver().progress() * 100.0 / download.dccReceiver().size(), f(download.dccReceiver().overallRate())))); + } catch (IOException ioe1) { + /* ignore. */ + } + } + + /** + * Displays the received message on the console. + * + * @param messageReceived + * The message received event + */ + @Subscribe + public void messageReceived(MessageReceived messageReceived) { + try { + writeLine(String.format("Message from %s: %s", messageReceived.source(), MessageCleaner.getDefaultInstance().clean(messageReceived.message()))); + } catch (IOException e) { + /* ignore. */ + } + } + + /** + * Writes a generic message to the console. + * + * @param genericMessage + * The generic message event + */ + @Subscribe + public void genericMessage(GenericMessage genericMessage) { try { - writer.write(String.format("Download of %s (from %s, %s) has failed at %.1f%% and %s/s.\n", download.filename(), download.bot().name(), download.bot().network().name(), download.dccReceiver().progress() * 100.0 / download.dccReceiver().size(), f(download.dccReceiver().overallRate()))); - writer.flush(); + writeLine(genericMessage.message()); } catch (IOException ioe1) { /* ignore. */ } @@ -215,6 +227,34 @@ public class CommandReader extends AbstractExecutionThreadService { // PRIVATE METHODS // + private Collection findEligibleCommands(String name) { + ImmutableSet.Builder eligibleCommands = ImmutableSet.builder(); + for (Command command : commands) { + if (command.getName().toLowerCase().startsWith(name.toLowerCase())) { + eligibleCommands.add(command); + } + for (String alias : command.getAliases()) { + if (alias.toLowerCase().startsWith(name.toLowerCase())) { + eligibleCommands.add(command); + } + } + } + return eligibleCommands.build(); + } + + /** + * Writes the given line followed by an LF to the {@link #writer}. + * + * @param line + * The line to write + * @throws IOException + * if an I/O error occurs + */ + private void writeLine(String line) throws IOException { + writer.write(line + "\n"); + writer.flush(); + } + /** * Converts large numbers into a human-friendly format, by showing SI prefixes * for ×1024 (K), ×1048576 (M), and ×1073741824 (G). @@ -223,7 +263,7 @@ public class CommandReader extends AbstractExecutionThreadService { * The number to convert * @return The converted number */ - private static String f(long number) { + static String f(long number) { if (number >= (1 << 30)) { return String.format("%.1fG", number / (double) (1 << 30)); } @@ -236,59 +276,21 @@ public class CommandReader extends AbstractExecutionThreadService { return String.format("%dB", number); } - /** Container for result information. */ - private static class Result implements Comparable { - - /** The bot carrying the pack. */ - private final Bot bot; - - /** The pack. */ - private final Pack pack; - - /** - * Creates a new result. - * - * @param bot - * The bot carrying the pack - * @param pack - * The pack - */ - private Result(Bot bot, Pack pack) { - this.bot = bot; - this.pack = pack; - } - - // - // ACCESSORS - // - - /** - * Returns the bot carrying the pack. - * - * @return The bot carrying the pack - */ - public Bot bot() { - return bot; - } - - /** - * Returns the pack. - * - * @return The pack - */ - public Pack pack() { - return pack; + /** + * Formats the given number of seconds into a more easily readable string. + * + * @param seconds + * The number of seconds + * @return The formatted time, or “unknown” if the time is unknown + */ + static String t(Optional seconds) { + if (!seconds.isPresent()) { + return "unknown"; } - - // - // COMPARABLE METHODS - // - - @Override - public int compareTo(Result result) { - return pack().name().compareToIgnoreCase(result.pack().name()); + if (seconds.get() > 3600) { + return String.format("%02d:%02d:%02d", seconds.get() / 3600, (seconds.get() / 60) % 60, seconds.get() % 60); } - + return String.format("%02d:%02d", (seconds.get() / 60) % 60, seconds.get() % 60); } }