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 --- src/eu/equalparts/cardbase/data/CardSet.java | 27 +- .../equalparts/cardbase/data/CardbaseManager.java | 68 ++- src/eu/equalparts/cardbase/data/Deck.java | 3 - src/eu/equalparts/cardbase/data/FullCardSet.java | 46 +- src/eu/equalparts/cardbase/io/IO.java | 119 +++++ src/eu/equalparts/cardbase/query/IO.java | 113 ----- .../cardbase/standalone/CardbaseCLI.java | 540 +++++++++++---------- 7 files changed, 533 insertions(+), 383 deletions(-) create mode 100644 src/eu/equalparts/cardbase/io/IO.java delete mode 100644 src/eu/equalparts/cardbase/query/IO.java (limited to 'src/eu/equalparts') diff --git a/src/eu/equalparts/cardbase/data/CardSet.java b/src/eu/equalparts/cardbase/data/CardSet.java index 959e0a3..b9720bf 100644 --- a/src/eu/equalparts/cardbase/data/CardSet.java +++ b/src/eu/equalparts/cardbase/data/CardSet.java @@ -2,10 +2,31 @@ package eu.equalparts.cardbase.data; public class CardSet { - public String name = ""; - public String code = ""; - public String releaseDate = ""; + private String name = ""; + private String code = ""; + private String releaseDate = ""; + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the code + */ + public String getCode() { + return code; + } + + /** + * @return the releaseDate + */ + public String getReleaseDate() { + return releaseDate; + } + @Override public String toString() { return String.format("%1$-12s : %2$s", code, name, releaseDate); diff --git a/src/eu/equalparts/cardbase/data/CardbaseManager.java b/src/eu/equalparts/cardbase/data/CardbaseManager.java index e6a4f97..dfef3c8 100644 --- a/src/eu/equalparts/cardbase/data/CardbaseManager.java +++ b/src/eu/equalparts/cardbase/data/CardbaseManager.java @@ -2,18 +2,29 @@ package eu.equalparts.cardbase.data; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; -import eu.equalparts.cardbase.query.IO; +import eu.equalparts.cardbase.io.IO; public class CardbaseManager { - + private ArrayList cardSets; - public Cardbase cardbase; + /** + * A cache of CardSets to avoid querying the server many times for the same information. + */ + private HashMap cardSetCache = new HashMap(); + /** + * + */ + private Cardbase cardbase; + + /** * Parse a cardbase file and create an associated Cardbase object. * @@ -39,11 +50,45 @@ public class CardbaseManager { cardSets = IO.getCardSetList(); cardbase = new Cardbase(); } - + public ArrayList getCardSetList() { return cardSets; } + + public void writeCardbase(File outputFile) throws JsonGenerationException, JsonMappingException, IOException { + IO.writeCardbase(outputFile, cardbase); + } + /** + * Returns the specified set in the form of a {@code FullCardSet} object. + * + * @param code the code of the set to be returned. + * @return the requested {@code FullCardSet} or null if no set matches the given code. + * + * @throws JsonParseException if the upstream JSON is not formatted correctly. + * @throws JsonMappingException if the upstream JSON does not map to {@code FullCardSet}. + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public FullCardSet getFullCardSet(String code) throws JsonParseException, JsonMappingException, IOException { + FullCardSet requestedSet = null; + for (CardSet cardSet : cardSets) { + if (cardSet.getCode().equalsIgnoreCase(code)) { + // if the set is cached, no need to fetch + if (cardSetCache.containsKey(cardSet.getCode())) { + requestedSet = cardSetCache.get(cardSet.getCode()); + } + // not cached; fetch, cache and return it + else { + requestedSet = IO.getFullCardSet(cardSet.getCode()); + cardSetCache.put(cardSet.getCode(), requestedSet); + } + return requestedSet; + } + } + // not found + return null; + } + /** * Add a specific amount of a card to the cardbase. * If the card is not already in the cardbase, it is added. @@ -70,20 +115,25 @@ public class CardbaseManager { * If that amount is equal to or exceeds the count already in the cardbase, * the card entry is removed altogether. * - * @param remove + * @param cardToRemove * @param count + * @return the number of cards actually removed. */ - public void removeCard(Card remove, Integer count) { - Card card = cardbase.getCardByNumber(remove.setCode, remove.number); + public Integer removeCard(Card cardToRemove, Integer count) { + Card card = cardbase.getCardByNumber(cardToRemove.setCode, cardToRemove.number); + Integer removed = 0; if (card != null) { if (card.count <= count) { cardbase.cards.remove(card); + removed = card.count; } else { card.count -= count; + removed = count; } - } + } + return removed; } - + /** * @return an iterator to the cards in the cardbase. */ diff --git a/src/eu/equalparts/cardbase/data/Deck.java b/src/eu/equalparts/cardbase/data/Deck.java index b5fe402..8a9ed12 100644 --- a/src/eu/equalparts/cardbase/data/Deck.java +++ b/src/eu/equalparts/cardbase/data/Deck.java @@ -9,7 +9,4 @@ public class Deck { */ public HashMap cards; - public Deck() { - - } } diff --git a/src/eu/equalparts/cardbase/data/FullCardSet.java b/src/eu/equalparts/cardbase/data/FullCardSet.java index 0b5c099..48488b5 100644 --- a/src/eu/equalparts/cardbase/data/FullCardSet.java +++ b/src/eu/equalparts/cardbase/data/FullCardSet.java @@ -3,11 +3,47 @@ package eu.equalparts.cardbase.data; import java.util.ArrayList; public class FullCardSet extends CardSet { - public String border; - public String type; - public String block; - public String gathererCode; - public ArrayList cards; + + private String border; + private String type; + private String block; + private String gathererCode; + private ArrayList cards; + + /** + * @return the border + */ + public String getBorder() { + return border; + } + + /** + * @return the type + */ + public String getType() { + return type; + } + + /** + * @return the block + */ + public String getBlock() { + return block; + } + + /** + * @return the gathererCode + */ + public String getGathererCode() { + return gathererCode; + } + + /** + * @return the cards + */ + public ArrayList getCards() { + return cards; + } /** * Searches for a card by number (the one shown on the card itself). diff --git a/src/eu/equalparts/cardbase/io/IO.java b/src/eu/equalparts/cardbase/io/IO.java new file mode 100644 index 0000000..2275913 --- /dev/null +++ b/src/eu/equalparts/cardbase/io/IO.java @@ -0,0 +1,119 @@ +package eu.equalparts.cardbase.io; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import eu.equalparts.cardbase.data.Card; +import eu.equalparts.cardbase.data.FullCardSet; +import eu.equalparts.cardbase.data.Cardbase; +import eu.equalparts.cardbase.data.CardSet; + +/** + * Class responsible for all I/O operations, such as fetching content from remote servers, reading from + * and writing to files. + *
+ * All relevant methods here are static, this class should not be instantiated. + * + * @author Eduardo Pedroni + */ +public class IO { + + /** + * The base URL from where the information is fetched. + */ + private static final String BASE_URL = "http://mtgjson.com/json/"; + /** + * The URL where the complete list of sets is fetched. + */ + private static final String SETS_URL = BASE_URL + "SetList.json"; + /** + * The Jackson ObjectMapper which parses fetched JSON files. + */ + private static final ObjectMapper mapper = createMapper(); + + /** + * Private constructor, this class is not to be instantiated. + */ + private IO() {} + + /** + * Instantiate and configure Jackson mapper statically. + * + * @return the {@code ObjectMapper}, ready to use. + */ + private static ObjectMapper createMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + // TODO decide what to do about this + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return objectMapper; + } + + /** + * Fetches a complete set by code, where the code is a short string determined by WotC. + * The full list of valid codes can be acquired with {@code IO.getCardSetList()}. + * + * @param setCode the code of the set to be fetched. + * @return the complete specified set in a {@code FullCardSet} object. + * + * @throws JsonParseException if underlying input contains invalid content of type JsonParser supports (JSON for default case). + * @throws JsonMappingException if the input JSON structure does not match structure expected for result type (or has other mismatch issues). + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public static FullCardSet getFullCardSet(String setCode) throws JsonParseException, JsonMappingException, IOException { + FullCardSet fullCardSet = mapper.readValue(new URL(BASE_URL + setCode + ".json"), FullCardSet.class); + // MTG JSON does not include set code in the card information, but it is useful for sorting + for (Card card : fullCardSet.getCards()) { + card.setCode = setCode; + } + return fullCardSet; + } + + /** + * @return a list of all card sets in the form of {@code CardSet} objects. + * + * @throws JsonParseException if underlying input contains invalid content of type JsonParser supports (JSON for default case). + * @throws JsonMappingException if the input JSON structure does not match structure expected for result type (or has other mismatch issues). + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public static ArrayList getCardSetList() throws JsonParseException, JsonMappingException, IOException { + return mapper.readValue(new URL(SETS_URL), new TypeReference>() {}); + } + + /** + * Attemps to the read the specified file into a {@code Cardbase.} Exceptions are thrown as outlined below. + * + * @param file the file to read. + * @return a {@code Cardbase} object equivalent to the given file. + * + * @throws JsonParseException if underlying input contains invalid content of type JsonParser supports (JSON for default case). + * @throws JsonMappingException if the input JSON structure does not match structure expected for result type (or has other mismatch issues). + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public static Cardbase readCardbase(File file) throws JsonParseException, JsonMappingException, IOException { + return mapper.readValue(file, Cardbase.class); + } + + /** + * Writes the provided {@code Cardbase} to the provided file in JSON format. + * + * @param file the file to which to write the {@code Cardbase}. + * @param cardbase the {@code Cardbase} to write out. + * + * @throws JsonGenerationException if the data structure given does not generate valid JSON. + * @throws JsonMappingException if the data structure given does not generate valid JSON as well? + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public static void writeCardbase(File file, Cardbase cardbase) throws JsonGenerationException, JsonMappingException, IOException { + mapper.writeValue(file, cardbase); + } + +} diff --git a/src/eu/equalparts/cardbase/query/IO.java b/src/eu/equalparts/cardbase/query/IO.java deleted file mode 100644 index 6fe9390..0000000 --- a/src/eu/equalparts/cardbase/query/IO.java +++ /dev/null @@ -1,113 +0,0 @@ -package eu.equalparts.cardbase.query; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; - -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import eu.equalparts.cardbase.data.FullCardSet; -import eu.equalparts.cardbase.data.Cardbase; -import eu.equalparts.cardbase.data.CardSet; - -/** - * Class responsible for all I/O operations, such as fetching content from remote servers, reading from - * and writing to files. - *
- * All relevant methods here are static, this class should not be instantiated. - * - * @author Eduardo Pedroni - */ -public class IO { - - /** - * The base URL from where the information is fetched. - */ - private static final String BASE_URL = "http://mtgjson.com/json/"; - /** - * The URL where the complete list of sets is fetched. - */ - private static final String SETS_URL = BASE_URL + "SetList.json"; - /** - * The Jackson ObjectMapper which parses fetched JSON files. - */ - private static final ObjectMapper mapper = createMapper(); - - /** - * Private constructor, this class is not to be instantiated. - */ - private IO() {} - - /** - * Instantiate and configure Jackson mapper statically. - * - * @return the {@code ObjectMapper}, ready to use. - */ - private static ObjectMapper createMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - // TODO decide what to do about this - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return objectMapper; - } - - /** - * Fetches a complete set by code, where the code is a short string determined by WotC. - * The full list of valid codes can be acquired with {@code IO.getCardSetList()}. - * - * @param setCode the code of the set to be fetched. - * @return the complete specified set in a {@code FullCardSet} object. - * - * @throws JsonParseException if underlying input contains invalid content of type JsonParser supports (JSON for default case). - * @throws JsonMappingException if the input JSON structure does not match structure expected for result type (or has other mismatch issues). - * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. - */ - public static FullCardSet getCardSet(String setCode) throws JsonParseException, JsonMappingException, IOException { - return mapper.readValue(new URL(BASE_URL + setCode + ".json"), FullCardSet.class); - } - - /** - * @return a list of all card sets in the form of {@code CardSet} objects. - * - * @throws JsonParseException if underlying input contains invalid content of type JsonParser supports (JSON for default case). - * @throws JsonMappingException if the input JSON structure does not match structure expected for result type (or has other mismatch issues). - * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. - */ - public static ArrayList getCardSetList() throws JsonParseException, JsonMappingException, IOException { - return mapper.readValue(new URL(SETS_URL), new TypeReference>() {}); - } - - /** - * Attemps to the read the specified file into a {@code Cardbase.} Exceptions are thrown as outlined below. - * - * @param file the file to read. - * @return a {@code Cardbase} object equivalent to the given file. - * - * @throws JsonParseException if underlying input contains invalid content of type JsonParser supports (JSON for default case). - * @throws JsonMappingException if the input JSON structure does not match structure expected for result type (or has other mismatch issues). - * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. - */ - public static Cardbase readCardbase(File file) throws JsonParseException, JsonMappingException, IOException { - return mapper.readValue(file, Cardbase.class); - } - - /** - * Writes the provided {@code Cardbase} to the provided file in JSON format. - * - * @param file the file to which to write the {@code Cardbase}. - * @param cardbase the {@code Cardbase} to write out. - * - * @throws JsonGenerationException if the data structure given does not generate valid JSON. - * @throws JsonMappingException if the data structure given does not generate valid JSON as well? - * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. - */ - public static void writeCardbase(File file, Cardbase cardbase) throws JsonGenerationException, JsonMappingException, IOException { - mapper.writeValue(file, cardbase); - } - -} 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