Major Update to CardDb (along with tests).

This commit includes major updates to the CardDb aligned with the new ICardDatabase (refactored) API.

The implementation of the API now includes a more standardised and linear approach in (single) card retrieval which leverages on the refactored methods.

All these methods rely on the **new** and revised CardRequest object that now support a new method to compose request strings, as well as extending support to requests including the collector numbers.
This is a major update going towards the direction of integrating the collector number in Deck Importer when analysing cards lists.

All these new implementations have been thoroughly tested considering all possible conditions, foil cards, cards with multiple arts, and date filtering, and results compared with **previous** card DB implementation to be sure no behaviour was left uncovered from previous implementation.
(Please see `LegacyDb` class in `forge.card` tests for more details).

Cards in CardPool are now added also including the collector number directly. This should avoid PaperCard instances to rely on the retrieveCollectorNumber methods in PaperCard. [THIS needs to be tested yet].

Another major change to class structure regards the new renamed SetPreference to a more intuitive CardArtPreference.
This will be used to set up card art preference in the UI, as well as to guide card retrieval default behaviour.
(This option will be later included in the Desktop GUI as well).
Tests to compare the behaviour of these new options, and the old ones have been conducted, and all passed.
Also, this attribute has been moved from StaticData to CardDb as it seems more appropriate encapsulation, without any circular dependency.
A new method (not included in ICardDatabase) has been added to CardDb API (i.e. getCardFromEditions(name) ) which relies on the default Card Art preference.

Last but not least, Anthologies edition file receives an update on the TYPE (from other to reprint) to also deal with expected testings - case "Hymn To Tourach" when `LatestPrintNoPromoNoOnline` is selected.
This commit is contained in:
leriomaggio
2021-06-09 17:13:11 +01:00
parent 20386462ce
commit f21612c2c4
5 changed files with 2038 additions and 296 deletions

View File

