mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
Merge branch 'patch-carddb-performance' into 'master'
CardDb Optimisations See merge request core-developers/forge!5258
This commit is contained in:
@@ -2,13 +2,11 @@ package forge;
|
||||
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.FileUtil;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.TextUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
public final class ImageKeys {
|
||||
public static final String CARD_PREFIX = "c:";
|
||||
@@ -68,9 +66,8 @@ public final class ImageKeys {
|
||||
}
|
||||
|
||||
public static File getImageFile(String key) {
|
||||
if (StringUtils.isEmpty(key)) {
|
||||
if (StringUtils.isEmpty(key))
|
||||
return null;
|
||||
}
|
||||
|
||||
final String dir;
|
||||
final String filename;
|
||||
@@ -220,14 +217,35 @@ public final class ImageKeys {
|
||||
|
||||
//shortcut for determining if a card image exists for a given card
|
||||
//should only be called from PaperCard.hasImage()
|
||||
static HashMap<String, HashSet<String>> cachedContent=new HashMap<>();
|
||||
public static boolean hasImage(PaperCard pc) {
|
||||
Boolean editionHasImage = editionImageLookup.get(pc.getEdition());
|
||||
if (editionHasImage == null) {
|
||||
String setFolder = getSetFolder(pc.getEdition());
|
||||
editionHasImage = FileUtil.isDirectoryWithFiles(CACHE_CARD_PICS_DIR + setFolder);
|
||||
editionImageLookup.put(pc.getEdition(), editionHasImage);
|
||||
if (editionHasImage){
|
||||
File f = new File(CACHE_CARD_PICS_DIR + setFolder); // no need to check this, otherwise editionHasImage would be false!
|
||||
HashSet<String> setFolderContent = new HashSet<>();
|
||||
for (String filename : Arrays.asList(f.list())) {
|
||||
// TODO: should this use FILE_EXTENSIONS ?
|
||||
if (!filename.endsWith(".jpg") && !filename.endsWith(".png"))
|
||||
continue; // not image - not interested
|
||||
setFolderContent.add(filename.split("\\.")[0]); // get rid of any full or fullborder
|
||||
}
|
||||
cachedContent.put(setFolder, setFolderContent);
|
||||
}
|
||||
}
|
||||
String[] keyParts = pc.getImageKeyFromSet().split(File.separator);
|
||||
HashSet<String> content = cachedContent.getOrDefault(keyParts[0], null);
|
||||
//avoid checking for file if edition doesn't have any images
|
||||
return editionHasImage && getImageFile(ImageUtil.getImageKey(pc, false, true)) != null;
|
||||
return editionHasImage && hitCache(content, keyParts[1]);
|
||||
}
|
||||
|
||||
private static boolean hitCache(HashSet<String> cache, String filename){
|
||||
if (cache == null || cache.isEmpty())
|
||||
return false;
|
||||
final String keyPrefix = filename.split("\\.")[0];
|
||||
return cache.contains(keyPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,12 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
this.collectorNumber = collectorNumber;
|
||||
}
|
||||
|
||||
public static String compose(String cardName, boolean isFoil){
|
||||
if (isFoil)
|
||||
return cardName+foilSuffix;
|
||||
return cardName;
|
||||
}
|
||||
|
||||
public static String compose(String cardName, String setCode) {
|
||||
setCode = setCode != null ? setCode : "";
|
||||
cardName = cardName != null ? cardName : "";
|
||||
@@ -135,7 +141,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
|
||||
private static boolean isArtIndex(String s) {
|
||||
return StringUtils.isNumeric(s) && s.length() == 1;
|
||||
return StringUtils.isNumeric(s) && s.length() <= 2 ; // only artIndex between 1-99
|
||||
}
|
||||
|
||||
private static boolean isSetCode(String s) {
|
||||
@@ -462,15 +468,15 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
|
||||
// 2. Card lookup in edition with specified filter didn't work.
|
||||
// So now check whether the cards exists in the DB first,
|
||||
// So now check whether the cards exist in the DB first,
|
||||
// and select pick the card based on current SetPreference policy as a fallback
|
||||
Collection<PaperCard> cards = getAllCards(request.cardName);
|
||||
if (cards == null)
|
||||
if (cards.isEmpty()) // Never null being this a view in MultiMap
|
||||
return null;
|
||||
// Either No Edition has been specified OR as a fallback in case of any error!
|
||||
// get card using the default card art preference
|
||||
result = getCardFromEditions(request.cardName, this.defaultCardArtPreference, request.artIndex);
|
||||
return result != null && request.isFoil ? result.getFoiled() : result;
|
||||
String cardRequest = CardRequest.compose(request.cardName, request.isFoil);
|
||||
return getCardFromEditions(cardRequest, this.defaultCardArtPreference, request.artIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -510,9 +516,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
cardName = cardNameRequest.cardName;
|
||||
isFoil = isFoil || cardNameRequest.isFoil;
|
||||
|
||||
List<PaperCard> cards = getAllCards(cardName);
|
||||
// Look for Code or Code2 to make the retrieval more robust
|
||||
List<PaperCard> candidates = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
|
||||
List<PaperCard> candidates = getAllCards(cardName, new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard c) {
|
||||
boolean artIndexFilter = true;
|
||||
@@ -526,21 +530,17 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
collectorNumberFilter = (c.getCollectorNumber().equals(collectorNumber));
|
||||
return setFilter && artIndexFilter && collectorNumberFilter;
|
||||
}
|
||||
}));
|
||||
});
|
||||
if (candidates.isEmpty())
|
||||
return null;
|
||||
|
||||
PaperCard candidate = candidates.get(0);
|
||||
Iterator<PaperCard> candidatesIterator = candidates.iterator();
|
||||
PaperCard candidate = candidatesIterator.next();
|
||||
// Before returning make sure that actual candidate has Image.
|
||||
// If not, try to replace current candidate with one having image,
|
||||
// so to align this implementation with old one.
|
||||
if (!candidate.hasImage()) {
|
||||
for (PaperCard card : candidates) {
|
||||
if (card.hasImage()) {
|
||||
candidate = card;
|
||||
break; // found, ready to go
|
||||
}
|
||||
}
|
||||
while (!candidate.hasImage() && candidatesIterator.hasNext()) {
|
||||
candidate = candidatesIterator.next();
|
||||
}
|
||||
return isFoil ? candidate.getFoiled() : candidate;
|
||||
}
|
||||
@@ -567,8 +567,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaperCard getCardFromEditions(final String cardName, final CardArtPreference artPreference, int artIndex) {
|
||||
return this.tryToGetCardFromEditions(cardName, artPreference, artIndex);
|
||||
public PaperCard getCardFromEditions(final String cardInfo, final CardArtPreference artPreference, int artIndex) {
|
||||
return this.tryToGetCardFromEditions(cardInfo, artPreference, artIndex);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -634,38 +634,52 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
if (cr.artIndex != artIndex && artIndex > IPaperCard.DEFAULT_ART_INDEX )
|
||||
cr.artIndex = artIndex; // 2nd cond. is to verify that some actual value has been passed in.
|
||||
|
||||
List<PaperCard> cards = getAllCards(cr.cardName);
|
||||
List<PaperCard> cards;
|
||||
if (releaseDate != null) {
|
||||
cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
|
||||
cards = getAllCards(cr.cardName, new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard c) {
|
||||
if (c.getArtIndex() != cr.artIndex)
|
||||
return false; // not interested anyway!
|
||||
CardEdition ed = editions.get(c.getEdition());
|
||||
if (ed == null) return false;
|
||||
if (releasedBeforeFlag)
|
||||
return ed.getDate().before(releaseDate);
|
||||
else
|
||||
return ed.getDate().after(releaseDate);
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
} else // filter candidates based on requested artIndex
|
||||
cards = getAllCards(cr.cardName, new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard card) {
|
||||
return card.getArtIndex() == cr.artIndex;
|
||||
}
|
||||
});
|
||||
|
||||
if (cards.size() == 0) // Don't bother continuing! No card has been found!
|
||||
return null;
|
||||
if (cards.size() == 1) // if only one candidate, there much else we should do
|
||||
return cr.isFoil ? cards.get(0).getFoiled() : cards.get(0);
|
||||
|
||||
/* 2. Retrieve cards based of [Frame]Set Preference
|
||||
================================================ */
|
||||
|
||||
// Collect the list of all editions found for target card
|
||||
LinkedHashSet<CardEdition> cardEditions = new LinkedHashSet<>();
|
||||
List<CardEdition> cardEditions = new ArrayList<>();
|
||||
Map<String, PaperCard> candidatesCard = new HashMap<>();
|
||||
for (PaperCard card : cards) {
|
||||
String setCode = card.getEdition();
|
||||
CardEdition ed;
|
||||
if (setCode.equals(CardEdition.UNKNOWN.getCode()))
|
||||
cardEditions.add(CardEdition.UNKNOWN);
|
||||
else {
|
||||
CardEdition ed = editions.get(card.getEdition());
|
||||
if (ed != null)
|
||||
cardEditions.add(ed);
|
||||
ed = CardEdition.UNKNOWN;
|
||||
else
|
||||
ed = editions.get(card.getEdition());
|
||||
if (ed != null) {
|
||||
cardEditions.add(ed);
|
||||
candidatesCard.put(setCode, card);
|
||||
}
|
||||
}
|
||||
if (cardEditions.isEmpty())
|
||||
return null; // nothing to do
|
||||
|
||||
// Filter Cards Editions based on set preferences
|
||||
List<CardEdition> acceptedEditions = Lists.newArrayList(Iterables.filter(cardEditions, new Predicate<CardEdition>() {
|
||||
@Override
|
||||
@@ -681,26 +695,24 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
If this happens, we won't try to iterate over an empty list. Instead, we will fall back
|
||||
to original lists of editions (unfiltered, of course) AND STILL sorted according to chosen art preference.
|
||||
*/
|
||||
if (acceptedEditions.size() == 0)
|
||||
if (acceptedEditions.isEmpty())
|
||||
acceptedEditions.addAll(cardEditions);
|
||||
|
||||
Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date
|
||||
if (artPref.latestFirst)
|
||||
Collections.reverse(acceptedEditions); // newest editions first
|
||||
PaperCard candidate = null;
|
||||
for (CardEdition ed : acceptedEditions) {
|
||||
PaperCard cardFromSet = getCardFromSet(cr.cardName, ed, cr.artIndex, cr.isFoil);
|
||||
if (candidate == null && cardFromSet != null)
|
||||
// save the first card found, as the last backup in case no other candidate *with image* will be found
|
||||
candidate = cardFromSet;
|
||||
|
||||
if (cardFromSet != null && cardFromSet.hasImage()) {
|
||||
candidate = cardFromSet;
|
||||
break; // we're done here: found card **with Image**
|
||||
}
|
||||
if (acceptedEditions.size() > 1) {
|
||||
Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date
|
||||
if (artPref.latestFirst)
|
||||
Collections.reverse(acceptedEditions); // newest editions first
|
||||
}
|
||||
//If any, we're sure that at least one candidate is always returned nevertheless it has image or not
|
||||
return candidate; // any foil request already handled in getCardFromSet
|
||||
|
||||
final Iterator<CardEdition> editionIterator = acceptedEditions.iterator();
|
||||
CardEdition ed = editionIterator.next();
|
||||
PaperCard candidate = candidatesCard.get(ed.getCode());
|
||||
while (!candidate.hasImage() && editionIterator.hasNext()) {
|
||||
ed = editionIterator.next();
|
||||
candidate = candidatesCard.get(ed.getCode());
|
||||
}
|
||||
//If any, we're sure that at least one candidate is always returned despite it having any image
|
||||
return cr.isFoil ? candidate.getFoiled() : candidate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -716,18 +728,16 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArtCount(String cardName, String setName) {
|
||||
if (cardName == null || setName == null)
|
||||
public int getArtCount(String cardName, String setCode) {
|
||||
if (cardName == null || setCode == null)
|
||||
return 0;
|
||||
Collection<PaperCard> cards = getAllCards(cardName);
|
||||
if (null == cards || cards.size() == 0)
|
||||
return 0;
|
||||
int artCount = 0;
|
||||
for (PaperCard pc : cards) {
|
||||
if (pc.getEdition().equalsIgnoreCase(setName))
|
||||
artCount++;
|
||||
}
|
||||
return artCount;
|
||||
Collection<PaperCard> cardsInSet = getAllCards(cardName, new Predicate<PaperCard>() {
|
||||
@Override
|
||||
public boolean apply(PaperCard card) {
|
||||
return card.getEdition().equalsIgnoreCase(setCode);
|
||||
}
|
||||
});
|
||||
return cardsInSet.size();
|
||||
}
|
||||
|
||||
// returns a list of all cards from their respective latest (or preferred) editions
|
||||
@@ -834,6 +844,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
return Lists.newArrayList(Iterables.filter(getAllCards(), predicate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PaperCard> getAllCards(final String cardName, Predicate<PaperCard> predicate){
|
||||
return Lists.newArrayList(Iterables.filter(getAllCards(cardName), predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a modifiable list of cards matching the given predicate
|
||||
*/
|
||||
@@ -1006,29 +1021,33 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
|
||||
// 1. generate all paper cards from edition data we have (either explicit, or found in res/editions, or add to unknown edition)
|
||||
List<PaperCard> paperCards = new ArrayList<>();
|
||||
if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) {
|
||||
// TODO Not performant Each time we "putCard" we loop through ALL CARDS IN ALL editions
|
||||
// @friarsol: Not performant Each time we "putCard" we loop through ALL CARDS IN ALL editions
|
||||
// @leriomaggio: DONE! re-using here the same strategy implemented for lazy-loading!
|
||||
for (CardEdition e : editions.getOrderedEditions()) {
|
||||
int artIdx = IPaperCard.DEFAULT_ART_INDEX;
|
||||
for (CardInSet cis : e.getAllCardsInSet()) {
|
||||
if (!cis.name.equals(cardName)) {
|
||||
continue;
|
||||
}
|
||||
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++));
|
||||
}
|
||||
for (CardInSet cis : e.getCardInSet(cardName))
|
||||
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++, false,
|
||||
cis.collectorNumber, cis.artistName));
|
||||
}
|
||||
} else {
|
||||
String lastEdition = null;
|
||||
int artIdx = 0;
|
||||
for (Pair<String, CardRarity> tuple : whenItWasPrinted) {
|
||||
if (!tuple.getKey().equals(lastEdition)) {
|
||||
artIdx = IPaperCard.DEFAULT_ART_INDEX;
|
||||
artIdx = IPaperCard.DEFAULT_ART_INDEX; // reset artIndex
|
||||
lastEdition = tuple.getKey();
|
||||
}
|
||||
CardEdition ed = editions.get(lastEdition);
|
||||
if (null == ed) {
|
||||
if (ed == null) {
|
||||
continue;
|
||||
}
|
||||
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++));
|
||||
List<CardInSet> cardsInSet = ed.getCardInSet(cardName);
|
||||
if (cardsInSet.isEmpty())
|
||||
continue;
|
||||
int cardInSetIndex = Math.max(artIdx-1, 0); // make sure doesn't go below zero
|
||||
CardInSet cds = cardsInSet.get(cardInSetIndex); // use ArtIndex to get the right Coll. Number
|
||||
paperCards.add(new PaperCard(rules, lastEdition, tuple.getValue(), artIdx++, false,
|
||||
cds.collectorNumber, cds.artistName));
|
||||
}
|
||||
}
|
||||
if (paperCards.isEmpty()) {
|
||||
|
||||
@@ -373,6 +373,13 @@ public final class CardEdition implements Comparable<CardEdition> {
|
||||
}
|
||||
|
||||
private ListMultimap<String, CardInSet> cardsInSetLookupMap = null;
|
||||
|
||||
/**
|
||||
* Get all the CardInSet instances with the input card name.
|
||||
* @param cardName Name of the Card to look for.
|
||||
* @return A List of all the CardInSet instances for a given name.
|
||||
* If not fount, an Empty sequence (view) will be returned instead!
|
||||
*/
|
||||
public List<CardInSet> getCardInSet(String cardName){
|
||||
if (cardsInSetLookupMap == null) {
|
||||
// initialise
|
||||
|
||||
@@ -79,6 +79,7 @@ public interface ICardDatabase extends Iterable<PaperCard> {
|
||||
Collection<PaperCard> getAllCards();
|
||||
Collection<PaperCard> getAllCards(String cardName);
|
||||
Collection<PaperCard> getAllCards(Predicate<PaperCard> predicate);
|
||||
Collection<PaperCard> getAllCards(String cardName,Predicate<PaperCard> predicate);
|
||||
Collection<PaperCard> getAllCards(CardEdition edition);
|
||||
Collection<PaperCard> getUniqueCards();
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ public interface IPaperCard extends InventoryItem, Serializable {
|
||||
String NO_COLLECTOR_NUMBER = "N.A."; // Placeholder for No-Collection number available
|
||||
int DEFAULT_ART_INDEX = 1;
|
||||
int NO_ART_INDEX = -1; // Placeholder when NO ArtIndex is Specified
|
||||
String NO_ARTIST_NAME = "";
|
||||
|
||||
/**
|
||||
* Number of filters based on CardPrinted values.
|
||||
|
||||
@@ -25,6 +25,7 @@ import forge.card.CardEdition;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
import forge.util.CardTranslation;
|
||||
import forge.util.ImageUtil;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.TextUtil;
|
||||
|
||||
@@ -52,7 +53,7 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
By default the attribute is marked as "unset" so that it could be retrieved and set.
|
||||
(see getCollectorNumber())
|
||||
*/
|
||||
private String collectorNumber = null;
|
||||
private String collectorNumber;
|
||||
private final String artist;
|
||||
private final int artIndex;
|
||||
private final boolean foil;
|
||||
@@ -75,18 +76,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
|
||||
@Override
|
||||
public String getCollectorNumber() {
|
||||
/* The collectorNumber attribute is managed in a property-like fashion.
|
||||
By default it is marked as "unset" (-1), which integrates with all constructors
|
||||
invocations not including this as an extra parameter. In this way, the new
|
||||
attribute could be added to the API with minimum disruption to code
|
||||
throughout the other packages.
|
||||
If "unset", the corresponding collectorNumber will be retrieved
|
||||
from the corresponding CardEdition (see retrieveCollectorNumber)
|
||||
* */
|
||||
if (collectorNumber == null) {
|
||||
collectorNumber = this.retrieveCollectorNumber();
|
||||
}
|
||||
|
||||
return collectorNumber;
|
||||
}
|
||||
|
||||
@@ -152,6 +141,13 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
return hasImage;
|
||||
}
|
||||
|
||||
private String imageKeyFromSet = null;
|
||||
public String getImageKeyFromSet() {
|
||||
if (this.imageKeyFromSet == null)
|
||||
this.imageKeyFromSet = ImageUtil.getImageKey(this, false, true);
|
||||
return imageKeyFromSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lambda to get rules for selects from list of printed cards.
|
||||
*/
|
||||
@@ -169,15 +165,12 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
};
|
||||
|
||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0){
|
||||
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX);
|
||||
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false,
|
||||
IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME);
|
||||
}
|
||||
|
||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0) {
|
||||
this(rules0, edition0, rarity0, artIndex0, false, "");
|
||||
}
|
||||
|
||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0, final int artIndex0,
|
||||
final boolean foil0, final String artist0) {
|
||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
|
||||
final int artIndex0, final boolean foil0, final String collectorNumber0, final String artist0) {
|
||||
if (rules0 == null || edition0 == null || rarity0 == null) {
|
||||
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
|
||||
}
|
||||
@@ -187,16 +180,8 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
artIndex = Math.max(artIndex0, IPaperCard.DEFAULT_ART_INDEX);
|
||||
foil = foil0;
|
||||
rarity = rarity0;
|
||||
artist = (artist0 != null ? artist0 : "");
|
||||
}
|
||||
|
||||
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0,
|
||||
final int artIndex0, final boolean foil0, final String collectorNumber0, final String artist) {
|
||||
this(rules0, edition0, rarity0, artIndex0, foil0, artist);
|
||||
if ((collectorNumber0 == null) || (collectorNumber0.length() == 0))
|
||||
collectorNumber = IPaperCard.NO_COLLECTOR_NUMBER;
|
||||
else
|
||||
collectorNumber = collectorNumber0;
|
||||
artist = (artist0 != null ? artist0 : IPaperCard.NO_ARTIST_NAME);
|
||||
collectorNumber = (collectorNumber0 != null) && (collectorNumber0.length() > 0) ? collectorNumber0 : IPaperCard.NO_COLLECTOR_NUMBER;
|
||||
}
|
||||
|
||||
// Want this class to be a key for HashTable
|
||||
@@ -268,10 +253,14 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
return CardEdition.CardInSet.getSortableCollectorNumber(collectorNumber);
|
||||
}
|
||||
|
||||
private String sortableCNKey = null;
|
||||
public String getCollectorNumberSortingKey(){
|
||||
// Hardly the case, but just invoke getter rather than direct
|
||||
// attribute to be sure that collectorNumber has been retrieved already!
|
||||
return makeCollectorNumberSortingKey(getCollectorNumber());
|
||||
if (sortableCNKey == null) {
|
||||
// Hardly the case, but just invoke getter rather than direct
|
||||
// attribute to be sure that collectorNumber has been retrieved already!
|
||||
sortableCNKey = makeCollectorNumberSortingKey(getCollectorNumber());
|
||||
}
|
||||
return sortableCNKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -309,32 +298,6 @@ public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFro
|
||||
rarity = pc.getRarity();
|
||||
}
|
||||
|
||||
// FIXME: @leriomaggio - remember to get rid of this method once and for all :)
|
||||
private String retrieveCollectorNumber() {
|
||||
StaticData data = StaticData.instance();
|
||||
CardEdition edition = data.getEditions().get(this.edition);
|
||||
if (edition == null) {
|
||||
edition = data.getCustomEditions().get(this.edition);
|
||||
if (edition == null) // don't bother continuing - non-existing card!
|
||||
return NO_COLLECTOR_NUMBER;
|
||||
}
|
||||
int artIndexCount = 0;
|
||||
String collectorNumberInEdition = "";
|
||||
for (CardEdition.CardInSet card : edition.getAllCardsInSet()) {
|
||||
if (card.name.equalsIgnoreCase(this.name)) {
|
||||
artIndexCount += 1;
|
||||
if (artIndexCount == this.artIndex) {
|
||||
collectorNumberInEdition = card.collectorNumber;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// CardEdition stores collectorNumber as a String, which is null if there isn't any.
|
||||
// In this case, the NO_COLLECTOR_NUMBER value (i.e. 0) is returned.
|
||||
return ((collectorNumberInEdition != null) && (collectorNumberInEdition.length() > 0)) ?
|
||||
collectorNumberInEdition : NO_COLLECTOR_NUMBER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageKey(boolean altState) {
|
||||
String imageKey = ImageKeys.CARD_PREFIX + name + CardDb.NameSetSeparator
|
||||
|
||||
@@ -78,8 +78,10 @@ public final class FileUtil {
|
||||
}
|
||||
|
||||
public static boolean isDirectoryWithFiles(final String path) {
|
||||
if (path == null) return false;
|
||||
final File f = new File(path);
|
||||
return f.exists() && f.isDirectory() && f.list().length > 0;
|
||||
final String[] fileList = f.list();
|
||||
return fileList!=null && fileList.length > 0;
|
||||
}
|
||||
|
||||
public static boolean ensureDirectoryExists(final String path) {
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ImageUtil {
|
||||
int artIdx = cp.getArtIndex() - 1;
|
||||
if (hasManyPictures) {
|
||||
if (cntPictures <= artIdx) // prevent overflow
|
||||
artIdx = cntPictures == 0 ? 0 : artIdx % cntPictures;
|
||||
artIdx = artIdx % cntPictures;
|
||||
s.append(artIdx + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ public class ImageCache {
|
||||
if (useArtCrop) {
|
||||
if (ipc != null && ipc.getRules().getSplitType() == CardSplitType.Flip) {
|
||||
// Art crop will always use front face as image key for flip cards
|
||||
imageKey = ImageUtil.getImageKey((PaperCard) ipc, false, true);
|
||||
imageKey = ((PaperCard) ipc).getImageKeyFromSet(); // ImageUtil.getImageKey((PaperCard) ipc, false, true);
|
||||
}
|
||||
imageKey = TextUtil.fastReplace(imageKey, ".full", ".artcrop");
|
||||
}
|
||||
|
||||
@@ -536,13 +536,7 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
|
||||
CardManager cardManager = (CardManager) CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController().getDeckManager();
|
||||
PaperCard existingCard = cardManager.getSelectedItem();
|
||||
// make a foiled version based on the original
|
||||
PaperCard foiledCard = new PaperCard(
|
||||
existingCard.getRules(),
|
||||
existingCard.getEdition(),
|
||||
existingCard.getRarity(),
|
||||
existingCard.getArtIndex(),
|
||||
true,
|
||||
existingCard.getArtist());
|
||||
PaperCard foiledCard = existingCard.getFoiled();
|
||||
// remove *quantity* instances of existing card
|
||||
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, quantity);
|
||||
// add *quantity* into the deck and set them as selected
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package forge.card;
|
||||
|
||||
import forge.item.PaperCard;
|
||||
import org.testng.annotations.BeforeMethod;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
|
||||
public class CardDbPerformanceTests extends CardDbTestCase {
|
||||
|
||||
private Set<String> fullDbCardNames = new TreeSet<>();
|
||||
|
||||
@Override
|
||||
@BeforeMethod
|
||||
public void setup() {
|
||||
super.setup();
|
||||
Collection<PaperCard> uniqueCards = this.cardDb.getUniqueCards();
|
||||
for (PaperCard card : uniqueCards)
|
||||
this.fullDbCardNames.add(card.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBenchmarkFullDbGetCardLegacyImplementation() {
|
||||
int nRuns = 100;
|
||||
long averageTime = 0;
|
||||
long minTime = 10000; // 10 secs
|
||||
long maxTime = 0;
|
||||
for (int r = 1; r <= nRuns; r++) {
|
||||
long start = System.currentTimeMillis();
|
||||
for (String name : this.fullDbCardNames) {
|
||||
PaperCard card = this.legacyCardDb.getCard(name);
|
||||
assertNotNull(card);
|
||||
}
|
||||
long timeRun = System.currentTimeMillis() - start;
|
||||
averageTime += timeRun;
|
||||
if (timeRun < minTime)
|
||||
minTime = timeRun;
|
||||
if (timeRun > maxTime)
|
||||
maxTime = timeRun;
|
||||
}
|
||||
System.out.println("[LEGACY] Total Time (in sec): " + ((double) averageTime)/ 1000);
|
||||
System.out.println("[LEGACY] Average Time (in sec): " + ((double) averageTime / nRuns)/ 1000);
|
||||
System.out.println("[LEGACY] Best Time (in sec): " + ((double) minTime)/ 1000);
|
||||
System.out.println("[LEGACY] Worst Time (in sec): " + ((double) maxTime)/ 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBenchmarkFullDbGetCardNewDbImplementation() {
|
||||
int nRuns = 100;
|
||||
long averageTime = 0;
|
||||
long minTime = 10000; // 10 secs
|
||||
long maxTime = 0;
|
||||
for (int r = 1; r <= nRuns; r++) {
|
||||
long start = System.currentTimeMillis();
|
||||
for (String name : this.fullDbCardNames) {
|
||||
PaperCard card = this.cardDb.getCard(name);
|
||||
assertNotNull(card);
|
||||
}
|
||||
long timeRun = System.currentTimeMillis() - start;
|
||||
averageTime += timeRun;
|
||||
if (timeRun < minTime)
|
||||
minTime = timeRun;
|
||||
if (timeRun > maxTime)
|
||||
maxTime = timeRun;
|
||||
}
|
||||
System.out.println("[NEW] Total Time (in sec): " + ((double) averageTime)/ 1000);
|
||||
System.out.println("[NEW] Average Time (in sec): " + ((double) averageTime / nRuns)/ 1000);
|
||||
System.out.println("[NEW] Best Time (in sec): " + ((double) minTime)/ 1000);
|
||||
System.out.println("[NEW] Worst Time (in sec): " + ((double) maxTime)/ 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCardFullDbNewImplementationToProfile(){
|
||||
for (String name : this.fullDbCardNames) {
|
||||
PaperCard card = this.cardDb.getCard(name);
|
||||
assertNotNull(card);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCardFullDbLegacyImplementationToProfile(){
|
||||
for (String name : this.fullDbCardNames) {
|
||||
PaperCard card = this.legacyCardDb.getCard(name);
|
||||
assertNotNull(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package forge.card;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import forge.StaticData;
|
||||
import forge.item.IPaperCard;
|
||||
import forge.item.PaperCard;
|
||||
@@ -10,7 +11,10 @@ import org.testng.annotations.Test;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
@@ -101,6 +105,44 @@ public class CardDbTestCase extends ForgeCardMockTestCase {
|
||||
this.legacyCardDb = new LegacyCardDb(data.getCommonCards().getAllCards(), data.getEditions());
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST FOR GET ALL CARDS
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testGetAllCardsWithName(){
|
||||
List<PaperCard> allCounterSpellPrints = this.cardDb.getAllCards(this.cardNameCounterspell);
|
||||
assertNotNull(allCounterSpellPrints);
|
||||
for (PaperCard card : allCounterSpellPrints)
|
||||
assertEquals(card.getName(), this.cardNameCounterspell);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllCardsThatWerePrintedInSets(){
|
||||
List<String> allowedSets = new ArrayList<>();
|
||||
allowedSets.add(this.latestArtShivanDragonEdition);
|
||||
Predicate<PaperCard> wasPrinted = (Predicate<PaperCard>) this.cardDb.wasPrintedInSets(allowedSets);
|
||||
List<PaperCard> allCardsInSet = this.cardDb.getAllCards(wasPrinted);
|
||||
assertNotNull(allCardsInSet);
|
||||
}
|
||||
|
||||
@Test void testGetAllCardsOfaGivenNameAndLegalInSets(){
|
||||
List<String> allowedSets = new ArrayList<>(Arrays.asList(this.editionsCounterspell));
|
||||
Predicate<PaperCard> printedInSets = (Predicate<PaperCard>) this.cardDb.wasPrintedInSets(allowedSets);
|
||||
List<PaperCard> allCounterSpellsInSets = this.cardDb.getAllCards(this.cardNameCounterspell, printedInSets);
|
||||
assertNotNull(allCounterSpellsInSets);
|
||||
assertTrue(allCounterSpellsInSets.size() > 0);
|
||||
assertTrue(allCounterSpellsInSets.size() > 1);
|
||||
for (PaperCard card : allCounterSpellsInSets) {
|
||||
assertEquals(card.getName(), this.cardNameCounterspell);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TEST FOR CARD RETRIEVAL METHODS
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testGetCardByName() {
|
||||
PaperCard legacyCard = this.legacyCardDb.getCard(cardNameShivanDragon);
|
||||
@@ -2019,6 +2061,30 @@ public class CardDbTestCase extends ForgeCardMockTestCase {
|
||||
assertEquals(legacyAinokCard, ainokCard);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIslandsFromEditionsWithSpecificArtIndex(){
|
||||
String cardName = "Island";
|
||||
assertEquals(this.cardDb.getCardArtPreference(), CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS);
|
||||
PaperCard islandLatest = this.cardDb.getCardFromEditions(cardName, CardDb.CardArtPreference.LATEST_ART_ALL_EDITIONS, 12);
|
||||
assertNotNull(islandLatest);
|
||||
assertEquals(islandLatest.getName(), "Island");
|
||||
assertEquals(islandLatest.getEdition(), "SLD");
|
||||
assertEquals(islandLatest.getArtIndex(), 12);
|
||||
|
||||
// PALP
|
||||
PaperCard islandOriginal = this.cardDb.getCardFromEditions(cardName, CardDb.CardArtPreference.ORIGINAL_ART_CORE_EXPANSIONS_REPRINT_ONLY, 12);
|
||||
assertNotNull(islandOriginal);
|
||||
assertEquals(islandOriginal.getName(), "Island");
|
||||
assertEquals(islandOriginal.getEdition(), "SLD");
|
||||
assertEquals(islandOriginal.getArtIndex(), 12);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxArtCountForBasicLand(){
|
||||
int maxArtIndex = this.cardDb.getMaxArtIndex("Island");
|
||||
assertEquals(maxArtIndex, 13);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ public class CardRequestTestCase {
|
||||
request = CardRequest.fromString(requestString);
|
||||
assertEquals(request.cardName, cardName);
|
||||
assertEquals(request.edition, edition);
|
||||
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
|
||||
assertEquals(request.artIndex, 20);
|
||||
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
|
||||
|
||||
|
||||
@@ -215,4 +215,15 @@ public class CardRequestTestCase {
|
||||
assertNotEquals(request.artIndex, newRequest.artIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatingCardRequestWithArtIndexGreaterThanNine(){
|
||||
String requestString = CardRequest.compose("Island", "SLD", 13);
|
||||
CardRequest request = CardRequest.fromString(requestString);
|
||||
|
||||
assertEquals(request.cardName, "Island");
|
||||
assertEquals(request.edition, "SLD");
|
||||
assertEquals(request.artIndex, 13);
|
||||
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import forge.ImageKeys;
|
||||
import forge.Singletons;
|
||||
import forge.StaticData;
|
||||
import forge.gamesimulationtests.util.CardDatabaseHelper;
|
||||
import forge.item.PaperCard;
|
||||
import forge.localinstance.properties.ForgeConstants;
|
||||
import forge.localinstance.properties.ForgePreferences;
|
||||
import forge.model.FModel;
|
||||
@@ -133,6 +134,9 @@ public class ForgeCardMockTestCase extends PowerMockTestCase {
|
||||
PowerMockito.mockStatic(ImageKeys.class);
|
||||
initForgeConstants();
|
||||
|
||||
// Always Has Image (there is a separated test case to cover the opposite case)
|
||||
PowerMockito.when(ImageKeys.hasImage(Mockito.any(PaperCard.class))).thenReturn(true);
|
||||
|
||||
//Mocking some more static stuff
|
||||
PowerMockito.mockStatic(Singletons.class);
|
||||
PowerMockito.mockStatic(FModel.class);
|
||||
|
||||
@@ -183,7 +183,7 @@ public class LegacyCardDb {
|
||||
}
|
||||
|
||||
public PaperCard getFoiled(PaperCard card0) {
|
||||
return new PaperCard(card0.getRules(), card0.getEdition(), card0.getRarity(), card0.getArtIndex(), true, card0.getArtist());
|
||||
return card0.getFoiled();
|
||||
}
|
||||
|
||||
public PaperCard getCardFromEdition(final String cardName, LegacySetPreference fromSet) {
|
||||
|
||||
Reference in New Issue
Block a user