From 4816a489e476c324155fa1f4e8adfe30867a766c Mon Sep 17 00:00:00 2001 From: Eduardo Pedroni Date: Sat, 6 Jun 2015 22:12:41 +0200 Subject: Bombproofed the CLI a bit more, tidied up the underlying API, now thinking about decks --- src/eu/equalparts/cardbase/utils/IO.java | 35 +++++ src/eu/equalparts/cardbase/utils/MTGUniverse.java | 152 ++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 src/eu/equalparts/cardbase/utils/IO.java create mode 100644 src/eu/equalparts/cardbase/utils/MTGUniverse.java (limited to 'src/eu/equalparts/cardbase/utils') 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. + *
+ * 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 cardSets; + + /** + * A cache of {@code FullCardSets} to avoid querying the server many times for the same information. + */ + private static HashMap cardSetCache = new HashMap(); + + /** + * 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 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>() {}); + } 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>() {}); + } 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. + *
+ * 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; + } +} -- cgit v1.2.3