From ff08a196fe98790d78ee7f5b26b9f367f9fa5a68 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Thu, 11 Jun 2015 18:12:24 +0200 Subject: Started work on the GUI, and added imageCode field to card, now need to update own collection --- src/eu/equalparts/cardbase/Cardbase.java | 1 + src/eu/equalparts/cardbase/cli/CardbaseCLI.java | 493 +++++++++++++++++++++ src/eu/equalparts/cardbase/data/Card.java | 1 + src/eu/equalparts/cardbase/data/FullCardSet.java | 145 +----- src/eu/equalparts/cardbase/gui/CardImageView.java | 18 + src/eu/equalparts/cardbase/gui/CardbaseGUI.java | 36 ++ src/eu/equalparts/cardbase/gui/CardbaseViewer.java | 11 + .../cardbase/standalone/CardbaseCLI.java | 493 --------------------- src/eu/equalparts/cardbase/utils/JSON.java | 2 +- src/eu/equalparts/cardbase/utils/MTGUniverse.java | 46 +- 10 files changed, 595 insertions(+), 651 deletions(-) create mode 100644 src/eu/equalparts/cardbase/cli/CardbaseCLI.java create mode 100644 src/eu/equalparts/cardbase/gui/CardImageView.java create mode 100644 src/eu/equalparts/cardbase/gui/CardbaseGUI.java create mode 100644 src/eu/equalparts/cardbase/gui/CardbaseViewer.java delete mode 100644 src/eu/equalparts/cardbase/standalone/CardbaseCLI.java (limited to 'src/eu') diff --git a/src/eu/equalparts/cardbase/Cardbase.java b/src/eu/equalparts/cardbase/Cardbase.java index c5efc85..9251d4c 100644 --- a/src/eu/equalparts/cardbase/Cardbase.java +++ b/src/eu/equalparts/cardbase/Cardbase.java @@ -1,4 +1,5 @@ package eu.equalparts.cardbase; + import java.io.File; import java.io.IOException; import java.util.Collection; diff --git a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java new file mode 100644 index 0000000..7a2e581 --- /dev/null +++ b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java @@ -0,0 +1,493 @@ +package eu.equalparts.cardbase.cli; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.Scanner; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; + +import eu.equalparts.cardbase.Cardbase; +import eu.equalparts.cardbase.data.Card; +import eu.equalparts.cardbase.data.FullCardSet; +import eu.equalparts.cardbase.data.CardSetInformation; +import eu.equalparts.cardbase.utils.MTGUniverse; + +/** + * This provides a lightweight CLI for interacting with cardbase files. + * + * @author Eduardo Pedroni + */ +public class CardbaseCLI { + + /** + * Enum type to store actions. + * + * @author Eduardo Pedroni + */ + private enum Action { + ADD, REMOVE; + public Card card; + public Integer count; + /** + * Sets both fields at once. + * + * @param card the card last modified. + * @param count the amount that was added or removed. + */ + public void set(Card card, Integer count) { + this.card = card; + this.count = count; + } + } + + /** + * Location of the help file. + */ + private static final String HELP_FILE_PATH = "/help"; + /** + * The last action performed by the user. + */ + private Action lastAction = null; + /** + * The currently selected set, from which new cards are added. + */ + private FullCardSet selectedSet = null; + /** + * The actual cardbase being interfaced with. + */ + private Cardbase cardbase; + /** + * Printed to the console when the user enters the help command. + */ + private String help = "Not available, check project page on GitHub."; + /** + * The cardbase file off which we are currently working, if any. + */ + private File cardbaseFile = null; + /** + * Save flag is raised when cards are added or removed and causes a prompt to be shown + * 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. + * + * @param args the first argument is the cardbase file. Further arguments are ignored. + */ + public static void main(String... args) { + new CardbaseCLI(args).startInterface(); + } + + /** + * 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. + */ + private CardbaseCLI(String... args) { + System.out.println("Welcome to Cardbase CLI!"); + + // set debug flag if we are debugging + if (Cardbase.DEBUG) System.out.println("Debug mode is on."); + + // make the CardbaseManager + if (args.length > 0) { + File cardbaseFile = new File(args[0]); + if (cardbaseFile.exists() && cardbaseFile.isFile() && cardbaseFile.canRead()) { + System.out.println("Loading cardbase from \"" + args[0] + "\"."); + try { + cardbase = new Cardbase(cardbaseFile); + } 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. + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } catch (JsonMappingException e) { + System.out.println("Error: unexpected fields found in cardbase, it may be from an old version?"); + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } catch (IOException e) { + System.out.println("Error: something went wrong reading cardbase file, abort..."); + if (Cardbase.DEBUG) e.printStackTrace(); + System.exit(1); + } + } 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."); + cardbase = new Cardbase(); + } + + // load help information + InputStream is = CardbaseCLI.class.getResourceAsStream(HELP_FILE_PATH); + if (is != null) { + help = new Scanner(is).useDelimiter("\\Z").next(); + } else { + System.out.println("Help file was not found, check the project page on GitHub for help instead."); + } + + } + + /** + * Read stdin for user input, sanitise and interpret any commands entered. + */ + private void startInterface() { + BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); + try { + // the main loop + while (!exit) { + // print prompt + System.out.print(selectedSet == null ? "> " : selectedSet.code + " > "); + // condition input and interpret + String[] raw = consoleReader.readLine().trim().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..."); + if (Cardbase.DEBUG) e.printStackTrace(); + } + } + + /** + * Print help to console. + */ + 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 { + cardbase.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; + } catch (JsonGenerationException | JsonMappingException e) { + System.out.println("Error: something terrible happened to the internal cardbase data structure. Oops."); + if (Cardbase.DEBUG) e.printStackTrace(); + } catch (IOException e) { + System.out.println("Error: lost contact with the output file, try again?"); + if (Cardbase.DEBUG) e.printStackTrace(); + } + } + } 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 (CardSetInformation set : MTGUniverse.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 = MTGUniverse.getFullCardSet(args[0]); + // if the set code is invalid, null is returned + if (selectedSet != null) { + System.out.println("Selected set: " + selectedSet.name + "."); + // undoing is not allowed if the set is changed - it would get tricky + lastAction = null; + } else { + System.out.println("\"" + args[0] + "\" does not correspond to any set (use \"sets\" to see all valid set codes)."); + } + } catch (JsonParseException e) { + System.out.println("Error: JSON fetched from upstream was not formatted properly."); + if (Cardbase.DEBUG) e.printStackTrace(); + } catch (JsonMappingException e) { + System.out.println("Error: JSON fetched from upstream does not match the data structure used internally."); + if (Cardbase.DEBUG) e.printStackTrace(); + } catch (IOException e) { + System.out.println("Error: JSON could not be fetched from upstream."); + if (Cardbase.DEBUG) e.printStackTrace(); + } + } 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() { + int total = 0; + for (Card card : cardbase.getCards()) { + printGlance(card); + total += card.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 = cardbase.getCard(selectedSet.code, args[0]); + 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 { + // peruse all cards in cardbase + int total = 0; + for (Card card : cardbase.getCards()) { + printPerusal(card); + total += card.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; + } + } + removeCard(cardToRemove, count); + } else { + System.out.println(args[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(number + " does not correspond to a card in " + selectedSet.name + "."); + } + } + } else { + System.out.println("Select a set before adding cards."); + } + } + + /** + * Add the specified count of the specified card + * to the cardbase. + * + * @param card the card to add. + * @param count the number of times to add it. + */ + private void addCard(Card card, Integer count) { + cardbase.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. + * + * @param card the card to remove. + * @param count the number of times to remove it. + */ + private void removeCard(Card card, Integer count) { + Integer removed = cardbase.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. + * + * @param name the file name candidate to sanitise. + * @return the sanitised name. + */ + private String sanitiseFileName(String name) { + // POSIX-compliant valid filename characters + name = name.replaceAll("[^-_.A-Za-z0-9]", ""); + // extension is not indispensable, but good practice + if (!name.endsWith(".cb")) { + name = name.concat(".cb"); + } + return name; + } + + /** + * Prints a glance of the specified card. A glance contains simply + * the card count, name, set code and set number. + * + * @param card the card to glance. + */ + 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 + * as that would be too verbose while adding little value. + * + * @param card the card to peruse. + */ + private void printPerusal(Card card) { + printGlance(card); + if (card.type != null) System.out.println("\t" + card.type); + 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); + } +} diff --git a/src/eu/equalparts/cardbase/data/Card.java b/src/eu/equalparts/cardbase/data/Card.java index cca1747..2810564 100644 --- a/src/eu/equalparts/cardbase/data/Card.java +++ b/src/eu/equalparts/cardbase/data/Card.java @@ -22,6 +22,7 @@ public class Card { // Not part of upstream JSON public String setCode; + public String imageCode; public Integer count; } \ No newline at end of file diff --git a/src/eu/equalparts/cardbase/data/FullCardSet.java b/src/eu/equalparts/cardbase/data/FullCardSet.java index e831b74..a7f3ea5 100644 --- a/src/eu/equalparts/cardbase/data/FullCardSet.java +++ b/src/eu/equalparts/cardbase/data/FullCardSet.java @@ -1,76 +1,19 @@ package eu.equalparts.cardbase.data; -import java.util.Collections; import java.util.Map; public class FullCardSet { - private String name; - private String code; - private String magicCardsInfoCode; - private String releaseDate; - private String border; - private String type; - private String block; - private String gathererCode; - private Map cards; + public String name; + public String code; + public String magicCardsInfoCode; + public String releaseDate; + public String border; + public String type; + public String block; + public String gathererCode; + public Map cards; - /** - * @return the set's name. - */ - public String getName() { - return name; - } - - /** - * @return the set code. - */ - public String getCode() { - return code; - } - - /** - * @return the set's release date. - */ - public String getReleaseDate() { - return releaseDate; - } - - /** - * @return the set's border type. - */ - public String getBorder() { - return border; - } - - /** - * @return the type of the set. - */ - public String getType() { - return type; - } - - /** - * @return the set's block. - */ - public String getBlock() { - return block; - } - - /** - * @return the set's Gatherer code. - */ - public String getGathererCode() { - return gathererCode; - } - - /** - * @return a full unmodifiable map of the set's cards. - */ - public Map getCards() { - return Collections.unmodifiableMap(cards); - } - /** * Searches for a card by number (the one shown on the card itself). * @@ -80,74 +23,4 @@ public class FullCardSet { public Card getCardByNumber(String number) { return cards.get(number); } - - /** - * @param name the name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * @param code the code to set - */ - public void setCode(String code) { - this.code = code; - } - - /** - * @param releaseDate the releaseDate to set - */ - public void setReleaseDate(String releaseDate) { - this.releaseDate = releaseDate; - } - - /** - * @param border the border to set - */ - public void setBorder(String border) { - this.border = border; - } - - /** - * @param type the type to set - */ - public void setType(String type) { - this.type = type; - } - - /** - * @param block the block to set - */ - public void setBlock(String block) { - this.block = block; - } - - /** - * @param gathererCode the gathererCode to set - */ - public void setGathererCode(String gathererCode) { - this.gathererCode = gathererCode; - } - - /** - * @param cards the cards to set - */ - public void setCards(Map cards) { - this.cards = cards; - } - - /** - * @return the magicCardsInfoCode - */ - public String getMagicCardsInfoCode() { - return magicCardsInfoCode; - } - - /** - * @param magicCardsInfoCode the magicCardsInfoCode to set - */ - public void setMagicCardsInfoCode(String magicCardsInfoCode) { - this.magicCardsInfoCode = magicCardsInfoCode; - } } \ No newline at end of file diff --git a/src/eu/equalparts/cardbase/gui/CardImageView.java b/src/eu/equalparts/cardbase/gui/CardImageView.java new file mode 100644 index 0000000..7c79c6a --- /dev/null +++ b/src/eu/equalparts/cardbase/gui/CardImageView.java @@ -0,0 +1,18 @@ +package eu.equalparts.cardbase.gui; + +import eu.equalparts.cardbase.data.Card; +import javafx.scene.image.ImageView; + +public class CardImageView extends ImageView { + + private static final String BASE_IMG_URL = "http://magiccards.info/scans/en/"; + + public CardImageView(Card card) { + super(makeUrl(card)); + } + + public static String makeUrl(Card card) { + return BASE_IMG_URL + card.imageCode + "/" + card.number + ".jpg"; + } + +} diff --git a/src/eu/equalparts/cardbase/gui/CardbaseGUI.java b/src/eu/equalparts/cardbase/gui/CardbaseGUI.java new file mode 100644 index 0000000..46d3c55 --- /dev/null +++ b/src/eu/equalparts/cardbase/gui/CardbaseGUI.java @@ -0,0 +1,36 @@ +package eu.equalparts.cardbase.gui; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Pane; +import javafx.stage.Stage; +import eu.equalparts.cardbase.data.Card; +import eu.equalparts.cardbase.utils.MTGUniverse; + +public class CardbaseGUI extends Application { + + /** + * Run the GUI. + * + * @param args arguments passed down to {@code Application.launch()}. + */ + public static void main(String... args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) throws Exception { + Card card = MTGUniverse.getCard("M15", "281"); + ImageView test = new ImageView(CardImageView.makeUrl(card)); + + Pane parent = new Pane(); + + parent.getChildren().add(test); + + primaryStage.setScene(new Scene(parent)); + + primaryStage.setTitle("Cardbase"); + primaryStage.show(); + } +} diff --git a/src/eu/equalparts/cardbase/gui/CardbaseViewer.java b/src/eu/equalparts/cardbase/gui/CardbaseViewer.java new file mode 100644 index 0000000..30b32fb --- /dev/null +++ b/src/eu/equalparts/cardbase/gui/CardbaseViewer.java @@ -0,0 +1,11 @@ +package eu.equalparts.cardbase.gui; + +import javafx.scene.Parent; + +public class CardbaseViewer extends Parent { + + + + + +} diff --git a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java b/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java deleted file mode 100644 index be3616a..0000000 --- a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java +++ /dev/null @@ -1,493 +0,0 @@ -package eu.equalparts.cardbase.standalone; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Arrays; -import java.util.Scanner; - -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; - -import eu.equalparts.cardbase.Cardbase; -import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.FullCardSet; -import eu.equalparts.cardbase.data.CardSetInformation; -import eu.equalparts.cardbase.utils.MTGUniverse; - -/** - * This provides a lightweight CLI for interacting with cardbase files. - * - * @author Eduardo Pedroni - */ -public class CardbaseCLI { - - /** - * Enum type to store actions. - * - * @author Eduardo Pedroni - */ - private enum Action { - ADD, REMOVE; - public Card card; - public Integer count; - /** - * Sets both fields at once. - * - * @param card the card last modified. - * @param count the amount that was added or removed. - */ - public void set(Card card, Integer count) { - this.card = card; - this.count = count; - } - } - - /** - * Location of the help file. - */ - private static final String HELP_FILE_PATH = "/help"; - /** - * The last action performed by the user. - */ - private Action lastAction = null; - /** - * The currently selected set, from which new cards are added. - */ - private FullCardSet selectedSet = null; - /** - * The actual cardbase being interfaced with. - */ - private Cardbase cardbase; - /** - * Printed to the console when the user enters the help command. - */ - private String help = "Not available, check project page on GitHub."; - /** - * The cardbase file off which we are currently working, if any. - */ - private File cardbaseFile = null; - /** - * Save flag is raised when cards are added or removed and causes a prompt to be shown - * 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. - * - * @param args the first argument is the cardbase file. Further arguments are ignored. - */ - public static void main(String... args) { - new CardbaseCLI(args).startInterface(); - } - - /** - * 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. - */ - private CardbaseCLI(String... args) { - System.out.println("Welcome to Cardbase CLI!"); - - // set debug flag if we are debugging - if (Cardbase.DEBUG) System.out.println("Debug mode is on."); - - // make the CardbaseManager - if (args.length > 0) { - File cardbaseFile = new File(args[0]); - if (cardbaseFile.exists() && cardbaseFile.isFile() && cardbaseFile.canRead()) { - System.out.println("Loading cardbase from \"" + args[0] + "\"."); - try { - cardbase = new Cardbase(cardbaseFile); - } 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. - if (Cardbase.DEBUG) e.printStackTrace(); - System.exit(1); - } catch (JsonMappingException e) { - System.out.println("Error: unexpected fields found in cardbase, it may be from an old version?"); - if (Cardbase.DEBUG) e.printStackTrace(); - System.exit(1); - } catch (IOException e) { - System.out.println("Error: something went wrong reading cardbase file, abort..."); - if (Cardbase.DEBUG) e.printStackTrace(); - System.exit(1); - } - } 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."); - cardbase = new Cardbase(); - } - - // load help information - InputStream is = CardbaseCLI.class.getResourceAsStream(HELP_FILE_PATH); - if (is != null) { - help = new Scanner(is).useDelimiter("\\Z").next(); - } else { - System.out.println("Help file was not found, check the project page on GitHub for help instead."); - } - - } - - /** - * Read stdin for user input, sanitise and interpret any commands entered. - */ - private void startInterface() { - BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); - try { - // the main loop - while (!exit) { - // print prompt - System.out.print(selectedSet == null ? "> " : selectedSet.getCode() + " > "); - // condition input and interpret - String[] raw = consoleReader.readLine().trim().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..."); - if (Cardbase.DEBUG) e.printStackTrace(); - } - } - - /** - * Print help to console. - */ - 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 { - cardbase.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; - } catch (JsonGenerationException | JsonMappingException e) { - System.out.println("Error: something terrible happened to the internal cardbase data structure. Oops."); - if (Cardbase.DEBUG) e.printStackTrace(); - } catch (IOException e) { - System.out.println("Error: lost contact with the output file, try again?"); - if (Cardbase.DEBUG) e.printStackTrace(); - } - } - } 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 (CardSetInformation set : MTGUniverse.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 = MTGUniverse.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 { - System.out.println("\"" + args[0] + "\" does not correspond to any set (use \"sets\" to see all valid set codes)."); - } - } catch (JsonParseException e) { - System.out.println("Error: JSON fetched from upstream was not formatted properly."); - if (Cardbase.DEBUG) e.printStackTrace(); - } catch (JsonMappingException e) { - System.out.println("Error: JSON fetched from upstream does not match the data structure used internally."); - if (Cardbase.DEBUG) e.printStackTrace(); - } catch (IOException e) { - System.out.println("Error: JSON could not be fetched from upstream."); - if (Cardbase.DEBUG) e.printStackTrace(); - } - } 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() { - int total = 0; - for (Card card : cardbase.getCards()) { - printGlance(card); - total += card.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 = cardbase.getCard(selectedSet.getCode(), args[0]); - 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 { - // peruse all cards in cardbase - int total = 0; - for (Card card : cardbase.getCards()) { - printPerusal(card); - total += card.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; - } - } - removeCard(cardToRemove, count); - } else { - System.out.println(args[0] + " does not correspond to a card in " + selectedSet.getName() + "."); - } - } 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(number + " does not correspond to a card in " + selectedSet.getName() + "."); - } - } - } else { - System.out.println("Select a set before adding cards."); - } - } - - /** - * Add the specified count of the specified card - * to the cardbase. - * - * @param card the card to add. - * @param count the number of times to add it. - */ - private void addCard(Card card, Integer count) { - cardbase.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. - * - * @param card the card to remove. - * @param count the number of times to remove it. - */ - private void removeCard(Card card, Integer count) { - Integer removed = cardbase.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. - * - * @param name the file name candidate to sanitise. - * @return the sanitised name. - */ - private String sanitiseFileName(String name) { - // POSIX-compliant valid filename characters - name = name.replaceAll("[^-_.A-Za-z0-9]", ""); - // extension is not indispensable, but good practice - if (!name.endsWith(".cb")) { - name = name.concat(".cb"); - } - return name; - } - - /** - * Prints a glance of the specified card. A glance contains simply - * the card count, name, set code and set number. - * - * @param card the card to glance. - */ - 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 - * as that would be too verbose while adding little value. - * - * @param card the card to peruse. - */ - private void printPerusal(Card card) { - printGlance(card); - if (card.type != null) System.out.println("\t" + card.type); - 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); - } -} diff --git a/src/eu/equalparts/cardbase/utils/JSON.java b/src/eu/equalparts/cardbase/utils/JSON.java index a5992c7..17e8392 100644 --- a/src/eu/equalparts/cardbase/utils/JSON.java +++ b/src/eu/equalparts/cardbase/utils/JSON.java @@ -28,7 +28,7 @@ public final class JSON { */ private static ObjectMapper createMapper() { ObjectMapper objectMapper = new ObjectMapper(); - // TODO decide what to do about this + // classes don't necessarily use all json fields objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); return objectMapper; } diff --git a/src/eu/equalparts/cardbase/utils/MTGUniverse.java b/src/eu/equalparts/cardbase/utils/MTGUniverse.java index 8c77032..51959aa 100644 --- a/src/eu/equalparts/cardbase/utils/MTGUniverse.java +++ b/src/eu/equalparts/cardbase/utils/MTGUniverse.java @@ -32,7 +32,7 @@ public final class MTGUniverse { /** * The base URL from where the information is fetched. */ - private static final String BASE_URL = "http://mtgjson.com/json/"; + private static final String BASE_DATA_URL = "http://mtgjson.com/json/"; /** * If the upstream set code list can't be loaded, this is loaded instead. */ @@ -100,7 +100,7 @@ public final class MTGUniverse { } // not cached; fetch, cache and return it else { - requestedSet = parseFullSet(JSON.mapper.readValue(new URL(BASE_URL + validCode + ".json"), JsonNode.class)); + requestedSet = parseFullSet(JSON.mapper.readValue(new URL(BASE_DATA_URL + validCode + ".json"), JsonNode.class)); cardSetCache.put(validCode, requestedSet); } } @@ -114,7 +114,7 @@ public final class MTGUniverse { // if the list isn't cached, fetch and cache it if (cardSets == null) { try { - cardSets = JSON.mapper.readValue(new URL(BASE_URL + "SetList.json"), new TypeReference>() {}); + cardSets = JSON.mapper.readValue(new URL(BASE_DATA_URL + "SetList.json"), new TypeReference>() {}); } catch (Exception e) { System.out.println("Error: could not fetch/parse set code list from upstream, loading fallback json..."); e.printStackTrace(); @@ -170,7 +170,7 @@ public final class MTGUniverse { * These fields are critical, if any of them is not present an exception is thrown. */ if (jsonTree.hasNonNull("name")) { - fcs.setName(jsonTree.get("name").asText()); + fcs.name = jsonTree.get("name").asText(); } else { throw new JsonMappingException("Field \"name\" not found."); } @@ -178,45 +178,49 @@ public final class MTGUniverse { String setCode; if (jsonTree.hasNonNull("code")) { setCode = jsonTree.get("code").asText(); - fcs.setCode(setCode); + fcs.code = setCode; } else { throw new JsonMappingException("Field \"code\" not found."); } + String imageCode; + if (jsonTree.hasNonNull("magicCardsInfoCode")) { + imageCode = jsonTree.get("magicCardsInfoCode").asText(); + fcs.magicCardsInfoCode = imageCode; + } else { + throw new JsonMappingException("Field \"magicCardsInfoCode\" not found."); + } + if (jsonTree.hasNonNull("releaseDate")) { - fcs.setReleaseDate(jsonTree.get("releaseDate").asText()); + fcs.releaseDate = jsonTree.get("releaseDate").asText(); } else { throw new JsonMappingException("Field \"releaseDate\" not found."); } - /* - * These fields are optional and are set to null if not present. - */ - fcs.setGathererCode(jsonTree.hasNonNull("gathererCode") ? jsonTree.get("gathererCode").asText() : null); - fcs.setBorder(jsonTree.hasNonNull("border") ? jsonTree.get("border").asText() : null); - fcs.setType(jsonTree.hasNonNull("type") ? jsonTree.get("type").asText() : null); - fcs.setMagicCardsInfoCode(jsonTree.hasNonNull("magicCardsInfoCode") ? jsonTree.get("magicCardsInfoCode").asText() : null); - fcs.setBlock(jsonTree.hasNonNull("block") ? jsonTree.get("block").asText() : null); - - /* - * This is a critical field which must be present, if not an exception is thrown. - */ if (jsonTree.hasNonNull("cards")) { - // attempt to card list as POJO + // attempt to read card list as a POJO using the standard mapper List rawList = jsonTree.get("cards").traverse(JSON.mapper).readValueAs(new TypeReference>() {}); - // generate the map Map cardMap = new HashMap(); for (Card card : rawList) { // add set code for convenience card.setCode = setCode; + card.imageCode = imageCode; cardMap.put(card.number, card); } - fcs.setCards(cardMap); + fcs.cards = cardMap; } else { throw new JsonMappingException("Field \"cards\" not found."); } + /* + * These fields are optional and are set to null if not present. + */ + fcs.gathererCode = jsonTree.hasNonNull("gathererCode") ? jsonTree.get("gathererCode").asText() : null; + fcs.border = jsonTree.hasNonNull("border") ? jsonTree.get("border").asText() : null; + fcs.type = jsonTree.hasNonNull("type") ? jsonTree.get("type").asText() : null; + fcs.block = jsonTree.hasNonNull("block") ? jsonTree.get("block").asText() : null; + return fcs; } -- cgit v1.2.3