@@ -17,41 +17,21 @@
*/ */
package forge.card; package forge.card;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import forge.StaticData;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.*;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import forge.card.CardEdition.CardInSet; import forge.card.CardEdition.CardInSet;
import forge.card.CardEdition.Type; import forge.card.CardEdition.Type;
import forge.deck.generation.IDeckGenPool; import forge.deck.generation.IDeckGenPool;
import forge.item.IPaperCard;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.CollectionSuppliers; import forge.util.CollectionSuppliers;
import forge.util.Lang; import forge.util.Lang;
import forge.util.MyRandom;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
import java.util.Map.Entry;
public final class CardDb implements ICardDatabase, IDeckGenPool { public final class CardDb implements ICardDatabase, IDeckGenPool {
public final static String foilSuffix = "+"; public final static String foilSuffix = "+";
@@ -72,16 +52,18 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
private final CardEdition.Collection editions; private final CardEdition.Collection editions;
private List<String> filtered; private List<String> filtered;
public enum SetPreference { public enum CardArtPreference {
Latest(false), LatestPrint(false, true),
LatestCoreExp(true), LatestPrintNoPromoNoOnline(true, true),
Earliest(false), OldPrint(false, false),
EarliestCoreExp(true), OldPrintNoPromoNoOnline(true, false);
Random(false);
final boolean filterSets; final boolean filterSets;
SetPreference(boolean filterIrregularSets) { final boolean latestFirst;
CardArtPreference(boolean filterIrregularSets, boolean latestSetFirst) {
filterSets = filterIrregularSets; filterSets = filterIrregularSets;
latestFirst = latestSetFirst;
} }
public boolean accept(CardEdition ed) { public boolean accept(CardEdition ed) {
@@ -90,54 +72,126 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
} }
// NO GETTERS/SETTERS HERE! // Placeholder to setup default art Preference - to be moved from Static Data!
private CardArtPreference defaultCardArtPreference;
public static class CardRequest { public static class CardRequest {
// TODO Move Request to its own class
public String cardName; public String cardName;
public String edition; public String edition;
public int artIndex; public int artIndex;
public boolean isFoil; public boolean isFoil;
public String collectorNumber;
private CardRequest(String name, String edition, int artIndex, boolean isFoil) { private CardRequest(String name, String edition, int artIndex, boolean isFoil, String collectorNumber) {
cardName = name; cardName = name;
this.edition = edition; this.edition = edition;
this.artIndex = artIndex; this.artIndex = artIndex;
this.isFoil = isFoil; this.isFoil = isFoil;
this.collectorNumber = collectorNumber;
} }
public static CardRequest fromString(String name) { public static String compose(String cardName, String setCode) {
boolean isFoil = name.endsWith(foilSuffix); setCode = setCode != null ? setCode : "";
if (isFoil) { cardName = cardName != null ? cardName : "";
name = name.substring(0, name.length() - foilSuffix.length()); return cardName + NameSetSeparator + setCode;
} }
String preferredArt = artPrefs.get(name); public static String compose(String cardName, String setCode, int artIndex) {
if (preferredArt != null) { //account for preferred art if needed String requestInfo = compose(cardName, setCode);
name += NameSetSeparator + preferredArt; artIndex = Math.max(artIndex, IPaperCard.DEFAULT_ART_INDEX);
return requestInfo + NameSetSeparator + artIndex;
} }
String[] nameParts = TextUtil.split(name, NameSetSeparator); public static String compose(String cardName, String setCode, String collectorNumber) {
String requestInfo = compose(cardName, setCode);
// CollectorNumber will be wrapped in square brackets
collectorNumber = preprocessCollectorNumber(collectorNumber);
return requestInfo + NameSetSeparator + collectorNumber;
}
int setPos = nameParts.length >= 2 && !StringUtils.isNumeric(nameParts[1]) ? 1 : -1; private static String preprocessCollectorNumber(String collectorNumber) {
int artPos = nameParts.length >= 2 && StringUtils.isNumeric(nameParts[1]) ? 1 : nameParts.length >= 3 && StringUtils.isNumeric(nameParts[2]) ? 2 : -1; if (collectorNumber == null)
return "";
collectorNumber = collectorNumber.trim();
if (!collectorNumber.startsWith("["))
collectorNumber = "[" + collectorNumber;
if (!collectorNumber.endsWith("]"))
collectorNumber += "]";
return collectorNumber;
}
String cardName = nameParts[0]; public static String compose(String cardName, String setCode, int artIndex, String collectorNumber) {
String requestInfo = compose(cardName, setCode, artIndex);
// CollectorNumber will be wrapped in square brackets
collectorNumber = preprocessCollectorNumber(collectorNumber);
return requestInfo + NameSetSeparator + collectorNumber;
}
private static boolean isCollectorNumber(String s) {
return s.startsWith("[") && s.endsWith("]");
}
private static boolean isArtIndex(String s) {
return StringUtils.isNumeric(s) && s.length() == 1;
}
private static boolean isSetCode(String s) {
return !StringUtils.isNumeric(s);
}
public static CardRequest fromString(String reqInfo) {
if (reqInfo == null)
return null;
String[] info = TextUtil.split(reqInfo, NameSetSeparator);
int setPos;
int artPos;
int cNrPos;
if (info.length >= 4) { // name|set|artIndex|[collNr]
setPos = isSetCode(info[1]) ? 1 : -1;
artPos = isArtIndex(info[2]) ? 2 : -1;
cNrPos = isCollectorNumber(info[3]) ? 3 : -1;
} else if (info.length == 3) { // name|set|artIndex (or CollNr)
setPos = isSetCode(info[1]) ? 1 : -1;
artPos = isArtIndex(info[2]) ? 2 : -1;
cNrPos = isCollectorNumber(info[2]) ? 2 : -1;
} else if (info.length == 2) { // name|set (or artIndex, even if not possible via compose)
setPos = isSetCode(info[1]) ? 1 : -1;
artPos = isArtIndex(info[1]) ? 1 : -1;
cNrPos = -1;
} else {
setPos = -1;
artPos = -1;
cNrPos = -1;
}
String cardName = info[0];
boolean isFoil = false;
if (cardName.endsWith(foilSuffix)) { if (cardName.endsWith(foilSuffix)) {
cardName = cardName.substring(0, cardName.length() - foilSuffix.length()); cardName = cardName.substring(0, cardName.length() - foilSuffix.length());
isFoil = true; isFoil = true;
} }
int artIndex = artPos > 0 ? Integer.parseInt(nameParts[artPos]) : 0; String preferredArt = artPrefs.get(cardName);
String setName = setPos > 0 ? nameParts[setPos] : null; int artIndex = artPos > 0 ? Integer.parseInt(info[artPos]) : IPaperCard.NO_ART_INDEX; // default: no art index
if (preferredArt != null) { //account for preferred art if needed
System.err.println("I AM HERE - DECIDE WHAT TO DO");
}
String collectorNumber = cNrPos > 0 ? info[cNrPos].substring(1, info[cNrPos].length() - 1) : IPaperCard.NO_COLLECTOR_NUMBER;
String setName = setPos > 0 ? info[setPos] : null;
if ("???".equals(setName)) { if ("???".equals(setName)) {
setName = null; setName = null;
} }
// finally, check whether any between artIndex and CollectorNumber has been set
return new CardRequest(cardName, setName, artIndex, isFoil); if (collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER) && artIndex == IPaperCard.NO_ART_INDEX)
artIndex = IPaperCard.DEFAULT_ART_INDEX;
return new CardRequest(cardName, setName, artIndex, isFoil, collectorNumber);
} }
} }
public CardDb(Map<String, CardRules> rules, CardEdition.Collection editions0, List<String> filteredCards){ public CardDb(Map<String, CardRules> rules, CardEdition.Collection editions0, List<String> filteredCards){
this(rules, editions0, filteredCards, "LatestPrint");
}
public CardDb(Map<String, CardRules> rules, CardEdition.Collection editions0, List<String> filteredCards, String preferredCardArt) {
this.filtered = filteredCards; this.filtered = filteredCards;
this.rulesByName = rules; this.rulesByName = rules;
this.editions = editions0; this.editions = editions0;
@@ -159,21 +213,22 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
} }
} }
try{
this.defaultCardArtPreference = CardArtPreference.valueOf(preferredCardArt);
} catch (IllegalArgumentException ex){
this.defaultCardArtPreference = CardArtPreference.LatestPrint;
} }
private ListMultimap<String, PaperCard> getAllCardsByName() {
return allCardsByName;
} }
private void addSetCard(CardEdition e, CardInSet cis, CardRules cr) { private void addSetCard(CardEdition e, CardInSet cis, CardRules cr) {
int artIdx = 1; int artIdx = IPaperCard.DEFAULT_ART_INDEX;
String key = e.getCode() + "/" + cis.name; String key = e.getCode() + "/" + cis.name;
if (artIds.containsKey(key)) { if (artIds.containsKey(key)) {
artIdx = artIds.get(key) + 1; artIdx = artIds.get(key) + 1;
} }
artIds.put(key, artIdx); artIds.put(key, artIdx);
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx)); addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx, cis.collectorNumber));
} }
public void loadCard(String cardName, CardRules cr) { public void loadCard(String cardName, CardRules cr) {
@@ -212,16 +267,14 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
CardRules cr = rulesByName.get(cis.name); CardRules cr = rulesByName.get(cis.name);
if (cr != null) { if (cr != null) {
addSetCard(e, cis, cr); addSetCard(e, cis, cr);
} } else {
else {
missingCards.add(cis.name); missingCards.add(cis.name);
} }
} }
if (isCoreExpSet && logMissingPerEdition) { if (isCoreExpSet && logMissingPerEdition) {
if (missingCards.isEmpty()) { if (missingCards.isEmpty()) {
System.out.println(" ... 100% "); System.out.println(" ... 100% ");
} } else {
else {
int missing = (e.getAllCardsInSet().size() - missingCards.size()) * 10000 / e.getAllCardsInSet().size(); int missing = (e.getAllCardsInSet().size() - missingCards.size()) * 10000 / e.getAllCardsInSet().size();
System.out.printf(" ... %.2f%% (%s missing: %s)%n", missing * 0.01f, Lang.nounWithAmount(missingCards.size(), "card"), StringUtils.join(missingCards, " | ")); System.out.printf(" ... %.2f%% (%s missing: %s)%n", missing * 0.01f, Lang.nounWithAmount(missingCards.size(), "card"), StringUtils.join(missingCards, " | "));
} }
@@ -244,10 +297,10 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
for (CardRules cr : rulesByName.values()) { for (CardRules cr : rulesByName.values()) {
if (!contains(cr.getName())) { if (!contains(cr.getName())) {
if (upcomingSet != null) { if (upcomingSet != null) {
addCard(new PaperCard(cr, upcomingSet.getCode(), CardRarity.Unknown, 1)); addCard(new PaperCard(cr, upcomingSet.getCode(), CardRarity.Unknown));
} else if (enableUnknownCards) { } else if (enableUnknownCards) {
System.err.println("The card " + cr.getName() + " was not assigned to any set. Adding it to UNKNOWN set... to fix see res/editions/ folder. "); System.err.println("The card " + cr.getName() + " was not assigned to any set. Adding it to UNKNOWN set... to fix see res/editions/ folder. ");
addCard(new PaperCard(cr, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1)); addCard(new PaperCard(cr, CardEdition.UNKNOWN.getCode(), CardRarity.Special));
} }
} }
} }
@@ -261,7 +314,9 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
allCardsByName.put(paperCard.getName(), paperCard); allCardsByName.put(paperCard.getName(), paperCard);
if (paperCard.getRules().getSplitType() == CardSplitType.None) { return; } if (paperCard.getRules().getSplitType() == CardSplitType.None) {
return;
}
if (paperCard.getRules().getOtherPart() != null) { if (paperCard.getRules().getOtherPart() != null) {
//allow looking up card by the name of other faces //allow looking up card by the name of other faces
@@ -272,20 +327,21 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
allCardsByName.put(paperCard.getRules().getMainPart().getName(), paperCard); allCardsByName.put(paperCard.getRules().getMainPart().getName(), paperCard);
} }
} }
private boolean excludeCard(String cardName, String cardEdition) { private boolean excludeCard(String cardName, String cardEdition) {
if (filtered.isEmpty()) if (filtered.isEmpty())
return false; return false;
if (filtered.contains(cardName)) { if (filtered.contains(cardName)) {
if (exlcudedCardSet.equalsIgnoreCase(cardEdition) && exlcudedCardName.equalsIgnoreCase(cardName)) if (exlcudedCardSet.equalsIgnoreCase(cardEdition) && exlcudedCardName.equalsIgnoreCase(cardName))
return true; return true;
else if (!exlcudedCardName.equalsIgnoreCase(cardName)) else return !exlcudedCardName.equalsIgnoreCase(cardName);
return true;
} }
return false; return false;
} }
private void reIndex() { private void reIndex() {
uniqueCardsByName.clear(); uniqueCardsByName.clear();
for (Entry<String, Collection<PaperCard>> kv : getAllCardsByName().asMap().entrySet()) { for (Entry<String, Collection<PaperCard>> kv : allCardsByName.asMap().entrySet()) {
PaperCard pc = getFirstWithImage(kv.getValue()); PaperCard pc = getFirstWithImage(kv.getValue());
uniqueCardsByName.put(kv.getKey(), pc); uniqueCardsByName.put(kv.getKey(), pc);
} }
@@ -315,15 +371,32 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return false; return false;
} }
public CardRules getRules(String cardname) { public CardRules getRules(String cardName) {
CardRules result = rulesByName.get(cardname); CardRules result = rulesByName.get(cardName);
if (result != null) { if (result != null) {
return result; return result;
} else { } else {
return CardRules.getUnsupportedCardNamed(cardname); return CardRules.getUnsupportedCardNamed(cardName);
} }
} }
public CardArtPreference getCardArtPreference(){ return this.defaultCardArtPreference; }
public void setCardArtPreference(String artPreference){
CardArtPreference cardArtPreference = null;
try{
cardArtPreference = CardArtPreference.valueOf(artPreference);
} catch (IllegalArgumentException ex){}
finally {
if (cardArtPreference != null)
this.defaultCardArtPreference = cardArtPreference;
}
}
/*
* ======================
* 1. CARD LOOKUP METHODS
* ======================
*/
@Override @Override
public PaperCard getCard(String cardName) { public PaperCard getCard(String cardName) {
CardRequest request = CardRequest.fromString(cardName); CardRequest request = CardRequest.fromString(cardName);
@@ -332,222 +405,221 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override @Override
public PaperCard getCard(final String cardName, String setCode) { public PaperCard getCard(final String cardName, String setCode) {
CardRequest request = CardRequest.fromString(cardName); CardRequest request = CardRequest.fromString(CardRequest.compose(cardName, setCode));
if (setCode != null) {
request.edition = setCode;
}
return tryGetCard(request); return tryGetCard(request);
} }
@Override @Override
public PaperCard getCard(final String cardName, String setCode, int artIndex) { public PaperCard getCard(final String cardName, String setCode, int artIndex) {
CardRequest request = CardRequest.fromString(cardName); String reqInfo = CardRequest.compose(cardName, setCode, artIndex);
if (setCode != null) { CardRequest request = CardRequest.fromString(reqInfo);
request.edition = setCode;
}
if (artIndex > 0) {
request.artIndex = artIndex;
}
return tryGetCard(request); return tryGetCard(request);
} }
public String getCardCollectorNumber(String cardName, String reqEdition, int artIndex) { @Override
cardName = getName(cardName); public PaperCard getCard(final String cardName, String setCode, String collectorNumber) {
CardEdition edition = editions.get(reqEdition); String reqInfo = CardRequest.compose(cardName, setCode, collectorNumber);
if (edition == null) CardRequest request = CardRequest.fromString(reqInfo);
return null; return tryGetCard(request);
int numMatches = 0;
for (CardInSet card : edition.getAllCardsInSet()) {
if (card.name.equalsIgnoreCase(cardName)) {
numMatches += 1;
if (numMatches == artIndex) {
return card.collectorNumber;
} }
}
} @Override
return null; public PaperCard getCard(final String cardName, String setCode, int artIndex, String collectorNumber) {
String reqInfo = CardRequest.compose(cardName, setCode, artIndex, collectorNumber);
CardRequest request = CardRequest.fromString(reqInfo);
return tryGetCard(request);
} }
private PaperCard tryGetCard(CardRequest request) { private PaperCard tryGetCard(CardRequest request) {
Collection<PaperCard> cards = getAllCards(request.cardName); // Before doing anything, check that a non-null request has been provided
if (cards == null) { return null; } if (request == null)
return null;
// 1. First off, try using all possible search parameters, to narrow down the actual cards looked for.
String reqEditionCode = request.edition;
PaperCard result = null; PaperCard result = null;
if ((reqEditionCode != null) && (reqEditionCode.length() > 0)) {
String reqEdition = request.edition; // This get is robust even against expansion aliases (e.g. TE and TMP both valid for Tempest) -
if (reqEdition != null && !editions.contains(reqEdition)) { // MOST of the extensions have two short codes, 141 out of 221 (so far)
CardEdition edition = editions.get(reqEdition); CardEdition edition = editions.get(reqEditionCode);
if (edition != null) { return this.getCardFromSet(request.cardName, edition, request.artIndex,
reqEdition = edition.getCode(); request.collectorNumber, request.isFoil);
}
} }
if (request.artIndex <= 0) { // this stands for 'random art' // 2. Card lookup in edition with specified filter didn't work.
Collection<PaperCard> candidates; // So now check whether the cards exists in the DB first,
if (reqEdition == null) { // and select pick the card based on current SetPreference policy as a fallback
candidates = new ArrayList<>(cards); Collection<PaperCard> cards = getAllCards(request.cardName);
if (cards == null)
return null;
// Either No Edition has been specified OR as a fallback in case of any error!
// get card using the default policy strategy (Latest, Earliest, or Random)
// Note: Request is transformed back into the unique line,
// embedding all the information; Parameter Name is counter intuitive though.
result = getCardFromEditions(request.cardName);
return result != null && request.isFoil ? result.getFoiled() : result;
} }
else {
candidates = new ArrayList<>(); /*
for (PaperCard pc : cards) { * ==========================================
if (pc.getEdition().equalsIgnoreCase(reqEdition)) { * 2. CARD LOOKUP FROM A SINGLE EXPANSION SET
candidates.add(pc); * ==========================================
*/
@Override
public PaperCard getCardFromSet(String cardName, CardEdition edition, boolean isFoil) {
return getCardFromSet(cardName, edition, IPaperCard.NO_ART_INDEX,
IPaperCard.NO_COLLECTOR_NUMBER, isFoil);
} }
@Override
public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex, boolean isFoil) {
return getCardFromSet(cardName, edition, artIndex, IPaperCard.NO_COLLECTOR_NUMBER, isFoil);
} }
@Override
public PaperCard getCardFromSet(String cardName, CardEdition edition, String collectorNumber, boolean isFoil) {
return getCardFromSet(cardName, edition, IPaperCard.NO_ART_INDEX, collectorNumber, isFoil);
} }
@Override
public PaperCard getCardFromSet(String cardName, CardEdition edition, int artIndex,
String collectorNumber, boolean isFoil) {
if (edition == null || cardName == null) // preview cards
return null; // No cards will be returned
// Allow to pass in cardNames with foil markers, and adapt accordingly
CardRequest cardNameRequest = CardRequest.fromString(cardName);
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>() {
@Override
public boolean apply(PaperCard c) {
boolean artIndexFilter = true;
boolean collectorNumberFilter = true;
boolean setFilter = ((c.getEdition().equalsIgnoreCase(edition.getCode())) ||
(c.getEdition().equalsIgnoreCase(edition.getCode2())));
if (artIndex > 0)
artIndexFilter = (c.getArtIndex() == artIndex);
if ((collectorNumber != null) && (collectorNumber.length() > 0)
&& !(collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)))
collectorNumberFilter = (c.getCollectorNumber().equals(collectorNumber));
return setFilter && artIndexFilter && collectorNumberFilter;
}
}));
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
return null; return null;
} }
result = Aggregates.random(candidates); PaperCard candidate = candidates.get(0);
// 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
}
}
}
return isFoil ? candidate.getFoiled() : candidate;
}
//if card image doesn't exist for chosen candidate, try another one if possible /*
while (candidates.size() > 1 && !result.hasImage()) { * ====================================================
candidates.remove(result); * 3. CARD LOOKUP BASED ON PREFERRED PRINT (FRAME) OPTION
result = Aggregates.random(candidates); * ====================================================
} */
}
else {
for (PaperCard pc : cards) {
if (pc.getEdition().equalsIgnoreCase(reqEdition) && request.artIndex == pc.getArtIndex()) {
result = pc;
break;
}
}
}
if (result == null) { return null; }
return request.isFoil ? getFoiled(result) : result; /* Get Card from Edition using the default `CardArtPreference`
NOTE: this method has NOT been included in the Interface API refactoring as it
relies on a specific (new) attribute included in the `CardDB` that sets the
default `ArtPreference`. The method is public, though, for future use.
*/
public PaperCard getCardFromEditions(final String cardName) {
return this.getCardFromEditions(cardName, this.defaultCardArtPreference);
} }
@Override @Override
public PaperCard getCardFromEdition(final String cardName, SetPreference fromSet) { public PaperCard getCardFromEditions(final String cardName, CardArtPreference artPreference) {
return getCardFromEdition(cardName, null, fromSet); return getCardFromEditions(cardName, artPreference, IPaperCard.NO_ART_INDEX, null);
} }
@Override @Override
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final SetPreference fromSet) { public PaperCard getCardFromEditions(final String cardName, CardArtPreference artPreference, int artIndex) {
return getCardFromEdition(cardName, printedBefore, fromSet, -1); return getCardFromEditions(cardName, artPreference, artIndex, null);
} }
@Override @Override
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final SetPreference fromSets, int artIndex) { public PaperCard getCardFromEditions(final String cardName, final CardArtPreference artPreference, final Date printedBefore) {
return getCardFromEditions(cardName, artPreference, IPaperCard.NO_ART_INDEX, printedBefore);
}
@Override
public PaperCard getCardFromEditions(final String cardName, final CardArtPreference artPreference, int artIndex,
final Date printedBefore) {
if (cardName == null)
return null;
final CardRequest cr = CardRequest.fromString(cardName); final CardRequest cr = CardRequest.fromString(cardName);
SetPreference fromSet = fromSets; // Check whether input `frame` is null. In that case, fallback to default SetPreference !-)
final CardArtPreference artPref = artPreference != null ? artPreference : this.defaultCardArtPreference;
if (artIndex >= IPaperCard.DEFAULT_ART_INDEX && cr.artIndex < IPaperCard.DEFAULT_ART_INDEX) {
cr.artIndex = artIndex;
}
List<PaperCard> cards = getAllCards(cr.cardName); List<PaperCard> cards = getAllCards(cr.cardName);
if (printedBefore != null) { if (printedBefore != null) {
cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() { cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
@Override public boolean apply(PaperCard c) { @Override
public boolean apply(PaperCard c) {
CardEdition ed = editions.get(c.getEdition()); CardEdition ed = editions.get(c.getEdition());
return ed.getDate().before(printedBefore); } return ed.getDate().before(printedBefore);
}
})); }));
} }
if (cards.size() == 0) // Don't bother continuing! No cards has been found! if (cards.size() == 0) // Don't bother continuing! No card has been found!
return null; return null;
boolean cardsListReadOnly = true;
//overrides /* 2. Retrieve cards based of [Frame]Set Preference
if (StaticData.instance().getPrefferedArtOption().equals("Earliest")) ================================================ */
fromSet = SetPreference.EarliestCoreExp;
if (StringUtils.isNotBlank(cr.edition)) { // Collect the list of all editions found for target card
cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() { LinkedHashSet<CardEdition> cardEditions = new LinkedHashSet<>();
@Override public boolean apply(PaperCard input) { return input.getEdition().equalsIgnoreCase(cr.edition); } for (PaperCard card : cards) {
CardEdition ed = editions.get(card.getEdition());
cardEditions.add(ed);
}
// Filter Cards Editions based on set preferences
List<CardEdition> acceptedEditions = Lists.newArrayList(Iterables.filter(cardEditions, new Predicate<CardEdition>() {
@Override
public boolean apply(CardEdition ed) {
return artPref.accept(ed);
}
})); }));
} Collections.sort(acceptedEditions); // CardEdition correctly sort by (release) date
if (artIndex == -1 && cr.artIndex > 0) { if (artPref.latestFirst)
artIndex = cr.artIndex; Collections.reverse(acceptedEditions); // newest editions first
}
int sz = cards.size(); PaperCard candidate = null;
if (fromSet == SetPreference.Earliest || fromSet == SetPreference.EarliestCoreExp) { for (CardEdition ed : acceptedEditions) {
PaperCard firstWithoutImage = null; PaperCard cardFromSet = getCardFromSet(cr.cardName, ed, artIndex, cr.isFoil);
for (int i = sz - 1 ; i >= 0 ; i--) { if (candidate == null && cardFromSet != null)
PaperCard pc = cards.get(i); // save the first card found, as the last backup in case no other candidate *with image* will be found
CardEdition ed = editions.get(pc.getEdition()); candidate = cardFromSet;
if (!fromSet.accept(ed)) {
continue;
}
if ((artIndex <= 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore))) { if (cardFromSet != null && cardFromSet.hasImage()) {
if (pc.hasImage()) { candidate = cardFromSet;
return pc; break; // we're done here: found card **with Image**
}
else if (firstWithoutImage == null) {
firstWithoutImage = pc; //ensure first without image returns if none have image
} }
} }
} return candidate; // any foil request already handled in getCardFromSet
return firstWithoutImage;
}
else if (fromSet == SetPreference.LatestCoreExp || fromSet == SetPreference.Latest || fromSet == null || fromSet == SetPreference.Random) {
PaperCard firstWithoutImage = null;
for (int i = 0; i < sz; i++) {
PaperCard pc = cards.get(i);
CardEdition ed = editions.get(pc.getEdition());
if (fromSet != null && !fromSet.accept(ed)) {
continue;
}
if ((artIndex < 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore))) {
if (fromSet == SetPreference.LatestCoreExp || fromSet == SetPreference.Latest) {
if (pc.hasImage()) {
return pc;
}
else if (firstWithoutImage == null) {
firstWithoutImage = pc; //ensure first without image returns if none have image
}
}
else {
while (sz > i) {
int randomIndex = i + MyRandom.getRandom().nextInt(sz - i);
pc = cards.get(randomIndex);
if (pc.hasImage()) {
return pc;
}
else {
if (firstWithoutImage == null) {
firstWithoutImage = pc; //ensure first without image returns if none have image
}
if (cardsListReadOnly) { //ensure we don't modify a cached collection
cards = new ArrayList<>(cards);
cardsListReadOnly = false;
}
cards.remove(randomIndex); //remove card from collection and try another random card
sz--;
}
}
}
}
}
return firstWithoutImage;
}
return null;
}
public PaperCard getFoiled(PaperCard card0) {
// Here - I am still unsure if there should be a cache Card->Card from unfoiled to foiled, to avoid creation of N instances of single plains
return new PaperCard(card0.getRules(), card0.getEdition(), card0.getRarity(), card0.getArtIndex(), true);
} }
@Override @Override
public int getPrintCount(String cardName, String edition) { public int getMaxArtIndex(String cardName) {
int cnt = 0;
if (edition == null || cardName == null)
return cnt;
for (PaperCard pc : getAllCards(cardName)) {
if (pc.getEdition().equals(edition)) {
cnt++;
}
}
return cnt;
}
@Override
public int getMaxPrintCount(String cardName) {
int max = -1;
if (cardName == null) if (cardName == null)
return max; return IPaperCard.NO_ART_INDEX;
int max = IPaperCard.NO_ART_INDEX;
for (PaperCard pc : getAllCards(cardName)) { for (PaperCard pc : getAllCards(cardName)) {
if (max < pc.getArtIndex()) { if (max < pc.getArtIndex()) {
max = pc.getArtIndex(); max = pc.getArtIndex();
@@ -607,11 +679,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override @Override
public Collection<PaperCard> getAllCards() { public Collection<PaperCard> getAllCards() {
return Collections.unmodifiableCollection(getAllCardsByName().values()); return Collections.unmodifiableCollection(allCardsByName.values());
} }
public Collection<PaperCard> getAllCardsNoAlt() { public Collection<PaperCard> getAllCardsNoAlt() {
return Multimaps.filterEntries(getAllCardsByName(), new Predicate<Entry<String, PaperCard>>() { return Multimaps.filterEntries(allCardsByName, new Predicate<Entry<String, PaperCard>>() {
@Override @Override
public boolean apply(Entry<String, PaperCard> entry) { public boolean apply(Entry<String, PaperCard> entry) {
return entry.getKey().equals(entry.getValue().getName()); return entry.getKey().equals(entry.getValue().getName());
@@ -660,11 +732,11 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override @Override
public List<PaperCard> getAllCards(String cardName) { public List<PaperCard> getAllCards(String cardName) {
return getAllCardsByName().get(getName(cardName)); return allCardsByName.get(getName(cardName));
} }
public List<PaperCard> getAllCardsNoAlt(String cardName) { public List<PaperCard> getAllCardsNoAlt(String cardName) {
return Lists.newArrayList(Multimaps.filterEntries(getAllCardsByName(), new Predicate<Entry<String, PaperCard>>() { return Lists.newArrayList(Multimaps.filterEntries(allCardsByName, new Predicate<Entry<String, PaperCard>>() {
@Override @Override
public boolean apply(Entry<String, PaperCard> entry) { public boolean apply(Entry<String, PaperCard> entry) {
return entry.getKey().equals(entry.getValue().getName()); return entry.getKey().equals(entry.getValue().getName());
@@ -672,20 +744,24 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
}).get(getName(cardName))); }).get(getName(cardName)));
} }
/** Returns a modifiable list of cards matching the given predicate */ /**
* Returns a modifiable list of cards matching the given predicate
*/
@Override @Override
public List<PaperCard> getAllCards(Predicate<PaperCard> predicate) { public List<PaperCard> getAllCards(Predicate<PaperCard> predicate) {
return Lists.newArrayList(Iterables.filter(getAllCards(), predicate)); return Lists.newArrayList(Iterables.filter(getAllCards(), predicate));
} }
/** Returns a modifiable list of cards matching the given predicate */ /**
* Returns a modifiable list of cards matching the given predicate
*/
public List<PaperCard> getAllCardsNoAlt(Predicate<PaperCard> predicate) { public List<PaperCard> getAllCardsNoAlt(Predicate<PaperCard> predicate) {
return Lists.newArrayList(Iterables.filter(getAllCardsNoAlt(), predicate)); return Lists.newArrayList(Iterables.filter(getAllCardsNoAlt(), predicate));
} }
// Do I want a foiled version of these cards? // Do I want a foiled version of these cards?
@Override @Override
public List<PaperCard> getAllCardsFromEdition(CardEdition edition) { public Collection<PaperCard> getAllCards(CardEdition edition) {
List<PaperCard> cards = Lists.newArrayList(); List<PaperCard> cards = Lists.newArrayList();
for (CardInSet cis : edition.getAllCardsInSet()) { for (CardInSet cis : edition.getAllCardsInSet()) {
@@ -702,7 +778,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
@Override @Override
public boolean contains(String name) { public boolean contains(String name) {
return getAllCardsByName().containsKey(getName(name)); return allCardsByName.containsKey(getName(name));
} }
@Override @Override
@@ -710,6 +786,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return getAllCards().iterator(); return getAllCards().iterator();
} }
@Override
public Predicate<? super PaperCard> wasPrintedInSets(List<String> setCodes) { public Predicate<? super PaperCard> wasPrintedInSets(List<String> setCodes) {
return new PredicateExistsInSets(setCodes); return new PredicateExistsInSets(setCodes);
} }
@@ -731,7 +808,9 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return false; return false;
} }
} }
// This Predicate validates if a card was printed at [rarity], on any of its printings // This Predicate validates if a card was printed at [rarity], on any of its printings
@Override
public Predicate<? super PaperCard> wasPrintedAtRarity(CardRarity rarity) { public Predicate<? super PaperCard> wasPrintedAtRarity(CardRarity rarity) {
return new PredicatePrintedAtRarity(rarity); return new PredicatePrintedAtRarity(rarity);
} }
@@ -747,6 +826,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
} }
} }
@Override @Override
public boolean apply(final PaperCard subject) { public boolean apply(final PaperCard subject) {
return matchingCards.contains(subject.getName()); return matchingCards.contains(subject.getName());
@@ -763,7 +843,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
if (!hasBadSetInfo) { if (!hasBadSetInfo) {
int artCount = getArtCount(card.getName(), card.getEdition()); int artCount = getArtCount(card.getName(), card.getEdition());
sb.append(CardDb.NameSetSeparator).append(card.getEdition()); sb.append(CardDb.NameSetSeparator).append(card.getEdition());
if (artCount > 1) { if (artCount >= IPaperCard.DEFAULT_ART_INDEX) {
sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions sb.append(CardDb.NameSetSeparator).append(card.getArtIndex()); // indexes start at 1 to match image file name conventions
} }
} }
@@ -800,8 +880,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
break; break;
} }
} }
} } else {
else {
cardEdition = CardEdition.UNKNOWN; cardEdition = CardEdition.UNKNOWN;
} }
} }
@@ -812,16 +891,25 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
System.err.println("We're sorry, but you cannot use this card yet."); System.err.println("We're sorry, but you cannot use this card yet.");
} }
return new PaperCard(CardRules.getUnsupportedCardNamed(request.cardName), cardEdition.getCode(), cardRarity, 1); return new PaperCard(CardRules.getUnsupportedCardNamed(request.cardName), cardEdition.getCode(), cardRarity);
} }
private final Editor editor = new Editor(); private final Editor editor = new Editor();
public Editor getEditor() { return editor; }
public Editor getEditor() {
return editor;
}
public class Editor { public class Editor {
private boolean immediateReindex = true; private boolean immediateReindex = true;
public CardRules putCard(CardRules rules) { return putCard(rules, null); /* will use data from editions folder */ }
public CardRules putCard(CardRules rules, List<Pair<String, CardRarity>> whenItWasPrinted){ // works similarly to Map<K,V>, returning prev. value public CardRules putCard(CardRules rules) {
return putCard(rules, null); /* will use data from editions folder */
}
public CardRules putCard(CardRules rules, List<Pair<String, CardRarity>> whenItWasPrinted) {
// works similarly to Map<K,V>, returning prev. value
String cardName = rules.getName(); String cardName = rules.getName();
CardRules result = rulesByName.get(cardName); CardRules result = rulesByName.get(cardName);
@@ -837,7 +925,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) { if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) {
// TODO Not performant Each time we "putCard" we loop through ALL CARDS IN ALL editions // TODO Not performant Each time we "putCard" we loop through ALL CARDS IN ALL editions
for (CardEdition e : editions.getOrderedEditions()) { for (CardEdition e : editions.getOrderedEditions()) {
int artIdx = 1; int artIdx = IPaperCard.DEFAULT_ART_INDEX;
for (CardInSet cis : e.getAllCardsInSet()) { for (CardInSet cis : e.getAllCardsInSet()) {
if (!cis.name.equals(cardName)) { if (!cis.name.equals(cardName)) {
continue; continue;
@@ -845,13 +933,12 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++)); paperCards.add(new PaperCard(rules, e.getCode(), cis.rarity, artIdx++));
} }
} }
} } else {
else {
String lastEdition = null; String lastEdition = null;
int artIdx = 0; int artIdx = 0;
for (Pair<String, CardRarity> tuple : whenItWasPrinted) { for (Pair<String, CardRarity> tuple : whenItWasPrinted) {
if (!tuple.getKey().equals(lastEdition)) { if (!tuple.getKey().equals(lastEdition)) {
artIdx = 1; artIdx = IPaperCard.DEFAULT_ART_INDEX;
lastEdition = tuple.getKey(); lastEdition = tuple.getKey();
} }
CardEdition ed = editions.get(lastEdition); CardEdition ed = editions.get(lastEdition);
@@ -862,7 +949,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
} }
if (paperCards.isEmpty()) { if (paperCards.isEmpty()) {
paperCards.add(new PaperCard(rules, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 1)); paperCards.add(new PaperCard(rules, CardEdition.UNKNOWN.getCode(), CardRarity.Special));
} }
// 2. add them to db // 2. add them to db
for (PaperCard paperCard : paperCards) { for (PaperCard paperCard : paperCards) {
@@ -878,6 +965,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
public boolean isImmediateReindex() { public boolean isImmediateReindex() {
return immediateReindex; return immediateReindex;
} }
public void setImmediateReindex(boolean immediateReindex) { public void setImmediateReindex(boolean immediateReindex) {
this.immediateReindex = immediateReindex; this.immediateReindex = immediateReindex;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,197 @@
package forge.card;
import forge.card.CardDb.CardRequest;
import forge.item.IPaperCard;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@Test(timeOut = 1000, enabled = true)
public class CardRequestTestCase {
private String cardName;
private String edition;
private String collNr;
private String foilCardNameFoil;
private String foilCardName;
private String foilEdition;
private String foilCollNr;
private final char sep = CardDb.NameSetSeparator;
@BeforeTest
public void setup(){
cardName = "Shivan Dragon";
edition = "2ED";
collNr = "175";
foilCardName = "Lightning Dragon";
foilCardNameFoil = "Lightning Dragon+";
foilEdition = "PUSG";
foilCollNr = "202";
}
public void testComposeCardNameAndSet(){
// OK request
String requestInfo = CardRequest.compose(cardName, edition);
String expected = cardName + sep + edition;
assertEquals(requestInfo, expected);
// CardName null
String requestCardNameNull = CardRequest.compose(null, edition);
assertEquals(requestCardNameNull, sep + edition);
// SetCode null
String requestCardNameAndSetNull = CardRequest.compose(null, null);
assertEquals(requestCardNameAndSetNull, "" + sep + "");
// CardNameFoil
String requestInfoFoil = CardRequest.compose(foilCardName, foilEdition);
assertEquals(requestInfoFoil, foilCardName + sep + foilEdition);
}
public void testComposeCardNameSetAndArtIndex(){
String requestInfo = CardRequest.compose(cardName, edition, 2);
String expected = cardName + sep + edition + sep + 2;
assertEquals(requestInfo, expected);
// negative Art Index
String requestNegativeArtIndex = CardRequest.compose(cardName, edition, -3);
expected = cardName + sep + edition + sep + 1;
assertEquals(requestNegativeArtIndex, expected);
}
public void testComposeCardNameSetAndCollectorNumber(){
String requestInfo = CardRequest.compose(cardName, edition, collNr);
String expCN = "[" + collNr + "]";
String expected = cardName + sep + edition + sep + expCN;
assertEquals(requestInfo, expected);
// collNr only one bracket
requestInfo = CardRequest.compose(cardName, edition, "["+collNr);
assertEquals(requestInfo, expected);
requestInfo = CardRequest.compose(cardName, edition, collNr+"]");
assertEquals(requestInfo, expected);
// collNr with leading spaces, as possible result from a wrong parsing in a deck file
requestInfo = CardRequest.compose(cardName, edition, "\t\t 175 ");
assertEquals(requestInfo, expected);
// collNr is null
requestInfo = CardRequest.compose(cardName, edition, null);
assertEquals(requestInfo, cardName + sep + edition + sep);
}
public void testComposeFullRequest(){
String requestInfo = CardRequest.compose(cardName, edition, 1, collNr);
String expected = cardName + sep + edition + sep + 1 + sep + "[" + collNr + "]";
assertEquals(requestInfo, expected);
}
public void testFromStringCardNameOnly(){
CardRequest request = CardRequest.fromString(cardName);
assertEquals(request.cardName, cardName);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertNull(request.edition);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
}
public void testFromStringCardNameAndSetCode(){
String requestString = cardName + sep + edition;
CardRequest request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
// foil
requestString = foilCardNameFoil + sep + foilEdition;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, foilCardName);
assertEquals(request.edition, foilEdition);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertTrue(request.isFoil);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
}
public void testFromStringCardNameAndSetCodeAndArtIndex(){
String requestString = cardName + sep + edition + sep + 2;
CardRequest request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, 2);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
// ArtIndex not valid (as in >= 10) - supposed to be a single digit
requestString = cardName + sep + edition + sep + 20;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
// foil
requestString = foilCardNameFoil + sep + foilEdition + sep + IPaperCard.DEFAULT_ART_INDEX;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, foilCardName);
assertEquals(request.edition, foilEdition);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertTrue(request.isFoil);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
}
public void testFromStringCardNameAndSetCodeAndCollectorNumber(){
String requestString = cardName + sep + edition + sep + "[" + collNr + "]";
CardRequest request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, IPaperCard.NO_ART_INDEX);
assertEquals(request.collectorNumber, collNr);
// Not wrapped collNr
requestString = cardName + sep + edition + sep + collNr;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, IPaperCard.DEFAULT_ART_INDEX);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
// foil
requestString = foilCardNameFoil + sep + foilEdition + sep + "[" + foilCollNr + "]";;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, foilCardName);
assertEquals(request.edition, foilEdition);
assertEquals(request.artIndex, IPaperCard.NO_ART_INDEX);
assertTrue(request.isFoil);
assertEquals(request.collectorNumber, foilCollNr);
}
public void fromStringFullInfo(){
String requestString = cardName + sep + edition + sep + 2 + sep + "[" + collNr + "]";
CardRequest request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, 2);
assertEquals(request.collectorNumber, collNr);
// collNr not wrapped in brackets
requestString = cardName + sep + edition + sep + 2 + sep + collNr;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, cardName);
assertEquals(request.edition, edition);
assertEquals(request.artIndex, 2);
assertEquals(request.collectorNumber, IPaperCard.NO_COLLECTOR_NUMBER);
// foil
requestString = foilCardNameFoil + sep + foilEdition + sep + 3 + sep +"[" + foilCollNr + "]";;
request = CardRequest.fromString(requestString);
assertEquals(request.cardName, foilCardName);
assertEquals(request.edition, foilEdition);
assertEquals(request.artIndex,3);
assertTrue(request.isFoil);
assertEquals(request.collectorNumber, foilCollNr);
}
}

