From 4ee655bef4cdf9e62a1b247e77754441de806f22 Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Mon, 8 Jun 2015 16:57:44 +0200 Subject: Implemented sorting using reflection, not sure it was a good idea though --- src/eu/equalparts/cardbase/Cardbase.java | 164 +++++++++++++++++++++ .../cardbase/comparators/CardComparator.java | 102 +++++++++++++ src/eu/equalparts/cardbase/data/Card.java | 22 +-- src/eu/equalparts/cardbase/data/CardSet.java | 34 ----- .../cardbase/data/CardSetInformation.java | 34 +++++ src/eu/equalparts/cardbase/data/Cardbase.java | 24 --- .../equalparts/cardbase/data/CardbaseManager.java | 126 ---------------- src/eu/equalparts/cardbase/data/FullCardSet.java | 32 +++- .../cardbase/standalone/CardbaseCLI.java | 89 +++++------ src/eu/equalparts/cardbase/utils/IO.java | 35 ----- src/eu/equalparts/cardbase/utils/JSON.java | 35 +++++ src/eu/equalparts/cardbase/utils/MTGUniverse.java | 17 ++- 12 files changed, 432 insertions(+), 282 deletions(-) create mode 100644 src/eu/equalparts/cardbase/Cardbase.java create mode 100644 src/eu/equalparts/cardbase/comparators/CardComparator.java delete mode 100644 src/eu/equalparts/cardbase/data/CardSet.java create mode 100644 src/eu/equalparts/cardbase/data/CardSetInformation.java delete mode 100644 src/eu/equalparts/cardbase/data/Cardbase.java delete mode 100644 src/eu/equalparts/cardbase/data/CardbaseManager.java delete mode 100644 src/eu/equalparts/cardbase/utils/IO.java create mode 100644 src/eu/equalparts/cardbase/utils/JSON.java (limited to 'src') diff --git a/src/eu/equalparts/cardbase/Cardbase.java b/src/eu/equalparts/cardbase/Cardbase.java new file mode 100644 index 0000000..14afc10 --- /dev/null +++ b/src/eu/equalparts/cardbase/Cardbase.java @@ -0,0 +1,164 @@ +package eu.equalparts.cardbase; +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; + +import eu.equalparts.cardbase.comparators.CardComparator; +import eu.equalparts.cardbase.data.Card; +import eu.equalparts.cardbase.utils.JSON; + +/** + * Provides a variety of utility methods to interact with the loaded cardbase. + * + * @author Eduardo Pedroni + */ +public class Cardbase { + + /** + * The cards in the cardbase. + */ + private List cards; + /** + * Debug flag is raised when the DEBUG environment variable is set. This causes additional + * information to be printed to the console. + */ + public static final boolean DEBUG = System.getenv("CB_DEBUG") != null; + + /** + * Creates an empty cardbase. + */ + public Cardbase() { + cards = new ArrayList(); + } + + /** + * Initialises the cardbase with the contents of a file. + * + * @param cardbaseFile the cardbase JSON to load. + * + * @throws JsonParseException if the specified file does not contain valid JSON. + * @throws JsonMappingException if the specified file structure does not match that of {@code Cardbase}. + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public Cardbase(File cardbaseFile) throws JsonParseException, JsonMappingException, IOException { + cards = JSON.mapper.readValue(cardbaseFile, new TypeReference>() {}); + } + + /** + * 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 void writeCardbase(File outputFile) throws JsonGenerationException, JsonMappingException, IOException { + JSON.mapper.writeValue(outputFile, cards); + } + + /** + * Adds a specific amount of a card to the cardbase. + * If the card is not already in the cardbase, it is added. + * If it is already present, the count is simply updated. + * + * @param cardToAdd the card to be added. + * @param count the amount of the card to be added. + */ + public void addCard(Card cardToAdd, Integer count) { + Card card = getCard(cardToAdd.setCode, cardToAdd.number); + if (card != null) { + card.count += count; + } else { + cardToAdd.count = count; + cards.add(cardToAdd); + } + } + + /** + * Removes a specific amount of a card from the cardbase. + * If the card is not present in the cardbase, nothing happens. + * If the card is present in the cardbase, the specified amount is removed. + * If that amount is equal to or exceeds the count already in the cardbase, + * the card entry is removed altogether. + *

+ * In any case, the value returned is the actual number of cards removed. + * For example, if 5 Shivan Dragons are in the cardbase and the method is + * called to remove 10 Shivan Dragons, the {@code Card} representing the + * Shivan Dragon is removed from the cardbase, and the value returned is 5. + * + * @param cardToRemove the card to be removed. + * @param count the amount of the card to be removed. + * @return the number of cards actually removed. + */ + public Integer removeCard(Card cardToRemove, Integer count) { + Card card = getCard(cardToRemove.setCode, cardToRemove.number); + Integer removed = 0; + if (card != null) { + if (card.count <= count) { + cards.remove(card); + removed = card.count; + } else { + card.count -= count; + removed = count; + } + } + return removed; + } + + /** + * This method is intended to allow iteration directly on the list of cards, + * while at the same time retaining control over the insert and remove procedures. + * The returned {@code List} is a read-only; trying to modify its structure will + * result in a {@code UnsupportedOperationException}. + * + * @return an unmodifiable list of all the cards in the cardbase. + */ + public List getCards() { + return Collections.unmodifiableList(cards); + } + + /** + * Sorts the cardbase by the specified field. The field must be specified exactly + * as it is defined in {@code Card}, case-sensitive. It must also be comparable to + * itself, as {@code String} and {@code Integer} are. + * + * @param fieldName the declared name of the field to be used for sorting. + * @return true if the sort was successful, false if no such field was found. + */ + public boolean sortBy(String fieldName) { + for (Field field : Card.class.getDeclaredFields()) { + if (field.getName().equals(fieldName)) { + cards.sort(new CardComparator(field)); + return true; + } + } + return false; + } + + /** + * Returns a card from the cardbase by set code and number. + * If no such card is in the cardbase, returns null. + * + * @param setCode the set to which the requested card belongs. + * @param number the requested card's set number. + * @return the requested {@code Card} or null if no card is found. + */ + public Card getCard(String setCode, String number) { + for (Card card : cards) { + if (card.setCode.equals(setCode) && card.number.equals(number)) + return card; + } + + return null; + } +} diff --git a/src/eu/equalparts/cardbase/comparators/CardComparator.java b/src/eu/equalparts/cardbase/comparators/CardComparator.java new file mode 100644 index 0000000..3640c8b --- /dev/null +++ b/src/eu/equalparts/cardbase/comparators/CardComparator.java @@ -0,0 +1,102 @@ +package eu.equalparts.cardbase.comparators; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Comparator; + +import eu.equalparts.cardbase.Cardbase; +import eu.equalparts.cardbase.data.Card; + +/** + * I'm new to this reflection business, so bear with me. + *

+ * The idea here is to avoid having to write one class + * for each comparable field in {@code Card}. The program + * can dynamically instantiate them as cards are compared + * by different fields. + *

+ * This class uses reflection to determine if the specified + * field is comparable with itself upon construction, and throws + * an {@code IllegalArgumentException} if that is not the case. + * + * @author Eduardo Pedroni + * + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class CardComparator implements Comparator { + + /** + * The field being compared. + */ + private Field fieldToCompare; + + /** + * Creates a new comparator for the specified field only. This class + * will only be constructed successfully if the field comes from + * {@code Card} and can be compared to itself (i.e. implements + * {@code Comparable} where T is its own type. + *
+ * For reference, {@code String} and {@code Integer} are both self comparable. + * + * @param fieldToCompare the field this comparator will use to compare cards, as declared in {@code Card}. + */ + public CardComparator(Field fieldToCompare) { + if (fieldToCompare.getDeclaringClass().equals(Card.class) && + isSelfComparable(fieldToCompare.getType())) { + + this.fieldToCompare = fieldToCompare; + } else { + System.out.println(fieldToCompare.isAccessible()); + System.out.println(fieldToCompare.getDeclaringClass().equals(Card.class)); + System.out.println(isSelfComparable(fieldToCompare.getType())); + throw new IllegalArgumentException("The field provided is not valid."); + } + } + + @Override + public int compare(Card o1, Card o2) { + try { + /* + * we've already checked that the field is self comparable, + * so we are now free to cast to whatever type it is and compare. + */ + Comparable field1 = (Comparable) fieldToCompare.get(o1); + Comparable field2 = (Comparable) fieldToCompare.get(o2); + + return field1.compareTo(field2); + + } catch (IllegalArgumentException e) { + System.out.println("Error: class Card does not define field" + fieldToCompare.getName() + "."); + if (Cardbase.DEBUG) e.printStackTrace(); + } catch (IllegalAccessException e) { + System.out.println("Error: field " + fieldToCompare.getName() + " in Card is not visible."); + if (Cardbase.DEBUG) e.printStackTrace(); + } + + // not comparable, this should never happen + return 0; + } + + /** + * Use reflection to determine if the specified class can be compared with itself. + * + * @param type the type to analyse. + * @return true if the type can be compared to itself using {@code compareTo()}, false otherwise. + */ + private boolean isSelfComparable(Class type) { + + // go through all interfaces implemented by this class + for (Type implementedInterface : type.getGenericInterfaces()) { + // check if any parameterised interface found is "Comparable" + if (implementedInterface instanceof ParameterizedType) { + ParameterizedType genericInterface = (ParameterizedType) implementedInterface; + if (genericInterface.getRawType().equals(Comparable.class)) { + // check that the type argument of comparable is the same as the field type itself + return genericInterface.getActualTypeArguments()[0].equals(type); + } + } + } + return false; + } +} diff --git a/src/eu/equalparts/cardbase/data/Card.java b/src/eu/equalparts/cardbase/data/Card.java index 1bf6a75..03e7dea 100644 --- a/src/eu/equalparts/cardbase/data/Card.java +++ b/src/eu/equalparts/cardbase/data/Card.java @@ -1,19 +1,19 @@ package eu.equalparts.cardbase.data; -import java.util.ArrayList; +import java.util.List; public class Card { - public String layout; - public String name; - public ArrayList names; - public String manaCost; - public Integer cmc; - public ArrayList colors; + public String layout = ""; + public String name = ""; + public List names; + public String manaCost = ""; + public Integer cmc = 0; + public List colors; public String type; - public ArrayList supertypes; - public ArrayList types; - public ArrayList subtypes; + public List supertypes; + public List types; + public List subtypes; public String rarity; public String text; public String flavor; @@ -23,7 +23,7 @@ public class Card { public String toughness; public Integer loyalty; public Integer multiverseid; - public ArrayList variations; + public List variations; public String imageName; public String border; public String watermark; diff --git a/src/eu/equalparts/cardbase/data/CardSet.java b/src/eu/equalparts/cardbase/data/CardSet.java deleted file mode 100644 index b06be7c..0000000 --- a/src/eu/equalparts/cardbase/data/CardSet.java +++ /dev/null @@ -1,34 +0,0 @@ -package eu.equalparts.cardbase.data; - -public class CardSet { - - private String name; - private String code; - private String releaseDate; - - /** - * @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; - } - - @Override - public String toString() { - return String.format("%1$-12s : %2$s", code, name, releaseDate); - } -} diff --git a/src/eu/equalparts/cardbase/data/CardSetInformation.java b/src/eu/equalparts/cardbase/data/CardSetInformation.java new file mode 100644 index 0000000..d3394b7 --- /dev/null +++ b/src/eu/equalparts/cardbase/data/CardSetInformation.java @@ -0,0 +1,34 @@ +package eu.equalparts.cardbase.data; + +public class CardSetInformation { + + private String name; + private String code; + private String releaseDate; + + /** + * @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; + } + + @Override + public String toString() { + return String.format("%1$-12s : %2$s", code, name, releaseDate); + } +} diff --git a/src/eu/equalparts/cardbase/data/Cardbase.java b/src/eu/equalparts/cardbase/data/Cardbase.java deleted file mode 100644 index 021fac2..0000000 --- a/src/eu/equalparts/cardbase/data/Cardbase.java +++ /dev/null @@ -1,24 +0,0 @@ -package eu.equalparts.cardbase.data; - -import java.util.ArrayList; - -public class Cardbase { - - public ArrayList cards = new ArrayList<>(); - public ArrayList decks = new ArrayList<>(); - - /** - * @param setCode the set to which the requested card belongs. - * @param number the requested card's set number. - * @return the requested {@code Card} or null if no card is found. - */ - public Card getCardByNumber(String setCode, String number) { - for (Card card : cards) { - if (card.setCode.equals(setCode) && card.number.equals(number)) - return card; - } - - return null; - } - -} diff --git a/src/eu/equalparts/cardbase/data/CardbaseManager.java b/src/eu/equalparts/cardbase/data/CardbaseManager.java deleted file mode 100644 index d6ba6ee..0000000 --- a/src/eu/equalparts/cardbase/data/CardbaseManager.java +++ /dev/null @@ -1,126 +0,0 @@ -package eu.equalparts.cardbase.data; -import java.io.File; -import java.io.IOException; -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.utils.IO; - -/** - * Provides a variety of utility methods to interact with the loaded cardbase. - * - * @author Eduardo Pedroni - */ -public class CardbaseManager { - - /** - * The cardbase being managed. - */ - private Cardbase cardbase; - - /** - * Creates an empty cardbase. - * - */ - public CardbaseManager() { - cardbase = new Cardbase(); - } - - /** - * Initialises the cardbase with the contents of a file. - * - * @param cardbaseFile the cardbase JSON to load. - * - * @throws JsonParseException if the specified file does not contain valid JSON. - * @throws JsonMappingException if the specified file structure does not match that of {@code Cardbase}. - * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. - */ - public CardbaseManager(File cardbaseFile) throws JsonParseException, JsonMappingException, IOException { - cardbase = IO.jsonMapper.readValue(cardbaseFile, 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 void writeCardbase(File outputFile) throws JsonGenerationException, JsonMappingException, IOException { - IO.jsonMapper.writeValue(outputFile, cardbase); - } - - /** - * Adds a specific amount of a card to the cardbase. - * If the card is not already in the cardbase, it is added. - * If it is already present, the count is simply updated. - * - * @param cardToAdd the card to be added. - * @param count the amount of the card to be added. - */ - public void addCard(Card cardToAdd, Integer count) { - Card card = cardbase.getCardByNumber(cardToAdd.setCode, cardToAdd.number); - if (card != null) { - card.count += count; - } else { - cardToAdd.count = count; - cardbase.cards.add(cardToAdd); - } - } - - /** - * Removes a specific amount of a card from the cardbase. - * If the card is not present in the cardbase, nothing happens. - * If the card is present in the cardbase, the specified amount is removed. - * If that amount is equal to or exceeds the count already in the cardbase, - * the card entry is removed altogether. - *

- * In any case, the value returned is the actual number of cards removed. - * For example, if 5 Shivan Dragons are in the cardbase and the method is - * called to remove 10 Shivan Dragons, the {@code Card} representing the - * Shivan Dragon is removed from the cardbase, and the value returned is 5. - * - * @param cardToRemove the card to be removed. - * @param count the amount of the card to be removed. - * @return the number of cards actually removed. - */ - 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 {@code Card}s in the cardbase. - */ - public Iterator cardIterator() { - return cardbase.cards.iterator(); - } - - /** - * Returns a card from the cardbase by set code and number. - * If no such card is in the cardbase, returns null. - * - * @param setCode the set to which the requested card belongs. - * @param number the requested card's set number. - * @return the requested {@code Card} or null if no card is found. - */ - public Card getCard(String setCode, String number) { - return cardbase.getCardByNumber(setCode, number); - } -} diff --git a/src/eu/equalparts/cardbase/data/FullCardSet.java b/src/eu/equalparts/cardbase/data/FullCardSet.java index 0f58633..d469829 100644 --- a/src/eu/equalparts/cardbase/data/FullCardSet.java +++ b/src/eu/equalparts/cardbase/data/FullCardSet.java @@ -1,15 +1,39 @@ package eu.equalparts.cardbase.data; -import java.util.ArrayList; +import java.util.List; -public class FullCardSet extends CardSet { +public class FullCardSet { + private String name; + private String code; + private String releaseDate; private String border; private String type; private String block; private String gathererCode; - private ArrayList cards; + private List 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. */ @@ -41,7 +65,7 @@ public class FullCardSet extends CardSet { /** * @return a full list of the set's cards. */ - public ArrayList getCards() { + public List getCards() { return cards; } diff --git a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java b/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java index a8b11ba..108b753 100644 --- a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java +++ b/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java @@ -6,17 +6,16 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Arrays; -import java.util.Iterator; 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.CardbaseManager; import eu.equalparts.cardbase.data.FullCardSet; -import eu.equalparts.cardbase.data.CardSet; +import eu.equalparts.cardbase.data.CardSetInformation; import eu.equalparts.cardbase.utils.MTGUniverse; /** @@ -60,9 +59,9 @@ public class CardbaseCLI { */ private FullCardSet selectedSet = null; /** - * The manager object which allows interaction with cardbase data structure. + * The actual cardbase being interfaced with. */ - private CardbaseManager cardbaseManager; + private Cardbase cardbase; /** * Printed to the console when the user enters the help command. */ @@ -71,11 +70,6 @@ public class CardbaseCLI { * The cardbase file off which we are currently working, if any. */ private File cardbaseFile = null; - /** - * Debug flag is raised when the DEBUG environment variable is set. This causes additional - * information to be printed to the console. - */ - private boolean debug = false; /** * 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. @@ -106,10 +100,7 @@ public class CardbaseCLI { System.out.println("Welcome to Cardbase CLI!"); // set debug flag if we are debugging - if (System.getenv("CB_DEBUG") != null) { - System.out.println("Debug mode is on."); - debug = true; - } + if (Cardbase.DEBUG) System.out.println("Debug mode is on."); // make the CardbaseManager if (args.length > 0) { @@ -117,19 +108,19 @@ public class CardbaseCLI { if (cardbaseFile.exists() && cardbaseFile.isFile() && cardbaseFile.canRead()) { System.out.println("Loading cardbase from \"" + args[0] + "\"."); try { - cardbaseManager = new CardbaseManager(cardbaseFile); + 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 (debug) e.printStackTrace(); + 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 (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); System.exit(1); } catch (IOException e) { System.out.println("Error: something went wrong reading cardbase file, abort..."); - if (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); System.exit(1); } } else { @@ -138,7 +129,7 @@ public class CardbaseCLI { } } else { System.out.println("No cardbase file was provided, creating a clean cardbase."); - cardbaseManager = new CardbaseManager(); + cardbase = new Cardbase(); } // load help information @@ -162,7 +153,7 @@ public class CardbaseCLI { // print prompt System.out.print(selectedSet == null ? "> " : selectedSet.getCode() + " > "); // condition input and interpret - String[] raw = consoleReader.readLine().trim().toLowerCase().split("[ \t]+"); + String[] raw = consoleReader.readLine().trim().split("[ \t]+"); String command = raw[0]; String[] args = Arrays.copyOfRange(raw, 1, raw.length); @@ -186,13 +177,15 @@ public class CardbaseCLI { } else if (command.equalsIgnoreCase("remove") || command.equalsIgnoreCase("rm")) { remove(args); + } else if (command.equalsIgnoreCase("sort")) { + sort(args); } else { add(command, args); } } } catch (IOException e) { System.out.println("Error: something went wrong with stdin, exiting..."); - if (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); } } @@ -223,7 +216,7 @@ public class CardbaseCLI { } else { // handle these exceptions locally - they don't necessarily mean the program should exit try { - cardbaseManager.writeCardbase(outputFile); + 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() + "\". " @@ -231,10 +224,10 @@ public class CardbaseCLI { savePrompt = false; } catch (JsonGenerationException | JsonMappingException e) { System.out.println("Error: something terrible happened to the internal cardbase data structure. Oops."); - if (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); } catch (IOException e) { System.out.println("Error: lost contact with the output file, try again?"); - if (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); } } } else { @@ -258,7 +251,7 @@ public class CardbaseCLI { * Print a list of valid set codes. */ public void sets() { - for (CardSet set : MTGUniverse.getCardSetList()) { + for (CardSetInformation set : MTGUniverse.getCardSetList()) { // CardSet has an overridden toString() System.out.println(set); } @@ -283,13 +276,13 @@ public class CardbaseCLI { } } catch (JsonParseException e) { System.out.println("Error: JSON fetched from upstream was not formatted properly."); - if (debug) e.printStackTrace(); + 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 (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); } catch (IOException e) { System.out.println("Error: JSON could not be fetched from upstream."); - if (debug) e.printStackTrace(); + if (Cardbase.DEBUG) e.printStackTrace(); } } else { System.out.println("Please enter a set code (use \"sets\" to see all valid set codes)."); @@ -300,12 +293,10 @@ public class CardbaseCLI { * 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; + for (Card card : cardbase.getCards()) { + printGlance(card); + total += card.count; } System.out.println("Total: " + total); } @@ -319,7 +310,7 @@ public class CardbaseCLI { // if a card is specified, peruse only that if (args.length > 0) { if (selectedSet != null) { - Card card = cardbaseManager.getCard(selectedSet.getCode(), args[0]); + Card card = cardbase.getCard(selectedSet.getCode(), args[0]); if (card != null) { printPerusal(card); } else { @@ -330,12 +321,10 @@ public class CardbaseCLI { } } 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; + for (Card card : cardbase.getCards()) { + printPerusal(card); + total += card.count; } System.out.println("Total: " + total); } @@ -420,6 +409,24 @@ public class CardbaseCLI { System.out.println("Select a set before adding cards."); } } + + /** + * Sort the cardbase by a specified parameter. + * + * @param args the ordering to sort by. + */ + public void sort(String[] args) { + if (args.length > 0) { + String fieldName = args[0]; + if (cardbase.sortBy(fieldName)) { + System.out.println("Cardbase sorted by " + fieldName); + } else { + System.out.println("Unknown field: " + fieldName); + } + } else { + System.out.println("Please enter the field to use for sorting."); + } + } /** * Add the specified count of the specified card @@ -429,7 +436,7 @@ public class CardbaseCLI { * @param count the number of times to add it. */ private void addCard(Card card, Integer count) { - cardbaseManager.addCard(card, count); + cardbase.addCard(card, count); System.out.println("Added " + count + "x " + card.name + "."); savePrompt = true; lastAction = Action.ADD; @@ -444,7 +451,7 @@ public class CardbaseCLI { * @param count the number of times to remove it. */ private void removeCard(Card card, Integer count) { - Integer removed = cardbaseManager.removeCard(card, count); + Integer removed = cardbase.removeCard(card, count); if (removed > 0) { System.out.println("Removed " + removed + "x " + card.name + "."); savePrompt = true; diff --git a/src/eu/equalparts/cardbase/utils/IO.java b/src/eu/equalparts/cardbase/utils/IO.java deleted file mode 100644 index 5d4bef5..0000000 --- a/src/eu/equalparts/cardbase/utils/IO.java +++ /dev/null @@ -1,35 +0,0 @@ -package eu.equalparts.cardbase.utils; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; - -/** - * This class simply holds an {@code ObjectMapper} to be used whenever JSON must be parsed. - * In the future it may be removed in favour of individual mappers for each function. - * - * @author Eduardo Pedroni - */ -public final class IO { - - /** - * The Jackson {@code ObjectMapper} which parses fetched JSON files. - */ - public static final ObjectMapper jsonMapper = 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; - } -} diff --git a/src/eu/equalparts/cardbase/utils/JSON.java b/src/eu/equalparts/cardbase/utils/JSON.java new file mode 100644 index 0000000..a5992c7 --- /dev/null +++ b/src/eu/equalparts/cardbase/utils/JSON.java @@ -0,0 +1,35 @@ +package eu.equalparts.cardbase.utils; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * This class simply holds an {@code ObjectMapper} to be used whenever JSON must be parsed. + * In the future it may be removed in favour of individual mappers for each function. + * + * @author Eduardo Pedroni + */ +public final class JSON { + + /** + * The Jackson {@code ObjectMapper} which parses fetched JSON files. + */ + public static final ObjectMapper mapper = createMapper(); + + /** + * Private constructor, this class is not to be instantiated. + */ + private JSON() {} + + /** + * 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; + } +} diff --git a/src/eu/equalparts/cardbase/utils/MTGUniverse.java b/src/eu/equalparts/cardbase/utils/MTGUniverse.java index 7211f47..0bcda5c 100644 --- a/src/eu/equalparts/cardbase/utils/MTGUniverse.java +++ b/src/eu/equalparts/cardbase/utils/MTGUniverse.java @@ -10,7 +10,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonMappingException; import eu.equalparts.cardbase.data.Card; -import eu.equalparts.cardbase.data.CardSet; +import eu.equalparts.cardbase.data.CardSetInformation; import eu.equalparts.cardbase.data.FullCardSet; /** @@ -37,7 +37,7 @@ public final class MTGUniverse { /** * A cache of CardSets to avoid querying the server many times for the same information. */ - private static ArrayList cardSets; + private static ArrayList cardSets; /** * A cache of {@code FullCardSets} to avoid querying the server many times for the same information. @@ -76,6 +76,9 @@ public final class MTGUniverse { /** * Returns the specified set in the form of a {@code FullCardSet} object. If the specified * set code does not correspond to a set, this returns null. + *
+ * This method takes care of case differences in set code names. + * * * @param setCode the code of the set to be returned. * @return the requested {@code FullCardSet} or null if no set matches the given code. @@ -94,7 +97,7 @@ public final class MTGUniverse { } // not cached; fetch, cache and return it else { - requestedSet = IO.jsonMapper.readValue(new URL(BASE_URL + validCode + ".json"), FullCardSet.class); + requestedSet = JSON.mapper.readValue(new URL(BASE_URL + validCode + ".json"), FullCardSet.class); // MTG JSON does not include set code in the card information, but it is useful for sorting for (Card card : requestedSet.getCards()) { card.setCode = validCode; @@ -108,17 +111,17 @@ public final class MTGUniverse { /** * @return a list of all card sets in the form of {@code CardSet} objects. */ - public static ArrayList getCardSetList() { + public static ArrayList getCardSetList() { // if the list isn't cached, fetch and cache it if (cardSets == null) { try { - cardSets = IO.jsonMapper.readValue(new URL(BASE_URL + "SetList.json"), new TypeReference>() {}); + cardSets = JSON.mapper.readValue(new URL(BASE_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(); try { - cardSets = IO.jsonMapper.readValue(MTGUniverse.class.getResourceAsStream(FALLBACK_LIST_PATH), new TypeReference>() {}); + cardSets = JSON.mapper.readValue(MTGUniverse.class.getResourceAsStream(FALLBACK_LIST_PATH), new TypeReference>() {}); } catch (Exception f) { System.out.println("Error: could not parse fallback set code list, aborting..."); f.printStackTrace(); @@ -142,7 +145,7 @@ public final class MTGUniverse { * @return the valid form of the set code if any, null otherwise. */ public static String validateSetCode(String setCode) { - for (CardSet cardSet : getCardSetList()) { + for (CardSetInformation cardSet : getCardSetList()) { if (cardSet.getCode().equalsIgnoreCase(setCode)) { return cardSet.getCode(); } -- cgit v1.2.3