diff options
author | Eduardo Pedroni <e.pedroni91@gmail.com> | 2015-06-06 22:12:41 +0200 |
---|---|---|
committer | Eduardo Pedroni <e.pedroni91@gmail.com> | 2015-06-06 22:12:41 +0200 |
commit | 4816a489e476c324155fa1f4e8adfe30867a766c (patch) | |
tree | 87776851bba9f6611093cb276ca67af8eaa68501 /src | |
parent | 1b5b8f11d0dd33dc41c5c5a6841307fbb392f4c7 (diff) |
Bombproofed the CLI a bit more, tidied up the underlying API, now thinking about decks
Diffstat (limited to 'src')
-rw-r--r-- | src/eu/equalparts/cardbase/data/CardSet.java | 13 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/data/Cardbase.java | 6 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/data/CardbaseManager.java | 129 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/data/FullCardSet.java | 12 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/io/IO.java | 119 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/standalone/CardbaseCLI.java | 83 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/utils/IO.java | 35 | ||||
-rw-r--r-- | src/eu/equalparts/cardbase/utils/MTGUniverse.java | 152 |
8 files changed, 299 insertions, 250 deletions
diff --git a/src/eu/equalparts/cardbase/data/CardSet.java b/src/eu/equalparts/cardbase/data/CardSet.java index b9720bf..b06be7c 100644 --- a/src/eu/equalparts/cardbase/data/CardSet.java +++ b/src/eu/equalparts/cardbase/data/CardSet.java @@ -2,26 +2,26 @@ package eu.equalparts.cardbase.data; public class CardSet { - private String name = ""; - private String code = ""; - private String releaseDate = ""; + private String name; + private String code; + private String releaseDate; /** - * @return the name + * @return the set's name. */ public String getName() { return name; } /** - * @return the code + * @return the set code. */ public String getCode() { return code; } /** - * @return the releaseDate + * @return the set's release date. */ public String getReleaseDate() { return releaseDate; @@ -31,5 +31,4 @@ public class CardSet { 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 index 1c2aa12..021fac2 100644 --- a/src/eu/equalparts/cardbase/data/Cardbase.java +++ b/src/eu/equalparts/cardbase/data/Cardbase.java @@ -8,9 +8,9 @@ public class Cardbase { public ArrayList<Deck> decks = new ArrayList<>(); /** - * @param setCode - * @param number - * @return the card if found, else 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 getCardByNumber(String setCode, String number) { for (Card card : cards) { diff --git a/src/eu/equalparts/cardbase/data/CardbaseManager.java b/src/eu/equalparts/cardbase/data/CardbaseManager.java index dfef3c8..d6ba6ee 100644 --- a/src/eu/equalparts/cardbase/data/CardbaseManager.java +++ b/src/eu/equalparts/cardbase/data/CardbaseManager.java @@ -1,122 +1,93 @@ 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.io.IO; +import eu.equalparts.cardbase.utils.IO; +/** + * Provides a variety of utility methods to interact with the loaded cardbase. + * + * @author Eduardo Pedroni + */ public class CardbaseManager { - private ArrayList<CardSet> cardSets; - - /** - * A cache of CardSets to avoid querying the server many times for the same information. - */ - private HashMap<String, FullCardSet> cardSetCache = new HashMap<String, FullCardSet>(); /** - * + * The cardbase being managed. */ private Cardbase cardbase; - /** - * Parse a cardbase file and create an associated Cardbase object. - * - * @param cardbaseFile + * Creates an empty cardbase. * - * @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 CardbaseManager(File cardbaseFile) throws JsonParseException, JsonMappingException, IOException { - cardSets = IO.getCardSetList(); - cardbase = IO.readCardbase(cardbaseFile); + public CardbaseManager() { + cardbase = new Cardbase(); } - + /** - * Create an empty Cardbase. + * Initialises the cardbase with the contents of a file. + * + * @param cardbaseFile the cardbase JSON to load. * - * @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 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() throws JsonParseException, JsonMappingException, IOException { - cardSets = IO.getCardSetList(); - cardbase = new Cardbase(); - } - - public ArrayList<CardSet> getCardSetList() { - return cardSets; + public CardbaseManager(File cardbaseFile) throws JsonParseException, JsonMappingException, IOException { + cardbase = IO.jsonMapper.readValue(cardbaseFile, Cardbase.class); } - 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. + * Writes the provided {@code Cardbase} to the provided file in JSON format. * - * @param code the code of the set to be returned. - * @return the requested {@code FullCardSet} or null if no set matches the given code. + * @param file the file to which to write the {@code Cardbase}. + * @param cardbase the {@code Cardbase} to write out. * - * @throws JsonParseException if the upstream JSON is not formatted correctly. - * @throws JsonMappingException if the upstream JSON does not map to {@code FullCardSet}. + * @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 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; + public void writeCardbase(File outputFile) throws JsonGenerationException, JsonMappingException, IOException { + IO.jsonMapper.writeValue(outputFile, cardbase); } /** - * Add a specific amount of a card to the 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 newCard - * @param count + * @param cardToAdd the card to be added. + * @param count the amount of the card to be added. */ - public void addCard(Card newCard, Integer count) { - Card card = cardbase.getCardByNumber(newCard.setCode, newCard.number); + public void addCard(Card cardToAdd, Integer count) { + Card card = cardbase.getCardByNumber(cardToAdd.setCode, cardToAdd.number); if (card != null) { card.count += count; } else { - newCard.count = count; - cardbase.cards.add(newCard); + cardToAdd.count = count; + cardbase.cards.add(cardToAdd); } } /** - * Remove a specific amount of a card from the cardbase. + * 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 card, the specified amount is removed. + * 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. + * the card entry is removed altogether. + * <br><br> + * 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 - * @param count + * @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) { @@ -135,21 +106,21 @@ public class CardbaseManager { } /** - * @return an iterator to the cards in the cardbase. + * @return an iterator to the {@code Card}s in the cardbase. */ public Iterator<Card> cardIterator() { return cardbase.cards.iterator(); } /** - * Return a card from the cardBase by setCode and number. - * If no such card is in the cardbase, return null. + * Returns a card from the cardbase by set code and number. + * If no such card is in the cardbase, returns null. * - * @param code - * @param string - * @return + * @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 code, String number) { - return cardbase.getCardByNumber(code, number); + 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 48488b5..0f58633 100644 --- a/src/eu/equalparts/cardbase/data/FullCardSet.java +++ b/src/eu/equalparts/cardbase/data/FullCardSet.java @@ -11,35 +11,35 @@ public class FullCardSet extends CardSet { private ArrayList<Card> cards; /** - * @return the border + * @return the set's border type. */ public String getBorder() { return border; } /** - * @return the type + * @return the type of the set. */ public String getType() { return type; } /** - * @return the block + * @return the set's block. */ public String getBlock() { return block; } /** - * @return the gathererCode + * @return the set's Gatherer code. */ public String getGathererCode() { return gathererCode; } /** - * @return the cards + * @return a full list of the set's cards. */ public ArrayList<Card> getCards() { return cards; @@ -49,7 +49,7 @@ public class FullCardSet extends CardSet { * Searches for a card by number (the one shown on the card itself). * * @param number the number of the card to search. - * @return the card, or null if no card is found with that number. + * @return the requested {@code Card}, or null if no card is found with that number. */ public Card getCardByNumber(String number) { for (Card card : cards) { diff --git a/src/eu/equalparts/cardbase/io/IO.java b/src/eu/equalparts/cardbase/io/IO.java deleted file mode 100644 index 2275913..0000000 --- a/src/eu/equalparts/cardbase/io/IO.java +++ /dev/null @@ -1,119 +0,0 @@ -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. - * <br> - * 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<CardSet> getCardSetList() throws JsonParseException, JsonMappingException, IOException { - return mapper.readValue(new URL(SETS_URL), new TypeReference<ArrayList<CardSet>>() {}); - } - - /** - * 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 e0f5433..a8b11ba 100644 --- a/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java +++ b/src/eu/equalparts/cardbase/standalone/CardbaseCLI.java @@ -17,6 +17,7 @@ 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.utils.MTGUniverse; /** * This provides a lightweight CLI for interacting with cardbase files. @@ -24,18 +25,16 @@ import eu.equalparts.cardbase.data.CardSet; * @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. * @@ -49,6 +48,10 @@ public class CardbaseCLI { } /** + * Location of the help file. + */ + private static final String HELP_FILE_PATH = "/help"; + /** * The last action performed by the user. */ private Action lastAction = null; @@ -61,7 +64,7 @@ public class CardbaseCLI { */ private CardbaseManager cardbaseManager; /** - * Printed to the console when the user enter the help command. + * Printed to the console when the user enters the help command. */ private String help = "Not available, check project page on GitHub."; /** @@ -69,6 +72,11 @@ public class CardbaseCLI { */ 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. */ @@ -84,19 +92,7 @@ public class CardbaseCLI { * @param args the first argument is the cardbase file. Further arguments are ignored. */ public static void main(String... args) { - try { - 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. - e.printStackTrace(); - } catch (JsonMappingException e) { - System.out.println("Error: unexpected fields found in cardbase, it may be from an old version?"); - e.printStackTrace(); - } catch (IOException e) { - System.out.println("Error: something went wrong reading a file, abort..."); - e.printStackTrace(); - } + new CardbaseCLI(args).startInterface(); } /** @@ -105,19 +101,37 @@ public class CardbaseCLI { * 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 { + private CardbaseCLI(String... args) { 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; + } + // 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] + "\"."); - cardbaseManager = new CardbaseManager(cardbaseFile); + try { + cardbaseManager = new CardbaseManager(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(); + 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(); + System.exit(1); + } catch (IOException e) { + System.out.println("Error: something went wrong reading cardbase file, abort..."); + if (debug) e.printStackTrace(); + System.exit(1); + } } else { System.out.println(args[0] + " appears to be invalid."); System.exit(0); @@ -128,7 +142,7 @@ public class CardbaseCLI { } // load help information - InputStream is = CardbaseCLI.class.getResourceAsStream("/help"); + InputStream is = CardbaseCLI.class.getResourceAsStream(HELP_FILE_PATH); if (is != null) { help = new Scanner(is).useDelimiter("\\Z").next(); } else { @@ -178,7 +192,7 @@ public class CardbaseCLI { } } catch (IOException e) { System.out.println("Error: something went wrong with stdin, exiting..."); - e.printStackTrace(); + if (debug) e.printStackTrace(); } } @@ -217,10 +231,10 @@ public class CardbaseCLI { savePrompt = false; } catch (JsonGenerationException | JsonMappingException e) { System.out.println("Error: something terrible happened to the internal cardbase data structure. Oops."); - e.printStackTrace(); + if (debug) e.printStackTrace(); } catch (IOException e) { System.out.println("Error: lost contact with the output file, try again?"); - e.printStackTrace(); + if (debug) e.printStackTrace(); } } } else { @@ -244,8 +258,8 @@ public class CardbaseCLI { * Print a list of valid set codes. */ public void sets() { - for (CardSet set : cardbaseManager.getCardSetList()) { - // CardSet has an overridden toString(). + for (CardSet set : MTGUniverse.getCardSetList()) { + // CardSet has an overridden toString() System.out.println(set); } } @@ -258,7 +272,7 @@ public class CardbaseCLI { public void set(String[] args) { if (args.length > 0) { try { - selectedSet = cardbaseManager.getFullCardSet(args[0]); + selectedSet = MTGUniverse.getFullCardSet(args[0]); // if the set code is invalid, null is returned if (selectedSet != null) { System.out.println("Selected set: " + selectedSet.getName() + "."); @@ -269,16 +283,13 @@ public class CardbaseCLI { } } catch (JsonParseException e) { System.out.println("Error: JSON fetched from upstream was not formatted properly."); - e.printStackTrace(); - return; + if (debug) e.printStackTrace(); } catch (JsonMappingException e) { System.out.println("Error: JSON fetched from upstream does not match the data structure used internally."); - e.printStackTrace(); - return; + if (debug) e.printStackTrace(); } catch (IOException e) { System.out.println("Error: JSON could not be fetched from upstream."); - e.printStackTrace(); - return; + if (debug) e.printStackTrace(); } } else { System.out.println("Please enter a set code (use \"sets\" to see all valid set codes)."); @@ -442,7 +453,7 @@ public class CardbaseCLI { } else { System.out.println(card.name + " is not in the cardbase."); } - + } /** diff --git a/src/eu/equalparts/cardbase/utils/IO.java b/src/eu/equalparts/cardbase/utils/IO.java new file mode 100644 index 0000000..5d4bef5 --- /dev/null +++ b/src/eu/equalparts/cardbase/utils/IO.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 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/MTGUniverse.java b/src/eu/equalparts/cardbase/utils/MTGUniverse.java new file mode 100644 index 0000000..7211f47 --- /dev/null +++ b/src/eu/equalparts/cardbase/utils/MTGUniverse.java @@ -0,0 +1,152 @@ +package eu.equalparts.cardbase.utils; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; + +import com.fasterxml.jackson.core.JsonParseException; +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.FullCardSet; + +/** + * Access point to the complete set of cards that exist in the + * MTG universe. This class has a series of utility functions that + * query remote databases to acquire card information. + * <br> + * Conversely, {@code CardbaseManager}'s methods are used solely to + * acquire information regarding the loaded cardbase, which will + * most likely contain only a subset of the MTG universe. + * + * @author Eduardo Pedroni + */ +public final class MTGUniverse { + + /** + * The base URL from where the information is fetched. + */ + private static final String BASE_URL = "http://mtgjson.com/json/"; + /** + * If the upstream set code list can't be loaded, this is loaded instead. + */ + private static final String FALLBACK_LIST_PATH = "/setlist.json"; + /** + * A cache of CardSets to avoid querying the server many times for the same information. + */ + private static ArrayList<CardSet> cardSets; + + /** + * A cache of {@code FullCardSets} to avoid querying the server many times for the same information. + */ + private static HashMap<String, FullCardSet> cardSetCache = new HashMap<String, FullCardSet>(); + + /** + * Private constructor, this class is not to be instantiated. + */ + private MTGUniverse() {} + + /** + * Returns the specified card in the form of a {@code Card} object. If the specified number does + * not correspond to a card in the set or the specified set code does not correspond to a set, + * this 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. + * + * @throws JsonParseException if the upstream JSON is not formatted correctly. + * @throws JsonMappingException if the upstream JSON does not map to a local class. + * @throws IOException if a low-level I/O problem (unexpected end-of-input, network error) occurs. + */ + public static Card getCard(String setCode, String number) throws JsonParseException, JsonMappingException, IOException { + Card card = null; + FullCardSet fullCardSet = getFullCardSet(setCode); + + if (fullCardSet != null) { + card = fullCardSet.getCardByNumber(number); + } + + return card; + } + + /** + * 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. + * + * @param setCode 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 static FullCardSet getFullCardSet(String setCode) throws JsonParseException, JsonMappingException, IOException { + FullCardSet requestedSet = null; + String validCode = validateSetCode(setCode); + if (validCode != null) { + // if the set is cached, no need to fetch + if (cardSetCache.containsKey(validCode)) { + requestedSet = cardSetCache.get(validCode); + } + // not cached; fetch, cache and return it + else { + requestedSet = IO.jsonMapper.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; + } + cardSetCache.put(validCode, requestedSet); + } + } + return requestedSet; + } + + /** + * @return a list of all card sets in the form of {@code CardSet} objects. + */ + public static ArrayList<CardSet> 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<ArrayList<CardSet>>() {}); + } 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<ArrayList<CardSet>>() {}); + } catch (Exception f) { + System.out.println("Error: could not parse fallback set code list, aborting..."); + f.printStackTrace(); + System.exit(1); + } + } + } + return cardSets; + } + + /** + * This method effectively converts different set code spellings + * into the format parsed from the set code list. + * <br> + * For instance, if "m15" is passed as the argument, this returns + * "M15", which is the set code as it is stated in the formal list. + * If the specified set code does not approximate any entry from + * the list, this returns null. + * + * @param setCode the set code to be validated. + * @return the valid form of the set code if any, null otherwise. + */ + public static String validateSetCode(String setCode) { + for (CardSet cardSet : getCardSetList()) { + if (cardSet.getCode().equalsIgnoreCase(setCode)) { + return cardSet.getCode(); + } + } + return null; + } +} |