View File

@@ -0,0 +1,333 @@
package forge.card;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimaps;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.CollectionSuppliers;
import forge.util.MyRandom;
import forge.util.TextUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
public class LegacyCardDb {
public CardEdition.Collection editions;
public ListMultimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.arrayLists());
public enum LegacySetPreference {
Latest(false),
LatestCoreExp(true),
Earliest(false),
EarliestCoreExp(true),
Random(false);
final boolean filterSets;
LegacySetPreference(boolean filterIrregularSets) {
filterSets = filterIrregularSets;
}
public boolean accept(CardEdition ed) {
if (ed == null) return false;
return !filterSets || ed.getType() == CardEdition.Type.CORE || ed.getType() == CardEdition.Type.EXPANSION || ed.getType() == CardEdition.Type.REPRINT;
}
}
public final static String foilSuffix = "+";
public final static char NameSetSeparator = '|';
public static class LegacyCardRequest {
// TODO Move Request to its own class
public String cardName;
public String edition;
public int artIndex;
public boolean isFoil;
private LegacyCardRequest(String name, String edition, int artIndex, boolean isFoil) {
cardName = name;
this.edition = edition;
this.artIndex = artIndex;
this.isFoil = isFoil;
}
public static LegacyCardRequest fromString(String name) {
boolean isFoil = name.endsWith(foilSuffix);
if (isFoil) {
name = name.substring(0, name.length() - foilSuffix.length());
}
String[] nameParts = TextUtil.split(name, NameSetSeparator);
int setPos = nameParts.length >= 2 && !StringUtils.isNumeric(nameParts[1]) ? 1 : -1;
int artPos = nameParts.length >= 2 && StringUtils.isNumeric(nameParts[1]) ? 1 : nameParts.length >= 3 && StringUtils.isNumeric(nameParts[2]) ? 2 : -1;
String cardName = nameParts[0];
if (cardName.endsWith(foilSuffix)) {
cardName = cardName.substring(0, cardName.length() - foilSuffix.length());
isFoil = true;
}
int artIndex = artPos > 0 ? Integer.parseInt(nameParts[artPos]) : 0;
String setName = setPos > 0 ? nameParts[setPos] : null;
if ("???".equals(setName)) {
setName = null;
}
return new LegacyCardDb.LegacyCardRequest(cardName, setName, artIndex, isFoil);
}
}
public LegacyCardDb(Collection<PaperCard> cards0, CardEdition.Collection editions){
this.editions = editions;
for (PaperCard card : cards0){
allCardsByName.put(card.getName(), card);
}
}
public String getName(final String cardName) {
return cardName;
}
private ListMultimap<String, PaperCard> getAllCardsByName() {
return allCardsByName;
}
public Collection<PaperCard> getAllCards() {
return Collections.unmodifiableCollection(getAllCardsByName().values());
}
public List<PaperCard> getAllCards(String cardName) {
return getAllCardsByName().get(getName(cardName));
}
public PaperCard getCard(String cardName) {
LegacyCardDb.LegacyCardRequest request = LegacyCardDb.LegacyCardRequest.fromString(cardName);
return tryGetCard(request);
}
public PaperCard getCard(final String cardName, String setCode) {
LegacyCardDb.LegacyCardRequest request = LegacyCardDb.LegacyCardRequest.fromString(cardName);
if (setCode != null) {
request.edition = setCode;
}
return tryGetCard(request);
}
public PaperCard getCard(final String cardName, String setCode, int artIndex) {
LegacyCardDb.LegacyCardRequest request = LegacyCardDb.LegacyCardRequest.fromString(cardName);
if (setCode != null) {
request.edition = setCode;
}
if (artIndex > 0) {
request.artIndex = artIndex;
}
return tryGetCard(request);
}
private PaperCard tryGetCard(LegacyCardDb.LegacyCardRequest request) {
Collection<PaperCard> cards = getAllCards(request.cardName);
if (cards == null) { return null; }
PaperCard result = null;
String reqEdition = request.edition;
if (reqEdition != null && !editions.contains(reqEdition)) {
CardEdition edition = editions.get(reqEdition);
if (edition != null) {
reqEdition = edition.getCode();
}
}
if (request.artIndex <= 0) { // this stands for 'random art'
Collection<PaperCard> candidates;
if (reqEdition == null) {
candidates = new ArrayList<>(cards);
}
else {
candidates = new ArrayList<>();
for (PaperCard pc : cards) {
if (pc.getEdition().equalsIgnoreCase(reqEdition)) {
candidates.add(pc);
}
}
}
if (candidates.isEmpty()) {
return null;
}
result = Aggregates.random(candidates);
//if card image doesn't exist for chosen candidate, try another one if possible
while (candidates.size() > 1 && !result.hasImage()) {
candidates.remove(result);
result = Aggregates.random(candidates);
}
}
else {
for (PaperCard pc : cards) {
if (pc.getEdition().equalsIgnoreCase(reqEdition) && request.artIndex == pc.getArtIndex()) {
result = pc;
break;
}
}
}
if (result == null) { return null; }
return request.isFoil ? getFoiled(result) : result;
}
public PaperCard getFoiled(PaperCard card0) {
// Here - I am still unsure if there should be a cache Card->Card from unfoiled to foiled, to avoid creation of N instances of single plains
return new PaperCard(card0.getRules(), card0.getEdition(), card0.getRarity(), card0.getArtIndex(), true);
}
public PaperCard getCardFromEdition(final String cardName, LegacySetPreference fromSet) {
return getCardFromEdition(cardName, null, fromSet);
}
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final LegacySetPreference fromSet) {
return getCardFromEdition(cardName, printedBefore, fromSet, -1);
}
public PaperCard getCardFromEdition(final String cardName, final Date printedBefore, final LegacySetPreference fromSets, int artIndex) {
final CardDb.CardRequest cr = CardDb.CardRequest.fromString(cardName);
LegacySetPreference fromSet = fromSets;
List<PaperCard> cards = getAllCards(cr.cardName);
if (printedBefore != null){
cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
@Override public boolean apply(PaperCard c) {
CardEdition ed = editions.get(c.getEdition());
return ed.getDate().before(printedBefore); }
}));
}
if (cards.size() == 0) // Don't bother continuing! No cards has been found!
return null;
boolean cardsListReadOnly = true;
// Removed from testing - completely Non-sense
// //overrides
// if (StaticData.instance().getPrefferedArtOption().equals("Earliest"))
// fromSet = LegacySetPreference.EarliestCoreExp;
if (StringUtils.isNotBlank(cr.edition)) {
cards = Lists.newArrayList(Iterables.filter(cards, new Predicate<PaperCard>() {
@Override public boolean apply(PaperCard input) { return input.getEdition().equalsIgnoreCase(cr.edition); }
}));
}
if (artIndex == -1 && cr.artIndex > 0) {
artIndex = cr.artIndex;
}
int sz = cards.size();
if (fromSet == LegacySetPreference.Earliest || fromSet == LegacySetPreference.EarliestCoreExp) {
PaperCard firstWithoutImage = null;
for (int i = sz - 1 ; i >= 0 ; i--) {
PaperCard pc = cards.get(i);
CardEdition ed = editions.get(pc.getEdition());
if (!fromSet.accept(ed)) {
continue;
}
if ((artIndex <= 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore))) {
if (pc.hasImage()) {
return pc;
}
else if (firstWithoutImage == null) {
firstWithoutImage = pc; //ensure first without image returns if none have image
}
}
}
return firstWithoutImage;
}
else if (fromSet == LegacySetPreference.LatestCoreExp || fromSet == LegacySetPreference.Latest || fromSet == null || fromSet == LegacySetPreference.Random) {
PaperCard firstWithoutImage = null;
for (int i = 0; i < sz; i++) {
PaperCard pc = cards.get(i);
CardEdition ed = editions.get(pc.getEdition());
if (fromSet != null && !fromSet.accept(ed)) {
continue;
}
if ((artIndex < 0 || pc.getArtIndex() == artIndex) && (printedBefore == null || ed.getDate().before(printedBefore))) {
if (fromSet == LegacySetPreference.LatestCoreExp || fromSet == LegacySetPreference.Latest) {
if (pc.hasImage()) {
return pc;
}
else if (firstWithoutImage == null) {
firstWithoutImage = pc; //ensure first without image returns if none have image
}
}
else {
while (sz > i) {
int randomIndex = i + MyRandom.getRandom().nextInt(sz - i);
pc = cards.get(randomIndex);
if (pc.hasImage()) {
return pc;
}
else {
if (firstWithoutImage == null) {
firstWithoutImage = pc; //ensure first without image returns if none have image
}
if (cardsListReadOnly) { //ensure we don't modify a cached collection
cards = new ArrayList<>(cards);
cardsListReadOnly = false;
}
cards.remove(randomIndex); //remove card from collection and try another random card
sz--;
}
}
}
}
}
return firstWithoutImage;
}
return null;
}
public int getPrintCount(String cardName, String edition) {
int cnt = 0;
if (edition == null || cardName == null)
return cnt;
for (PaperCard pc : getAllCards(cardName)) {
if (pc.getEdition().equals(edition)) {
cnt++;
}
}
return cnt;
}
public int getMaxPrintCount(String cardName) {
int max = -1;
if (cardName == null)
return max;
for (PaperCard pc : getAllCards(cardName)) {
if (max < pc.getArtIndex()) {
max = pc.getArtIndex();
}
}
return max;
}
public int getArtCount(String cardName, String setName) {
int cnt = 0;
if (cardName == null || setName == null)
return cnt;
Collection<PaperCard> cards = getAllCards(cardName);
if (null == cards) {
return 0;
}
for (PaperCard pc : cards) {
if (pc.getEdition().equalsIgnoreCase(setName)) {
cnt++;
}
}
return cnt;
}
}

View File

@@ -3,7 +3,7 @@ Code=ATH
MciCode=at MciCode=at
Date=1998-11 Date=1998-11
Name=Anthologies Name=Anthologies
Type=Other Type=Reprint
Border=White Border=White
Foil=NotSupported Foil=NotSupported