From 1b5b8f11d0dd33dc41c5c5a6841307fbb392f4c7 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Sat, 6 Jun 2015 19:23:39 +0200 Subject: Squashed a few minor bugs, refactored CardbaseManager a bit --- .../cardbase/standalone/CardbaseCLI.java | 540 +++++++++++---------- 1 file changed, 290 insertions(+), 250 deletions(-) (limited to 'src/eu/equalparts/cardbase/standalone') diff --git a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java b/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java index 4071fad..e0f5433 100644 --- a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java +++ b/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java @@ -5,7 +5,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.HashMap; +import java.util.Arrays; import java.util.Iterator; import java.util.Scanner; @@ -17,7 +17,6 @@ import eu.equalparts.cardbase.data.Card; import eu.equalparts.cardbase.data.CardbaseManager; import eu.equalparts.cardbase.data.FullCardSet; import eu.equalparts.cardbase.data.CardSet; -import eu.equalparts.cardbase.query.IO; /** * This provides a lightweight CLI for interacting with cardbase files. @@ -32,11 +31,11 @@ public class CardbaseCLI { * @author Eduardo Pedroni */ private enum Action { - + ADD, REMOVE; public Card card; public Integer count; - + /** * Sets both fields at once. * @@ -48,7 +47,7 @@ public class CardbaseCLI { this.count = count; } } - + /** * The last action performed by the user. */ @@ -58,21 +57,13 @@ public class CardbaseCLI { */ private FullCardSet selectedSet = null; /** - * A cache of CardSets to avoid querying the server many times for the same information. - */ - private HashMap setCache = new HashMap(); - /** - * The manager object which provides a façade to the cardbase data structure. - */ - private CardbaseManager cbm; - /** - * Exit flag, program breaks out of the main loop when true. + * The manager object which allows interaction with cardbase data structure. */ - private boolean exit = false; + private CardbaseManager cardbaseManager; /** * Printed to the console when the user enter the help command. */ - private String help = "Not available, check project page."; + private String help = "Not available, check project page on GitHub."; /** * The cardbase file off which we are currently working, if any. */ @@ -82,6 +73,10 @@ public class CardbaseCLI { * if the user tries to exit with unsaved changed. */ private boolean savePrompt = false; + /** + * Exit flag, program breaks out of the main loop when true. + */ + private boolean exit = false; /** * Execute the interface. @@ -90,7 +85,7 @@ public class CardbaseCLI { */ public static void main(String... args) { try { - new CardbaseCLI(args).run(); + new CardbaseCLI(args).startInterface(); } catch (JsonParseException e) { System.out.println("Error: poorly formatted cardbase, check the syntax and try again."); // although the problem could also be with the upstream CardSetList json. @@ -103,23 +98,33 @@ public class CardbaseCLI { e.printStackTrace(); } } - + + /** + * Reads in an optional cardbase JSON and initialises other necessary components. + * This does not actually produce the CLI, for that use {@code startInterface()} + * on the constructed object. + * + * @param args a list of arguments. Only the first argument is used, as a cardbase JSON. + * @throws JsonParseException the file specified does not comply to JSON standards. + * @throws JsonMappingException the file specified cannot be mapped to a {@code Cardbase} object. + * @throws IOException something went wrong with the low-level I/O. + */ private CardbaseCLI(String... args) throws JsonParseException, JsonMappingException, IOException { System.out.println("Welcome to Cardbase CLI!"); // make the CardbaseManager if (args.length > 0) { - cardbaseFile = new File(args[0]); + File cardbaseFile = new File(args[0]); if (cardbaseFile.exists() && cardbaseFile.isFile() && cardbaseFile.canRead()) { System.out.println("Loading cardbase from \"" + args[0] + "\"."); - cbm = new CardbaseManager(cardbaseFile); + cardbaseManager = new CardbaseManager(cardbaseFile); } else { System.out.println(args[0] + " appears to be invalid."); System.exit(0); } } else { System.out.println("No cardbase file was provided, creating a clean cardbase."); - cbm = new CardbaseManager(); + cardbaseManager = new CardbaseManager(); } // load help information @@ -127,18 +132,49 @@ public class CardbaseCLI { if (is != null) { help = new Scanner(is).useDelimiter("\\Z").next(); } else { - System.out.println("Help file was not found, check the project page for help instead."); + System.out.println("Help file was not found, check the project page on GitHub for help instead."); } } - private void run() { + /** + * Read stdin for user input, sanitise and interpret any commands entered. + */ + private void startInterface() { BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); - // the main loop try { + // the main loop while (!exit) { - System.out.print(selectedSet == null ? "> " : selectedSet.code + " > "); - interpret(consoleReader.readLine().trim().toLowerCase().split("[ \t]+")); + // print prompt + System.out.print(selectedSet == null ? "> " : selectedSet.getCode() + " > "); + // condition input and interpret + String[] raw = consoleReader.readLine().trim().toLowerCase().split("[ \t]+"); + String command = raw[0]; + String[] args = Arrays.copyOfRange(raw, 1, raw.length); + + if (command.equalsIgnoreCase("help")) { + help(); + } else if (command.equalsIgnoreCase("write") + || command.equalsIgnoreCase("save")) { + write(args); + } else if (command.equalsIgnoreCase("exit")) { + exit(); + } else if (command.equalsIgnoreCase("sets")) { + sets(); + } else if (command.equalsIgnoreCase("set")) { + set(args); + } else if (command.equalsIgnoreCase("glance")) { + glance(); + } else if (command.equalsIgnoreCase("peruse")) { + peruse(args); + } else if (command.equalsIgnoreCase("undo")) { + undo(); + } else if (command.equalsIgnoreCase("remove") + || command.equalsIgnoreCase("rm")) { + remove(args); + } else { + add(command, args); + } } } catch (IOException e) { System.out.println("Error: something went wrong with stdin, exiting..."); @@ -147,232 +183,233 @@ public class CardbaseCLI { } /** - * Handle console commands appropriately. - * - * @param commands an array of {@code String} containing the arguments in order. + * Print help to console. */ - private void interpret(String[] commands) { - if (commands.length > 0) { - switch (commands[0]) { - /* - * Show help. - */ - case "help": - System.out.println(help); - break; - - /* - * Write current cardbase to file. - */ - case "write": - File outputFile; - if (commands.length > 1) { - outputFile = new File(sanitiseFileName(commands[1])); - } else { - outputFile = cardbaseFile; - } - - if (outputFile != null) { - if (outputFile.exists() && (!outputFile.isFile() || !outputFile.canWrite())) { - System.out.println("Could not write to \"" + outputFile.getAbsolutePath() + "\"."); - return; - } - // handle these exceptions locally - they don't necessarily mean the program should halt. - try { - IO.writeCardbase(outputFile, cbm.cardbase); - // we are now working off outputFile, which may or may not be the same as cardbaseFile at this point - cardbaseFile = outputFile; - System.out.println("Cardbase was saved to \"" + outputFile.getAbsolutePath() + "\". " - + "Subsequent writes will be done to this same file unless otherwise requested."); - savePrompt = false; - } catch (JsonGenerationException | JsonMappingException e) { - System.out.println("Error: something terrible happened to the internal cardbase data structure. Oops."); - e.printStackTrace(); - } catch (IOException e) { - System.out.println("Error: lost contact with the output file, try again?"); - e.printStackTrace(); - } - } else { - System.out.println("Please provide a file name."); - } - break; - - /* - * Exit procedure. - */ - case "exit": - if (savePrompt) { - System.out.println("Don't forget to save. If you really wish to quit without saving, type \"exit\" again."); + public void help() { + System.out.println(help); + } + + /** + * Write current cardbase to file. + * + * @param args optionally the file to which to write. + */ + public void write(String[] args) { + File outputFile; + // user-provided file overrides everything else + if (args.length > 0) { + outputFile = new File(sanitiseFileName(args[0])); + } else { + outputFile = cardbaseFile; + } + + if (outputFile != null) { + if (outputFile.exists() && (!outputFile.isFile() || !outputFile.canWrite())) { + System.out.println("Could not write to \"" + outputFile.getAbsolutePath() + "\"."); + } else { + // handle these exceptions locally - they don't necessarily mean the program should exit + try { + cardbaseManager.writeCardbase(outputFile); + // we are now working off outputFile, which may or may not be the same as cardbaseFile at this point + cardbaseFile = outputFile; + System.out.println("Cardbase was saved to \"" + outputFile.getAbsolutePath() + "\". " + + "Subsequent writes will be done to this same file unless otherwise requested."); savePrompt = false; - } else { - exit = true; - } - break; - - /* - * Print a list of valid set codes. - */ - case "sets": - for (CardSet set : cbm.getCardSetList()) { - // MetaCardSet has an overridden toString(). - System.out.println(set); - } - break; - - /* - * Select a set. - */ - case "set": - // first check if the set code is valid - for (CardSet set : cbm.getCardSetList()) { - if (set.code.equalsIgnoreCase(commands[1])) { - // if the set is already cached, use that - if (setCache.containsKey(set.code)) { - selectedSet = setCache.get(set.code); - } else { - // if not, download it and cache it - try { - selectedSet = IO.getCardSet(set.code); - setCache.put(set.code, selectedSet); - } catch (JsonParseException e) { - System.out.println("Error: JSON fetched from upstream was not formatted properly."); - e.printStackTrace(); - return; - } catch (JsonMappingException e) { - System.out.println("Error: JSON fetched from upstream does not match the data structure used internally."); - e.printStackTrace(); - return; - } catch (IOException e) { - System.out.println("Error: JSON could not be fetched from upstream."); - e.printStackTrace(); - return; - } - } - System.out.println("Selected set: " + set.name + "."); - // undoing is not allowed if the set is changed - it would get tricky - lastAction = null; - return; - } - } - System.out.println("\"" + commands[1] + "\" does not correspond to any set (use \"sets\" to see all valid set codes)."); - break; - - /* - * Print a brief list of the whole cardbase. - */ - case "glance": - Card current; - int total = 0; - for (Iterator i = cbm.cardIterator(); i.hasNext();) { - current = i.next(); - printGlance(current); - total += current.count; + } catch (JsonGenerationException | JsonMappingException e) { + System.out.println("Error: something terrible happened to the internal cardbase data structure. Oops."); + e.printStackTrace(); + } catch (IOException e) { + System.out.println("Error: lost contact with the output file, try again?"); + e.printStackTrace(); } - System.out.println("Total: " + total); - break; - - /* - * Print detailed information of a single card or the whole cardbase. - */ - case "peruse": - // if a card is specified, peruse only that - if (commands.length > 1) { - if (selectedSet != null) { - Card card = cbm.getCard(selectedSet.code, commands[1]); - if (card != null) { - printPerusal(card); - } else { - System.out.println("Card not in cardbase."); - } - } else { - System.out.println("Please select a set before perusing a specific card."); - } + } + } else { + System.out.println("Please provide a file name."); + } + } + + /** + * Exit procedure. + */ + public void exit() { + if (savePrompt) { + System.out.println("Don't forget to save. If you really wish to quit without saving, type \"exit\" again."); + savePrompt = false; + } else { + exit = true; + } + } + + /** + * Print a list of valid set codes. + */ + public void sets() { + for (CardSet set : cardbaseManager.getCardSetList()) { + // CardSet has an overridden toString(). + System.out.println(set); + } + } + + /** + * Select a set. + * + * @param args the code of the chosen set. + */ + public void set(String[] args) { + if (args.length > 0) { + try { + selectedSet = cardbaseManager.getFullCardSet(args[0]); + // if the set code is invalid, null is returned + if (selectedSet != null) { + System.out.println("Selected set: " + selectedSet.getName() + "."); + // undoing is not allowed if the set is changed - it would get tricky + lastAction = null; } else { - // peruse all cards in cardbase - for (Iterator i = cbm.cardIterator(); i.hasNext();) { - printPerusal(i.next()); - } + System.out.println("\"" + args[0] + "\" does not correspond to any set (use \"sets\" to see all valid set codes)."); } - break; - - /* - * Undo previous action. - */ - case "undo": - if (lastAction != null) { - if (lastAction == Action.ADD) { - remove(lastAction.card, lastAction.count); - } else if (lastAction == Action.REMOVE) { - add(lastAction.card, lastAction.count); - } - // can only undo once - lastAction = null; + } catch (JsonParseException e) { + System.out.println("Error: JSON fetched from upstream was not formatted properly."); + e.printStackTrace(); + return; + } catch (JsonMappingException e) { + System.out.println("Error: JSON fetched from upstream does not match the data structure used internally."); + e.printStackTrace(); + return; + } catch (IOException e) { + System.out.println("Error: JSON could not be fetched from upstream."); + e.printStackTrace(); + return; + } + } else { + System.out.println("Please enter a set code (use \"sets\" to see all valid set codes)."); + } + } + + /** + * Print a brief list of the whole cardbase. + */ + public void glance() { + Card current; + int total = 0; + for (Iterator i = cardbaseManager.cardIterator(); i.hasNext();) { + current = i.next(); + printGlance(current); + total += current.count; + } + System.out.println("Total: " + total); + } + + /** + * Print detailed information of a single card or the whole cardbase. + * + * @param args optionally a card within the set (by number) to peruse. + */ + public void peruse(String[] args) { + // if a card is specified, peruse only that + if (args.length > 0) { + if (selectedSet != null) { + Card card = cardbaseManager.getCard(selectedSet.getCode(), args[0]); + if (card != null) { + printPerusal(card); } else { - System.out.println("Nothing to undo."); + System.out.println("Card not in cardbase."); } - break; - - /* - * Remove one or more of a card. - */ - case "remove": - if (selectedSet != null) { - if (commands.length > 1) { - Card cardToRemove = selectedSet.getCardByNumber(commands[1]); - if (cardToRemove != null) { - Integer count = 1; - if (commands.length > 2 && commands[2].matches("[0-9]+")) { - count = Integer.valueOf(commands[2]); - if (count <= 0) { - System.out.println("Can't remove " + count + " cards."); - return; - } - } - remove(cardToRemove, count); - } else { - System.out.println(commands[1] + " does not correspond to a card in " + selectedSet.name + "."); + } else { + System.out.println("Please select a set before perusing a specific card."); + } + } else { + // peruse all cards in cardbase + Card current; + int total = 0; + for (Iterator i = cardbaseManager.cardIterator(); i.hasNext();) { + current = i.next(); + printPerusal(current); + total += current.count; + } + System.out.println("Total: " + total); + } + } + + /** + * Undo previous action. + */ + public void undo() { + if (lastAction != null) { + if (lastAction == Action.ADD) { + removeCard(lastAction.card, lastAction.count); + } else if (lastAction == Action.REMOVE) { + addCard(lastAction.card, lastAction.count); + } + // can only undo once + lastAction = null; + } else { + System.out.println("Nothing to undo."); + } + } + + /** + * Remove one or more of a card. + * + * @param args the set number of the card to remove and optionally the count to be removed. + */ + public void remove(String[] args) { + if (selectedSet != null) { + if (args.length > 0) { + Card cardToRemove = selectedSet.getCardByNumber(args[0]); + if (cardToRemove != null) { + Integer count = 1; + if (args.length > 1 && args[1].matches("[0-9]+")) { + count = Integer.valueOf(args[1]); + if (count <= 0) { + System.out.println("Can't remove " + count + " cards."); + return; } - } else { - System.out.println("Please specify a card number to remove."); } + removeCard(cardToRemove, count); } else { - System.out.println("Select a set before removing cards."); + System.out.println(args[0] + " does not correspond to a card in " + selectedSet.getName() + "."); } - break; - - /* - * Add one or more of a card. - */ - default: - if (selectedSet != null) { - // a blank line after adding a card repeats the addition unlimitedly - if (commands.length == 1 && commands[0].isEmpty()) { - if (lastAction == Action.ADD) - add(lastAction.card, lastAction.count); - } else { - Card cardToAdd = selectedSet.getCardByNumber(commands[0]); - if (cardToAdd != null) { - Integer count = 1; - if (commands.length > 1 && commands[1].matches("[0-9]+")) { - count = Integer.valueOf(commands[1]); - if (count <= 0) { - System.out.println("Can't add " + count + " cards."); - return; - } - } - add(cardToAdd, count); - } else { - System.out.println(commands[0] + " does not correspond to a card in " + selectedSet.name + "."); + } else { + System.out.println("Please specify a card number to remove."); + } + } else { + System.out.println("Select a set before removing cards."); + } + } + + /** + * Add one or more of a card. + * + * @param number the number of the card to add. + * @param args optionally the count to add. + */ + public void add(String number, String[] args) { + if (selectedSet != null) { + // a blank line after adding a card repeats the addition unlimitedly + if (number.isEmpty()) { + if (lastAction == Action.ADD) + addCard(lastAction.card, lastAction.count); + } else { + Card cardToAdd = selectedSet.getCardByNumber(number); + if (cardToAdd != null) { + Integer count = 1; + if (args.length > 0 && args[0].matches("[0-9]+")) { + count = Integer.valueOf(args[0]); + if (count <= 0) { + System.out.println("Can't add " + count + " cards."); + return; } } + addCard(cardToAdd, count); } else { - System.out.println("Select a set before adding cards."); + System.out.println(number + " does not correspond to a card in " + selectedSet.getName() + "."); } - break; } + } else { + System.out.println("Select a set before adding cards."); } } - + /** * Add the specified count of the specified card * to the cardbase. @@ -380,16 +417,14 @@ public class CardbaseCLI { * @param card the card to add. * @param count the number of times to add it. */ - private void add(Card card, Integer count) { - // MTG JSON does not contain this information, but it is useful for sorting - card.setCode = selectedSet.code; - cbm.addCard(card, count); + private void addCard(Card card, Integer count) { + cardbaseManager.addCard(card, count); System.out.println("Added " + count + "x " + card.name + "."); savePrompt = true; lastAction = Action.ADD; lastAction.set(card, count); } - + /** * Remove the specified count of the specified card * from the cardbase. @@ -397,14 +432,19 @@ public class CardbaseCLI { * @param card the card to remove. * @param count the number of times to remove it. */ - private void remove(Card card, Integer count) { - cbm.removeCard(card, count); - System.out.println("Removed " + count + "x " + card.name + "."); - savePrompt = true; - lastAction = Action.REMOVE; - lastAction.set(card, count); + private void removeCard(Card card, Integer count) { + Integer removed = cardbaseManager.removeCard(card, count); + if (removed > 0) { + System.out.println("Removed " + removed + "x " + card.name + "."); + savePrompt = true; + lastAction = Action.REMOVE; + lastAction.set(card, removed); + } else { + System.out.println(card.name + " is not in the cardbase."); + } + } - + /** * Return a {@code String} that is guaranteed to be a legal file name. * @@ -420,7 +460,7 @@ public class CardbaseCLI { } return name; } - + /** * Prints a glance of the specified card. A glance contains simply * the card count, name, set code and set number. @@ -430,7 +470,7 @@ public class CardbaseCLI { private void printGlance(Card card) { System.out.println(String.format("%1$-4d %2$s (%3$s, %4$s)", card.count, card.name, card.setCode, card.number)); } - + /** * Prints a perusal of the specified card. A perusal contains more * information than a glance, but not every single field the card has @@ -444,10 +484,10 @@ public class CardbaseCLI { if (card.manaCost != null) System.out.println("\tCost: " + card.manaCost); if (card.power != null && card.toughness != null) System.out.println("\t" + card.power + "/" + card.toughness); if (card.loyalty != null) System.out.println("\tLoyalty: " + card.loyalty); - + if (card.text != null) System.out.println("\t" + card.text.replaceAll("\n", "\n\t")); if (card.flavor != null) System.out.println("\t" + card.flavor.replaceAll("\n", "\n\t")); - + if (card.rarity != null) System.out.println("\t" + card.rarity); if (card.multiverseid != null) System.out.println("\tMID: " + card.multiverseid); if (card.artist != null) System.out.println("\tIllus. " + card.artist); -- cgit v1.2.3