Moved cardDb and most of static data to core project. Had to rollback some incompatible changes. Sorry, drdev!

This commit is contained in:
Maxmtg
2013-11-19 07:40:58 +00:00
parent a7fa173e52
commit 9b31032016
169 changed files with 1074 additions and 1126 deletions

View File

@@ -19,7 +19,6 @@ package forge;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.ImmutableList;
/**
* <p>
@@ -54,79 +53,6 @@ public final class Constant {
public static final int HEIGHT = 0;
}
/**
* The Interface Color.
*/
public static class Color {
/** The Black. */
public static final String BLACK = "black";
/** The Blue. */
public static final String BLUE = "blue";
/** The Green. */
public static final String GREEN = "green";
/** The Red. */
public static final String RED = "red";
/** The White. */
public static final String WHITE = "white";
/** The Colorless. */
public static final String COLORLESS = "colorless";
// color order "wubrg"
/** The only colors. */
public static final ImmutableList<String> ONLY_COLORS = ImmutableList.of(Color.WHITE, Color.BLUE, Color.BLACK, Color.RED, Color.GREEN);
/** The Snow. */
public static final String SNOW = "snow";
/** The Basic lands. */
public static final List<String> BASIC_LANDS = ImmutableList.of("Plains", "Island", "Swamp", "Mountain", "Forest");
public static final List<String> SNOW_LANDS = ImmutableList.of("Snow-Covered Plains", "Snow-Covered Island", "Snow-Covered Swamp", "Snow-Covered Mountain", "Snow-Covered Forest");
}
/**
* The Interface CardTypes.
*/
public static class CardTypes {
/** The loaded. */
public static final boolean[] LOADED = { false };
/** The card types. */
public static final List<String> CARD_TYPES = new ArrayList<String>();
/** The super types. */
public static final List<String> SUPER_TYPES = new ArrayList<String>();
/** The basic types. */
public static final List<String> BASIC_TYPES = new ArrayList<String>();
/** The land types. */
public static final List<String> LAND_TYPES = new ArrayList<String>();
/** The creature types. */
public static final List<String> CREATURE_TYPES = new ArrayList<String>();
/** The instant types. */
public static final List<String> INSTANT_TYPES = new ArrayList<String>();
/** The sorcery types. */
public static final List<String> SORCERY_TYPES = new ArrayList<String>();
/** The enchantment types. */
public static final List<String> ENCHANTMENT_TYPES = new ArrayList<String>();
/** The artifact types. */
public static final List<String> ARTIFACT_TYPES = new ArrayList<String>();
/** The walker types. */
public static final List<String> WALKER_TYPES = new ArrayList<String>();
}
/**
* The Interface Keywords.

View File

@@ -0,0 +1,9 @@
package forge;
import java.util.List;
import forge.card.CardRules;
public interface ICardStorageReader{
List<CardRules> loadCards();
}

View File

@@ -0,0 +1,120 @@
package forge;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import forge.card.CardDb;
import forge.card.CardEdition;
import forge.card.CardRules;
import forge.card.EditionCollection;
import forge.card.FatPackTemplate;
import forge.card.FormatCollection;
import forge.card.PrintSheet;
import forge.card.SealedProductTemplate;
import forge.game.GameFormat;
import forge.util.storage.IStorage;
import forge.util.storage.StorageBase;
/**
* The class holding game invariants, such as cards, editions, game formats. All that data, which is not supposed to be changed by player
*
* @author Max
*/
public class StaticData {
private final CardDb commonCards;
private final CardDb variantCards;
private final EditionCollection editions;
private final FormatCollection formats;
private final IStorage<SealedProductTemplate> boosters;
private final IStorage<SealedProductTemplate> specialBoosters;
private final IStorage<SealedProductTemplate> tournaments;
private final IStorage<FatPackTemplate> fatPacks;
private final IStorage<PrintSheet> printSheets;
private static StaticData lastInstance = null;
public StaticData(ICardStorageReader reader, String editionFolder, String blockDataFolder) {
this.editions = new EditionCollection(new CardEdition.Reader(new File(editionFolder)));
lastInstance = this;
final Map<String, CardRules> regularCards = new TreeMap<String, CardRules>(String.CASE_INSENSITIVE_ORDER);
final Map<String, CardRules> variantsCards = new TreeMap<String, CardRules>(String.CASE_INSENSITIVE_ORDER);
synchronized (CardDb.class) {
List<CardRules> rules = reader.loadCards();
for (CardRules card : rules) {
if (null == card) continue;
final String cardName = card.getName();
if ( card.isVariant() )
variantsCards.put(cardName, card);
else
regularCards.put(cardName, card);
}
commonCards = new CardDb(regularCards, editions, false);
variantCards = new CardDb(variantsCards, editions, false);
}
this.formats = new FormatCollection(new GameFormat.Reader(new File(blockDataFolder, "formats.txt")));
this.boosters = new StorageBase<SealedProductTemplate>("Boosters", editions.getBoosterGenerator());
this.specialBoosters = new StorageBase<SealedProductTemplate>("Special boosters", new SealedProductTemplate.Reader(new File(blockDataFolder, "boosters-special.txt")));
this.tournaments = new StorageBase<SealedProductTemplate>("Starter sets", new SealedProductTemplate.Reader(new File(blockDataFolder, "starters.txt")));
this.fatPacks = new StorageBase<FatPackTemplate>("Fat packs", new FatPackTemplate.Reader("res/blockdata/fatpacks.txt"));
this.printSheets = new StorageBase<PrintSheet>("Special print runs", new PrintSheet.Reader(new File(blockDataFolder, "printsheets.txt")));
}
public final static StaticData instance() {
return lastInstance;
}
public final EditionCollection getEditions() {
return this.editions;
}
public final FormatCollection getFormats() {
return this.formats;
}
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.FatPackTemplate}> */
public IStorage<FatPackTemplate> getFatPacks() {
return fatPacks;
}
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.BoosterTemplate}> */
public final IStorage<SealedProductTemplate> getTournamentPacks() {
return tournaments;
}
/** @return {@link forge.util.storage.IStorageView}<{@link forge.card.BoosterTemplate}> */
public final IStorage<SealedProductTemplate> getBoosters() {
return boosters;
}
public final IStorage<SealedProductTemplate> getSpecialBoosters() {
return specialBoosters;
}
public IStorage<PrintSheet> getPrintSheets() {
return printSheets;
}
public CardDb getCommonCards() {
return commonCards;
}
public CardDb getVariantCards() {
return variantCards;
}
}

View File

@@ -0,0 +1,14 @@
package forge.card;
public class BoosterSlots {
public static final String LAND = "Land";
public static final String ANY = "Any";
public static final String COMMON = "Common";
public static final String UNCOMMON = "Uncommon";
public static final String UNCOMMON_RARE = "UncommonRare";
public static final String RARE = "Rare";
public static final String RARE_MYTHIC = "RareMythic";
public static final String MYTHIC = "Mythic";
public static final String BASIC_LAND = "BasicLand";
public static final String TIME_SHIFTED = "TimeShifted";
}

View File

@@ -0,0 +1,74 @@
package forge.card;
/**
* TODO: Write javadoc for this type.
*
*/
public class CardAiHints {
private final boolean isRemovedFromAIDecks;
private final boolean isRemovedFromRandomDecks;
private final DeckHints deckHints;
private final DeckHints deckNeeds;
public CardAiHints(boolean remAi, boolean remRandom, DeckHints dh, DeckHints dn) {
isRemovedFromAIDecks = remAi;
isRemovedFromRandomDecks = remRandom;
deckHints = dh;
deckNeeds = dn;
}
/**
* Gets the rem ai decks.
*
* @return the rem ai decks
*/
public boolean getRemAIDecks() {
return this.isRemovedFromAIDecks;
}
/**
* Gets the rem random decks.
*
* @return the rem random decks
*/
public boolean getRemRandomDecks() {
return this.isRemovedFromRandomDecks;
}
/**
* @return the deckHints
*/
public DeckHints getDeckHints() {
return deckHints;
}
/**
* @return the deckHints
*/
public DeckHints getDeckNeeds() {
return deckNeeds;
}
/**
* Gets the ai status comparable.
*
* @return the ai status comparable
*/
public Integer getAiStatusComparable() {
if (this.isRemovedFromAIDecks && this.isRemovedFromRandomDecks) {
return Integer.valueOf(3);
} else if (this.isRemovedFromAIDecks) {
return Integer.valueOf(4);
} else if (this.isRemovedFromRandomDecks) {
return Integer.valueOf(2);
} else {
return Integer.valueOf(1);
}
}
}

View File

@@ -0,0 +1,346 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import forge.item.PaperCard;
import forge.util.Aggregates;
import forge.util.CollectionSuppliers;
import forge.util.Lang;
import forge.util.MyRandom;
public final class CardDb implements ICardDatabase {
public final static String foilSuffix = "+";
private final static int foilSuffixLength = foilSuffix.length();
// need this to obtain cardReference by name+set+artindex
private final Multimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<String,Collection<PaperCard>>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.<PaperCard>arrayLists());
private final Map<String, PaperCard> uniqueCardsByName = new TreeMap<String, PaperCard>(String.CASE_INSENSITIVE_ORDER);
private final Map<String, CardRules> rulesByName;
private final List<PaperCard> allCards = new ArrayList<PaperCard>();
private final List<PaperCard> roAllCards = Collections.unmodifiableList(allCards);
private final Collection<PaperCard> roUniqueCards = Collections.unmodifiableCollection(uniqueCardsByName.values());
private final EditionCollection editions;
public CardDb(Map<String, CardRules> rules, EditionCollection editions0, boolean logMissingCards) {
this.rulesByName = rules;
this.editions = editions0;
List<String> missingCards = new ArrayList<String>();
for(CardEdition e : editions.getOrderedEditions()) {
boolean worthLogging = logMissingCards && ( e.getType() == CardEdition.Type.CORE || e.getType() == CardEdition.Type.EXPANSION || e.getType() == CardEdition.Type.REPRINT );
if(worthLogging)
System.out.print(e.getName() + " (" + e.getCards().length + " cards)");
String lastCardName = null;
int artIdx = 0;
for(CardEdition.CardInSet cis : e.getCards()) {
if ( cis.name.equals(lastCardName) )
artIdx++;
else {
artIdx = 0;
lastCardName = cis.name;
}
CardRules cr = rulesByName.get(lastCardName);
if( cr != null )
addCard(new PaperCard(cr, e.getCode(), cis.rarity, artIdx));
else if (worthLogging)
missingCards.add(cis.name);
}
if(worthLogging) {
if(missingCards.isEmpty())
System.out.println(" ... 100% ");
else {
int missing = (e.getCards().length - missingCards.size()) * 10000 / e.getCards().length;
System.out.printf(" ... %.2f%% (%s missing: %s )%n", missing * 0.01f, Lang.nounWithAmount(missingCards.size(), "card"), StringUtils.join(missingCards, " | ") );
}
missingCards.clear();
}
}
for(CardRules cr : rulesByName.values()) {
if( !allCardsByName.containsKey(cr.getName()) )
{
System.err.println("The card " + cr.getName() + " was not assigned to any set. Adding it to UNKNOWN set... to fix see res/cardeditions/ folder. ");
addCard(new PaperCard(cr, CardEdition.UNKNOWN.getCode(), CardRarity.Special, 0));
}
}
reIndex();
}
private void addCard(PaperCard paperCard) {
allCardsByName.put(paperCard.name, paperCard);
}
private void reIndex() {
uniqueCardsByName.clear();
allCards.clear();
for(Entry<String, Collection<PaperCard>> kv : allCardsByName.asMap().entrySet()) {
uniqueCardsByName.put(kv.getKey(), Iterables.getFirst(kv.getValue(), null));
allCards.addAll(kv.getValue());
}
}
/**
* Splits cardname into Name and set whenever deck line reads as name|set.
*/
private static ImmutablePair<String, String> splitCardName(final String name) {
String cardName = name; // .trim() ?
final int pipePos = cardName.indexOf('|');
if (pipePos >= 0) {
final String setName = cardName.substring(pipePos + 1).trim();
cardName = cardName.substring(0, pipePos);
// only if set is not blank try to load it
if (StringUtils.isNotBlank(setName) && !"???".equals(setName)) {
return new ImmutablePair<String, String>(cardName, setName);
}
}
return new ImmutablePair<String, String>(cardName, null);
}
private boolean isFoil(final String cardName) {
return cardName.toLowerCase().endsWith(CardDb.foilSuffix);
}
/**
* Removes the foil suffix.
*
* @param cardName the card name
* @return the string
*/
public String removeFoilSuffix(final String cardName) {
return cardName.toLowerCase().endsWith(CardDb.foilSuffix) ? cardName.substring(0, cardName.length() - CardDb.foilSuffixLength) : cardName;
}
@Override
public PaperCard tryGetCard(final String cardName0) {
return tryGetCard(cardName0, true);
}
@Override
public PaperCard tryGetCard(final String cardName0, boolean fromLastSet) {
if (null == cardName0) {
return null; // obviously
}
final boolean isFoil = this.isFoil(cardName0);
final String cardName = isFoil ? this.removeFoilSuffix(cardName0) : cardName0;
final ImmutablePair<String, String> nameWithSet = CardDb.splitCardName(cardName);
final PaperCard res = nameWithSet.right == null
? ( fromLastSet ? this.uniqueCardsByName.get(nameWithSet.left) : Aggregates.random(this.allCardsByName.get(nameWithSet.left)) )
: tryGetCard(nameWithSet.left, nameWithSet.right);
return null != res && isFoil ? getFoiled(res) : res;
}
@Override
public PaperCard tryGetCardPrintedByDate(final String name0, final boolean fromLatestSet, final Date printedBefore) {
final boolean isFoil = this.isFoil(name0);
final String cardName = isFoil ? this.removeFoilSuffix(name0) : name0;
final ImmutablePair<String, String> nameWithSet = CardDb.splitCardName(cardName);
PaperCard res = null;
if (null != nameWithSet.right) // set explicitly requested, should return card from it and disregard the date
res = tryGetCard(nameWithSet.left, nameWithSet.right);
else {
Collection<PaperCard> cards = this.allCardsByName.get(nameWithSet.left); // cards are sorted by datetime desc
int idxRightSet = 0;
for (PaperCard card : cards) {
if (editions.get(card.getEdition()).getDate().after(printedBefore))
idxRightSet++;
else
break;
}
res = fromLatestSet ? Iterables.get(cards, idxRightSet, null) : Aggregates.random(Iterables.skip(cards, idxRightSet));
}
return null != res && isFoil ? getFoiled(res) : res;
}
@Override
public PaperCard tryGetCard(final String cardName, String setName) {
return tryGetCard(cardName, setName, -1);
}
@Override
public PaperCard tryGetCard(final String cardName0, String setName, int index) {
final boolean isFoil = this.isFoil(cardName0);
final String cardName = isFoil ? this.removeFoilSuffix(cardName0) : cardName0;
Collection<PaperCard> cards = allCardsByName.get(cardName);
if ( null == cards ) return null;
CardEdition edition = editions.get(setName);
if ( null == edition )
return tryGetCard(cardName, true); // set not found, try to get the same card from just any set.
String effectiveSet = edition.getCode();
PaperCard result = null;
if ( index < 0 ) { // this stands for 'random art'
PaperCard[] candidates = new PaperCard[9]; // 9 cards with same name per set is a maximum of what has been printed (Arnchenemy)
int cnt = 0;
for( PaperCard pc : cards ) {
if( pc.getEdition().equalsIgnoreCase(effectiveSet) )
candidates[cnt++] = pc;
}
if (cnt == 0 ) return null;
result = cnt == 1 ? candidates[0] : candidates[MyRandom.getRandom().nextInt(cnt)];
} else
for( PaperCard pc : cards ) {
if( pc.getEdition().equalsIgnoreCase(effectiveSet) && index == pc.getArtIndex() ) {
result = pc;
break;
}
}
if ( result == null ) return null;
return 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);
}
@Override
public int getPrintCount(String cardName, String edition) {
int cnt = 0;
for( PaperCard pc : allCardsByName.get(cardName) ) {
if( pc.getEdition().equals(edition) )
cnt++;
}
return cnt;
}
@Override
public int getMaxPrintCount(String cardName) {
int max = -1;
for( PaperCard pc : allCardsByName.get(cardName) ) {
if ( max < pc.getArtIndex() )
max = pc.getArtIndex();
}
return max + 1;
}
// Single fetch
@Override
public PaperCard getCard(final String name) {
return this.getCard(name, false);
}
@Override
public PaperCard getCard(final String name0, final boolean fromLatestSet) {
// Sometimes they read from decks things like "CardName|Set" - but we
// can handle it
final PaperCard result = tryGetCard(name0, fromLatestSet);
if (null == result) {
throw new NoSuchElementException(String.format("Card '%s' not found in our database.", name0));
}
return result;
}
@Override
public PaperCard getCardPrintedByDate(final String name0, final boolean fromLatestSet, Date printedBefore ) {
// Sometimes they read from decks things like "CardName|Set" - but we
// can handle it
final PaperCard result = tryGetCard(name0, fromLatestSet);
if (null == result) {
throw new NoSuchElementException(String.format("Card '%s' released before %s not found in our database.", name0, printedBefore.toString()));
}
return result;
}
// Advanced fetch by name+set
@Override
public PaperCard getCard(final String name, final String set) {
return this.getCard(name, set, -1);
}
@Override
public PaperCard getCard(final String name, final String set, final int artIndex) {
final PaperCard result = tryGetCard(name, set, artIndex);
if (null == result) {
final String message = String.format("Asked for '%s' from '%s' #%d: db didn't find that copy.", name, set, artIndex);
throw new NoSuchElementException(message);
}
return result;
}
// returns a list of all cards from their respective latest editions
@Override
public Collection<PaperCard> getUniqueCards() {
return roUniqueCards;
}
@Override
public List<PaperCard> getAllCards() {
return roAllCards;
}
/** Returns a modifiable list of cards matching the given predicate */
@Override
public List<PaperCard> getAllCards(Predicate<PaperCard> predicate) {
return Lists.newArrayList(Iterables.filter(this.roAllCards, predicate));
}
public Predicate<? super PaperCard> wasPrintedInSets(List<String> setCodes) {
return new PredicateExistsInSets(setCodes);
}
private class PredicateExistsInSets implements Predicate<PaperCard> {
private final List<String> sets;
public PredicateExistsInSets(final List<String> wantSets) {
this.sets = wantSets; // maybe should make a copy here?
}
@Override
public boolean apply(final PaperCard subject) {
Collection<PaperCard> cc = allCardsByName.get(subject.getName());
for(PaperCard c : cc)
if (sets.contains(c.getEdition()))
return true;
return false;
}
}
}

View File

@@ -0,0 +1,302 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.io.File;
import java.io.FilenameFilter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import forge.util.FileSection;
import forge.util.FileUtil;
import forge.util.storage.StorageReaderFolder;
/**
* <p>
* CardSet class.
* </p>
*
* @author Forge
* @version $Id: CardSet.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardEdition implements Comparable<CardEdition> { // immutable
public enum Type {
UNKNOWN,
CORE,
EXPANSION,
REPRINT,
STARTER,
DUEL_DECKS,
PREMIUM_DECK_SERIES,
FROM_THE_VAULT,
OTHER,
THIRDPARTY // custom sets
}
public enum FoilType {
NOT_SUPPORTED, // sets before Urza's Legacy
OLD_STYLE, // sets between Urza's Legacy and 8th Edition
MODERN // 8th Edition and newer
}
public static class CardInSet {
public final CardRarity rarity;
public final String name;
public CardInSet(final String name, final CardRarity rarity) {
this.rarity = rarity;
this.name = name;
}
}
/** The Constant unknown. */
private final static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
public static final CardEdition UNKNOWN = new CardEdition("1990-01-01", "??", "???", Type.UNKNOWN, "Undefined", FoilType.NOT_SUPPORTED, new CardInSet[]{});
private Date date;
private String code2;
private String code;
private Type type;
private String name;
private String alias = null;
private boolean whiteBorder = false;
private FoilType foilType = FoilType.NOT_SUPPORTED;
private int foilChanceInBooster = 0;
private boolean foilAlwaysInCommonSlot = false;
private final CardInSet[] cards;
private int boosterArts = 1;
private SealedProductTemplate boosterTpl = null;
private CardEdition(CardInSet[] cards) {
this.cards = cards;
}
/**
* Instantiates a new card set.
*
* @param index indicates order of set release date
* @param code2 the 2 (usually) letter code used for image filenames/URLs distributed by the HQ pics team that
* use Magic Workstation-type edition codes. Older sets only had 2-letter codes, and some of the 3-letter
* codes they use now aren't the same as the official list of 3-letter codes. When Forge downloads set-pics,
* it uses the 3-letter codes for the folder no matter the age of the set.
* @param code the MTG 3-letter set code
* @param type the set type
* @param name the name of the set
* @param an optional secondary code alias for the set
*/
private CardEdition(String date, String code2, String code, Type type, String name, FoilType foil, CardInSet[] cards) {
this(cards);
this.code2 = code2;
this.code = code;
this.type = type;
this.name = name;
this.date = parseDate(date);
this.foilType = foil;
}
private static Date parseDate(String date) {
if( date.length() <= 7 )
date = date + "-01";
try {
return formatter.parse(date);
} catch (ParseException e) {
return new Date();
}
}
public Date getDate() { return date; }
public String getCode2() { return code2; }
public String getCode() { return code; }
public Type getType() { return type; }
public String getName() { return name; }
public String getAlias() { return alias; }
public FoilType getFoilType() { return foilType; }
public int getFoilChanceInBooster() { return foilChanceInBooster; }
public boolean getFoilAlwaysInCommonSlot() { return foilAlwaysInCommonSlot; }
public CardInSet[] getCards() { return cards; }
/** The Constant fnGetName. */
public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() {
@Override
public String apply(final CardEdition arg1) {
return arg1.getCode();
}
};
@Override
public int compareTo(final CardEdition o) {
if (o == null) {
return 1;
}
return date.compareTo(o.date);
}
@Override
public int hashCode() {
return (this.code.hashCode() * 17) + this.name.hashCode();
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final CardEdition other = (CardEdition) obj;
return other.name.equals(this.name) && this.code.equals(other.code);
}
@Override
public String toString() {
return this.name + " (set)";
}
/**
* @return the whiteBorder
*/
public boolean isWhiteBorder() {
return whiteBorder;
}
public int getCntBoosterPictures() {
return boosterArts;
}
public SealedProductTemplate getBoosterTemplate() {
return boosterTpl;
}
public boolean hasBoosterTemplate() {
return boosterTpl != null;
}
public static class Reader extends StorageReaderFolder<CardEdition> {
public Reader(File path) {
super(path, CardEdition.FN_GET_CODE);
}
public final static CardInSet[] arrCards = new CardInSet[] {};
@Override
protected CardEdition read(File file) {
final Map<String, List<String>> contents = FileSection.parseSections(FileUtil.readFile(file));
List<CardEdition.CardInSet> processedCards = new ArrayList<CardEdition.CardInSet>();
for(String line : contents.get("cards")) {
if (StringUtils.isBlank(line))
continue;
// You may omit rarity for early development
CardRarity r = CardRarity.smartValueOf(line.substring(0, 1));
boolean hadRarity = r != CardRarity.Unknown && line.charAt(1) == ' ';
String cardName = hadRarity ? line.substring(2) : line;
CardInSet cis = new CardInSet(cardName, r);
processedCards.add(cis);
}
CardEdition res = new CardEdition(processedCards.toArray(arrCards));
FileSection section = FileSection.parse(contents.get("metadata"), "=");
res.name = section.get("name");
res.date = parseDate(section.get("date"));
res.code = section.get("code");
res.code2 = section.get("code2");
if( res.code2 == null )
res.code2 = res.code;
res.boosterArts = section.getInt("BoosterCovers", 1);
String boosterDesc = section.get("Booster");
res.boosterTpl = boosterDesc == null ? null : new SealedProductTemplate(res.code, SealedProductTemplate.Reader.parseSlots(boosterDesc));
res.alias = section.get("alias");
res.whiteBorder = "white".equalsIgnoreCase(section.get("border"));
String type = section.get("type");
Type enumType = Type.UNKNOWN;
if (null != type && !type.isEmpty()) {
try {
enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH));
} catch (IllegalArgumentException e) {
// ignore; type will get UNKNOWN
System.err.println(String.format("Ignoring unknown type in set definitions: name: %s; type: %s", res.name, type));
}
}
res.type = enumType;
switch(section.get("foil", "newstyle").toLowerCase()) {
case "notsupported":
res.foilType = FoilType.NOT_SUPPORTED;
break;
case "oldstyle":
case "classic":
res.foilType = FoilType.OLD_STYLE;
break;
case "newstyle":
case "modern":
res.foilType = FoilType.MODERN;
break;
default:
res.foilType = FoilType.NOT_SUPPORTED;
break;
}
res.foilChanceInBooster = section.getInt("FoilChanceInBooster", 16);
res.foilAlwaysInCommonSlot = section.getBoolean("FoilAlwaysInCommonSlot", false);
return res;
}
@Override
protected FilenameFilter getFileFilter() {
return TXT_FILE_FILTER;
}
public static final FilenameFilter TXT_FILE_FILTER = new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith(".txt");
}
};
}
}

View File

@@ -0,0 +1,222 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.util.List;
import forge.card.mana.ManaCost;
/**
* A collection of methods containing full
* meta and gameplay properties of a card.
*
* @author Forge
* @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class CardRules implements ICardCharacteristics {
private final CardSplitType splitType;
private final ICardFace mainPart;
private final ICardFace otherPart;
//private final Map<String, CardInSet> setsPrinted = new TreeMap<String, CardInSet>(String.CASE_INSENSITIVE_ORDER);
private CardAiHints aiHints;
private ColorSet colorIdentity = null;
public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) {
splitType = altMode;
mainPart = faces[0];
otherPart = faces[1];
aiHints = cah;
//System.out.print(faces[0].getName());
// for (Entry<String, CardInSet> cs : sets.entrySet()) {
// if( CardRulesReader.editions.get(cs.getKey()) != null )
// setsPrinted.put(cs.getKey(), cs.getValue());
// }
//
// if ( setsPrinted.isEmpty() ) {
// System.err.println(getName() + " was not assigned any set.");
// setsPrinted.put(CardEdition.UNKNOWN.getCode(), new CardInSet(CardRarity.Common, 1) );
// }
//
//Calculate Color Identity
byte colMask = calculateColorIdentity(mainPart);
if(otherPart != null)
{
colMask |= calculateColorIdentity(otherPart);
}
colorIdentity = ColorSet.fromMask(colMask);
}
private byte calculateColorIdentity(ICardFace face)
{
byte res = face.getColor().getColor();
boolean isReminder = false;
boolean isSymbol = false;
String oracleText = face.getOracleText();
int len = oracleText.length();
for(int i = 0; i < len; i++) {
char c = oracleText.charAt(i); // This is to avoid needless allocations performed by toCharArray()
switch(c) {
case('('): isReminder = i > 0; break; // if oracle has only reminder, consider it valid rules (basic and true lands need this)
case(')'): isReminder = false; break;
case('{'): isSymbol = true; break;
case('}'): isSymbol = false; break;
default:
if(isSymbol && !isReminder) {
switch(c) {
case('W'): res |= MagicColor.WHITE; break;
case('U'): res |= MagicColor.BLUE; break;
case('B'): res |= MagicColor.BLACK; break;
case('R'): res |= MagicColor.RED; break;
case('G'): res |= MagicColor.GREEN; break;
}
}
break;
}
}
return res;
}
public boolean isVariant() {
CardType t = getType();
return t.isVanguard() || t.isScheme() || t.isPlane() || t.isPhenomenon();
}
public CardSplitType getSplitType() {
return splitType;
}
public ICardFace getMainPart() {
return mainPart;
}
public ICardFace getOtherPart() {
return otherPart;
}
public String getName() {
switch(splitType.getAggregationMethod()) {
case AGGREGATE:
return mainPart.getName() + " // " + otherPart.getName();
default:
return mainPart.getName();
}
}
public CardAiHints getAiHints() {
return aiHints;
}
@Override
public CardType getType() {
switch(splitType.getAggregationMethod()) {
case AGGREGATE: // no cards currently have different types
return CardType.combine(mainPart.getType(), otherPart.getType());
default:
return mainPart.getType();
}
}
@Override
public ManaCost getManaCost() {
switch(splitType.getAggregationMethod()) {
case AGGREGATE:
return ManaCost.combine(mainPart.getManaCost(), otherPart.getManaCost());
default:
return mainPart.getManaCost();
}
}
@Override
public ColorSet getColor() {
switch(splitType.getAggregationMethod()) {
case AGGREGATE:
return ColorSet.fromMask(mainPart.getColor().getColor() | otherPart.getColor().getColor());
default:
return mainPart.getColor();
}
}
@Override public int getIntPower() { return mainPart.getIntPower(); }
@Override public int getIntToughness() { return mainPart.getIntToughness(); }
@Override public String getPower() { return mainPart.getPower(); }
@Override public String getToughness() { return mainPart.getToughness(); }
@Override public int getInitialLoyalty() { return mainPart.getInitialLoyalty(); }
@Override
public String getOracleText() {
switch(splitType.getAggregationMethod()) {
case AGGREGATE:
return mainPart.getOracleText() + "\r\n\r\n" + otherPart.getOracleText();
default:
return mainPart.getOracleText();
}
}
// public Set<String> getSets() { return this.setsPrinted.keySet(); }
// public CardInSet getEditionInfo(final String setCode) {
// final CardInSet result = this.setsPrinted.get(setCode);
// return result; // if returns null, String.format("Card '%s' was never printed in set '%s'", this.getName(), setCode);
// }
// vanguard card fields, they don't use sides.
private int deltaHand;
private int deltaLife;
public int getHand() { return deltaHand; }
public int getLife() { return deltaLife; }
public void setVanguardProperties(String pt) {
final int slashPos = pt == null ? -1 : pt.indexOf('/');
if (slashPos == -1) {
throw new RuntimeException(String.format("Vanguard '%s' has bad hand/life stats", this.getName()));
}
this.deltaHand = Integer.parseInt(pt.substring(0, slashPos).replace("+", ""));
this.deltaLife = Integer.parseInt(pt.substring(slashPos+1).replace("+", ""));
}
// Downloadable image
private String dlUrl;
private String dlUrlOtherSide;
public String getPictureUrl(boolean backface ) { return backface ? dlUrlOtherSide : dlUrl; }
public void setDlUrls(String[] dlUrls) { this.dlUrl = dlUrls[0]; this.dlUrlOtherSide = dlUrls[1]; }
public final List<String> getReplacements() {
return null;
}
public final List<String> getTriggers() {
return null;
}
public final List<String> getStaticAbilities() {
return null;
}
public final List<String> getAbilities() {
return null;
}
public ColorSet getColorIdentity() {
return colorIdentity;
}
}

View File

@@ -0,0 +1,549 @@
package forge.card;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.util.ComparableOp;
import forge.util.PredicateString;
/**
* Filtering conditions specific for CardRules class, defined here along with
* some presets.
*/
public final class CardRulesPredicates {
/** The Constant isKeptInAiDecks. */
public static final Predicate<CardRules> IS_KEPT_IN_AI_DECKS = new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules card) {
return !card.getAiHints().getRemAIDecks();
}
};
/** The Constant isKeptInRandomDecks. */
public static final Predicate<CardRules> IS_KEPT_IN_RANDOM_DECKS = new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules card) {
return !card.getAiHints().getRemRandomDecks();
}
};
// Static builder methods - they choose concrete implementation by
// themselves
/**
* Cmc.
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> cmc(final ComparableOp op, final int what) {
return new LeafNumber(LeafNumber.CardField.CMC, op, what);
}
/**
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> power(final ComparableOp op, final int what) {
return new LeafNumber(LeafNumber.CardField.POWER, op, what);
}
/**
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> toughness(final ComparableOp op, final int what) {
return new LeafNumber(LeafNumber.CardField.TOUGHNESS, op, what);
}
// P/T
/**
* Rules.
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> rules(final PredicateString.StringOp op, final String what) {
return new LeafString(LeafString.CardField.ORACLE_TEXT, op, what);
}
/**
* Name.
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> name(final PredicateString.StringOp op, final String what) {
return new LeafString(LeafString.CardField.NAME, op, what);
}
/**
* TODO: Write javadoc for this method.
* @param transform
* @return
*/
public static Predicate<CardRules> splitType(CardSplitType transform) {
return new PredicateSplitType(transform);
}
/**
* Sub type.
*
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> subType(final String what) {
return new LeafString(LeafString.CardField.SUBTYPE, PredicateString.StringOp.CONTAINS, what);
}
/**
* Sub type.
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> subType(final PredicateString.StringOp op, final String what) {
return new LeafString(LeafString.CardField.SUBTYPE, op, what);
}
/**
* Joined type.
*
* @param op
* the op
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> joinedType(final PredicateString.StringOp op, final String what) {
return new LeafString(LeafString.CardField.JOINED_TYPE, op, what);
}
/**
* Has Keyword.
*
* @param keyword
* the keyword
* @return the predicate
*/
public static Predicate<CardRules> hasKeyword(final String keyword) {
return new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules card) {
return Iterables.contains(card.getMainPart().getKeywords(), keyword);
}
};
}
/**
* Core type.
*
* @param isEqual
* the is equal
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> coreType(final boolean isEqual, final String what) {
try {
return CardRulesPredicates.coreType(isEqual, Enum.valueOf(CardCoreType.class, what));
} catch (final Exception e) {
return com.google.common.base.Predicates.alwaysFalse();
}
}
/**
* Core type.
*
* @param isEqual
* the is equal
* @param type
* the type
* @return the predicate
*/
public static Predicate<CardRules> coreType(final boolean isEqual, final CardCoreType type) {
return new PredicateCoreType(type, isEqual);
}
/**
* Super type.
*
* @param isEqual
* the is equal
* @param what
* the what
* @return the predicate
*/
public static Predicate<CardRules> superType(final boolean isEqual, final String what) {
try {
return CardRulesPredicates.superType(isEqual, Enum.valueOf(CardSuperType.class, what));
} catch (final Exception e) {
return com.google.common.base.Predicates.alwaysFalse();
}
}
/**
* Super type.
*
* @param isEqual
* the is equal
* @param type
* the type
* @return the predicate
*/
public static Predicate<CardRules> superType(final boolean isEqual, final CardSuperType type) {
return new PredicateSuperType(type, isEqual);
}
/**
* Checks for color.
*
* @param thatColor
* the that color
* @return the predicate
*/
public static Predicate<CardRules> hasColor(final byte thatColor) {
return new LeafColor(LeafColor.ColorOperator.HasAllOf, thatColor);
}
/**
* Checks if is color.
*
* @param thatColor
* the that color
* @return the predicate
*/
public static Predicate<CardRules> isColor(final byte thatColor) {
return new LeafColor(LeafColor.ColorOperator.HasAnyOf, thatColor);
}
/**
* Checks if is exactly that color.
*
* @param thatColor
* color to check
* @return the predicate
*/
public static Predicate<CardRules> isMonoColor(final byte thatColor) {
return new LeafColor(LeafColor.ColorOperator.Equals, thatColor);
}
/**
* Checks for cnt colors.
*
* @param cntColors
* the cnt colors
* @return the predicate
*/
public static Predicate<CardRules> hasCntColors(final byte cntColors) {
return new LeafColor(LeafColor.ColorOperator.CountColors, cntColors);
}
/**
* Checks for at least cnt colors.
*
* @param cntColors
* the cnt colors
* @return the predicate
*/
public static Predicate<CardRules> hasAtLeastCntColors(final byte cntColors) {
return new LeafColor(LeafColor.ColorOperator.CountColorsGreaterOrEqual, cntColors);
}
private static class LeafString extends PredicateString<CardRules> {
public enum CardField {
ORACLE_TEXT, NAME, SUBTYPE, JOINED_TYPE
}
private final String operand;
private final LeafString.CardField field;
@Override
public boolean apply(final CardRules card) {
boolean shouldContain;
switch (this.field) {
case NAME:
return op(card.getName(), this.operand);
case SUBTYPE:
shouldContain = (this.getOperator() == StringOp.CONTAINS) || (this.getOperator() == StringOp.EQUALS);
return shouldContain == card.getType().subTypeContains(this.operand);
case ORACLE_TEXT:
return op(card.getOracleText(), operand);
case JOINED_TYPE:
return op(card.getType().toString(), operand);
default:
return false;
}
}
public LeafString(final LeafString.CardField field, final StringOp operator, final String operand) {
super(operator);
this.field = field;
this.operand = operand;
}
}
private static class LeafColor implements Predicate<CardRules> {
public enum ColorOperator {
CountColors, CountColorsGreaterOrEqual, HasAnyOf, HasAllOf, Equals
}
private final LeafColor.ColorOperator op;
private final byte color;
public LeafColor(final LeafColor.ColorOperator operator, final byte thatColor) {
this.op = operator;
this.color = thatColor;
}
@Override
public boolean apply(final CardRules subject) {
if (null == subject) {
return false;
}
switch (this.op) {
case CountColors:
return subject.getColor().countColors() == this.color;
case CountColorsGreaterOrEqual:
return subject.getColor().countColors() >= this.color;
case Equals:
return subject.getColor().isEqual(this.color);
case HasAllOf:
return subject.getColor().hasAllColors(this.color);
case HasAnyOf:
return subject.getColor().hasAnyColor(this.color);
default:
return false;
}
}
}
public static class LeafNumber implements Predicate<CardRules> {
public enum CardField {
CMC, POWER, TOUGHNESS,
}
private final LeafNumber.CardField field;
private final ComparableOp operator;
private final int operand;
public LeafNumber(final LeafNumber.CardField field, final ComparableOp op, final int what) {
this.field = field;
this.operand = what;
this.operator = op;
}
@Override
public boolean apply(final CardRules card) {
int value;
switch (this.field) {
case CMC:
return this.op(card.getManaCost().getCMC(), this.operand);
case POWER:
value = card.getIntPower();
return value >= 0 ? this.op(value, this.operand) : false;
case TOUGHNESS:
value = card.getIntToughness();
return value >= 0 ? this.op(value, this.operand) : false;
default:
return false;
}
}
private boolean op(final int op1, final int op2) {
switch (this.operator) {
case EQUALS:
return op1 == op2;
case GREATER_THAN:
return op1 > op2;
case GT_OR_EQUAL:
return op1 >= op2;
case LESS_THAN:
return op1 < op2;
case LT_OR_EQUAL:
return op1 <= op2;
case NOT_EQUALS:
return op1 != op2;
default:
return false;
}
}
}
private static class PredicateCoreType implements Predicate<CardRules> {
private final CardCoreType operand;
private final boolean shouldBeEqual;
@Override
public boolean apply(final CardRules card) {
if (null == card) {
return false;
}
return this.shouldBeEqual == card.getType().typeContains(this.operand);
}
public PredicateCoreType(final CardCoreType type, final boolean wantEqual) {
this.operand = type;
this.shouldBeEqual = wantEqual;
}
}
private static class PredicateSuperType implements Predicate<CardRules> {
private final CardSuperType operand;
private final boolean shouldBeEqual;
@Override
public boolean apply(final CardRules card) {
return this.shouldBeEqual == card.getType().superTypeContains(this.operand);
}
public PredicateSuperType(final CardSuperType type, final boolean wantEqual) {
this.operand = type;
this.shouldBeEqual = wantEqual;
}
}
private static class PredicateSplitType implements Predicate<CardRules> {
private final CardSplitType cst;
public PredicateSplitType(final CardSplitType type) {
cst = type;
}
@Override
public boolean apply(final CardRules subject) {
return subject.getSplitType() == cst;
}
}
/**
* The Class Presets.
*/
public static class Presets {
/** The Constant isCreature. */
public static final Predicate<CardRules> IS_CREATURE = CardRulesPredicates
.coreType(true, CardCoreType.Creature);
public static final Predicate<CardRules> IS_LEGENDARY = CardRulesPredicates
.superType(true, CardSuperType.Legendary);
/** The Constant isArtifact. */
public static final Predicate<CardRules> IS_ARTIFACT = CardRulesPredicates
.coreType(true, CardCoreType.Artifact);
/** The Constant isEquipment. */
public static final Predicate<CardRules> IS_EQUIPMENT = CardRulesPredicates
.subType("Equipment");
/** The Constant isLand. */
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardCoreType.Land);
/** The Constant isBasicLand. */
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules subject) {
return subject.getType().isBasicLand();
}
};
/** The Constant isNonBasicLand. */
public static final Predicate<CardRules> IS_NONBASIC_LAND = new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules subject) {
return subject.getType().isLand() && !subject.getType().isBasicLand();
}
};
/** The Constant isPlaneswalker. */
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true,
CardCoreType.Planeswalker);
/** The Constant isInstant. */
public static final Predicate<CardRules> IS_INSTANT = CardRulesPredicates.coreType(true, CardCoreType.Instant);
/** The Constant isSorcery. */
public static final Predicate<CardRules> IS_SORCERY = CardRulesPredicates.coreType(true, CardCoreType.Sorcery);
/** The Constant isEnchantment. */
public static final Predicate<CardRules> IS_ENCHANTMENT = CardRulesPredicates.coreType(true, CardCoreType.Enchantment);
public static final Predicate<CardRules> IS_PLANE = CardRulesPredicates.coreType(true, CardCoreType.Plane);
public static final Predicate<CardRules> IS_PHENOMENON = CardRulesPredicates.coreType(true, CardCoreType.Phenomenon);
public static final Predicate<CardRules> IS_PLANE_OR_PHENOMENON = Predicates.or(IS_PLANE, IS_PHENOMENON);
public static final Predicate<CardRules> IS_SCHEME = CardRulesPredicates.coreType(true, CardCoreType.Scheme);
public static final Predicate<CardRules> IS_VANGUARD = CardRulesPredicates.coreType(true, CardCoreType.Vanguard);
/** The Constant isNonLand. */
public static final Predicate<CardRules> IS_NON_LAND = CardRulesPredicates.coreType(false, CardCoreType.Land);
/** The Constant isNonCreatureSpell. */
public static final Predicate<CardRules> IS_NON_CREATURE_SPELL = Predicates.not(Predicates.or(
Presets.IS_CREATURE, Presets.IS_LAND));
/** The Constant IS_NONCREATURE_SPELL_FOR_GENERATOR. **/
@SuppressWarnings("unchecked")
public static final Predicate<CardRules> IS_NONCREATURE_SPELL_FOR_GENERATOR = com.google.common.base.Predicates
.or(Presets.IS_SORCERY, Presets.IS_INSTANT, Presets.IS_PLANESWALKER, Presets.IS_ENCHANTMENT,
Predicates.and(Presets.IS_ARTIFACT, Predicates.not(Presets.IS_CREATURE)));
/** The Constant isWhite. */
public static final Predicate<CardRules> IS_WHITE = CardRulesPredicates.isColor(MagicColor.WHITE);
/** The Constant isBlue. */
public static final Predicate<CardRules> IS_BLUE = CardRulesPredicates.isColor(MagicColor.BLUE);
/** The Constant isBlack. */
public static final Predicate<CardRules> IS_BLACK = CardRulesPredicates.isColor(MagicColor.BLACK);
/** The Constant isRed. */
public static final Predicate<CardRules> IS_RED = CardRulesPredicates.isColor(MagicColor.RED);
/** The Constant isGreen. */
public static final Predicate<CardRules> IS_GREEN = CardRulesPredicates.isColor(MagicColor.GREEN);
/** The Constant isColorless. */
public static final Predicate<CardRules> IS_COLORLESS = CardRulesPredicates.hasCntColors((byte) 0);
/** The Constant isMulticolor. */
public static final Predicate<CardRules> IS_MULTICOLOR = CardRulesPredicates.hasAtLeastCntColors((byte) 2);
public static final Predicate<CardRules> IS_MONOCOLOR = CardRulesPredicates.hasCntColors((byte) 1);
/** The Constant colors. */
public static final List<Predicate<CardRules>> COLORS = new ArrayList<Predicate<CardRules>>();
static {
Presets.COLORS.add(Presets.IS_WHITE);
Presets.COLORS.add(Presets.IS_BLUE);
Presets.COLORS.add(Presets.IS_BLACK);
Presets.COLORS.add(Presets.IS_RED);
Presets.COLORS.add(Presets.IS_GREEN);
Presets.COLORS.add(Presets.IS_COLORLESS);
}
}
}

View File

@@ -24,8 +24,6 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
import forge.Constant;
/**
* <p>
* Immutable Card type. Can be build only from parsing a string.
@@ -236,6 +234,47 @@ public final class CardType implements Comparable<CardType> {
return this.coreType.contains(CardCoreType.Phenomenon);
}
/**
* The Interface CardTypes.
*/
public static class Constant {
/** The loaded. */
public static final boolean[] LOADED = { false };
/** The card types. */
public static final List<String> CARD_TYPES = new ArrayList<String>();
/** The super types. */
public static final List<String> SUPER_TYPES = new ArrayList<String>();
/** The basic types. */
public static final List<String> BASIC_TYPES = new ArrayList<String>();
/** The land types. */
public static final List<String> LAND_TYPES = new ArrayList<String>();
/** The creature types. */
public static final List<String> CREATURE_TYPES = new ArrayList<String>();
/** The instant types. */
public static final List<String> INSTANT_TYPES = new ArrayList<String>();
/** The sorcery types. */
public static final List<String> SORCERY_TYPES = new ArrayList<String>();
/** The enchantment types. */
public static final List<String> ENCHANTMENT_TYPES = new ArrayList<String>();
/** The artifact types. */
public static final List<String> ARTIFACT_TYPES = new ArrayList<String>();
/** The walker types. */
public static final List<String> WALKER_TYPES = new ArrayList<String>();
}
///////// Utility methods
public static boolean isACardType(final String cardType) {
return CardType.getAllCardTypes().contains(cardType);
@@ -245,7 +284,7 @@ public final class CardType implements Comparable<CardType> {
final ArrayList<String> types = new ArrayList<String>();
// types.addAll(getCardTypes());
types.addAll(Constant.CardTypes.CARD_TYPES);
types.addAll(Constant.CARD_TYPES);
// not currently used by Forge
types.add("Plane");
@@ -258,7 +297,7 @@ public final class CardType implements Comparable<CardType> {
public static ArrayList<String> getBasicTypes() {
final ArrayList<String> types = new ArrayList<String>();
types.addAll(Constant.CardTypes.BASIC_TYPES);
types.addAll(Constant.BASIC_TYPES);
return types;
}
@@ -266,8 +305,8 @@ public final class CardType implements Comparable<CardType> {
public static ArrayList<String> getLandTypes() {
final ArrayList<String> types = new ArrayList<String>();
types.addAll(Constant.CardTypes.BASIC_TYPES);
types.addAll(Constant.CardTypes.LAND_TYPES);
types.addAll(Constant.BASIC_TYPES);
types.addAll(Constant.LAND_TYPES);
return types;
}
@@ -275,13 +314,13 @@ public final class CardType implements Comparable<CardType> {
public static ArrayList<String> getCreatureTypes() {
final ArrayList<String> types = new ArrayList<String>();
types.addAll(Constant.CardTypes.CREATURE_TYPES);
types.addAll(Constant.CREATURE_TYPES);
return types;
}
public static boolean isASuperType(final String cardType) {
return (Constant.CardTypes.SUPER_TYPES.contains(cardType));
return (Constant.SUPER_TYPES.contains(cardType));
}
public static boolean isASubType(final String cardType) {
@@ -289,18 +328,18 @@ public final class CardType implements Comparable<CardType> {
}
public static boolean isACreatureType(final String cardType) {
return (Constant.CardTypes.CREATURE_TYPES.contains(cardType));
return (Constant.CREATURE_TYPES.contains(cardType));
}
public static boolean isALandType(final String cardType) {
return (Constant.CardTypes.LAND_TYPES.contains(cardType));
return (Constant.LAND_TYPES.contains(cardType));
}
public static boolean isAPlaneswalkerType(final String cardType) {
return (Constant.CardTypes.WALKER_TYPES.contains(cardType));
return (Constant.WALKER_TYPES.contains(cardType));
}
public static boolean isABasicLandType(final String cardType) {
return (Constant.CardTypes.BASIC_TYPES.contains(cardType));
return (Constant.BASIC_TYPES.contains(cardType));
}
}

View File

@@ -17,7 +17,6 @@
*/
package forge.card;
import forge.Constant;
import forge.card.mana.ManaCost;
import forge.util.BinaryUtil;
@@ -253,7 +252,7 @@ public final class ColorSet implements Comparable<ColorSet> {
return "n/a";
}
String toReturn = MagicColor.toLongString(myColor);
if (toReturn == Constant.Color.COLORLESS && myColor != 0) {
if (toReturn == MagicColor.Constant.COLORLESS && myColor != 0) {
return "multi";
}
return toReturn;

View File

@@ -0,0 +1,128 @@
package forge.card;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import forge.item.PaperCard;
import forge.util.PredicateString.StringOp;
/**
* DeckHints provides the ability for a Card to "want" another Card or type of
* Cards in its random deck.
*
*/
public class DeckHints {
/**
* Enum of types of DeckHints.
*/
public enum Type {
/** The Color. */
COLOR,
/** The Keyword. */
KEYWORD,
/** The Name. */
NAME,
/** The Type. */
TYPE,
/** The None. */
NONE
}
private Type type = Type.NONE;
private String filterParam = null;
/**
* Construct a DeckHints from the SVar string.
*
* @param wants
* SVar for DeckHints
*/
public DeckHints(String wants) {
String[] pieces = wants.split("\\$");
if (pieces.length == 2) {
try {
Type typeValue = Type.valueOf(pieces[0].toUpperCase());
for (Type t : Type.values()) {
if (typeValue == t) {
type = t;
break;
}
}
} catch (IllegalArgumentException e) {
// type will remain NONE
}
filterParam = pieces[1];
}
}
/**
* @return the type
*/
public Type getType() {
return type;
}
/**
* Returns a list of Cards from the given List<Card> that match this
* DeckHints. I.e., other cards that this Card needs in its deck.
*
* @param cardList
* list of cards to be filtered
* @return List<Card> of Cards that match this DeckHints.
*/
public List<PaperCard> filter(Iterable<PaperCard> cardList) {
List<PaperCard> ret;
switch (type) {
case TYPE:
ret = new ArrayList<PaperCard>();
String[] types = filterParam.split("\\|");
for (String type : types) {
addMatchingItems(ret, cardList, CardRulesPredicates.subType(type), PaperCard.FN_GET_RULES);
}
break;
case COLOR:
ret = new ArrayList<PaperCard>();
String[] colors = filterParam.split("\\|");
for (String color : colors) {
ColorSet cc = ColorSet.fromNames(color);
addMatchingItems(ret, cardList, CardRulesPredicates.isColor(cc.getColor()), PaperCard.FN_GET_RULES);
}
break;
case KEYWORD:
ret = new ArrayList<PaperCard>();
String[] keywords = filterParam.split("\\|");
for (String keyword : keywords) {
addMatchingItems(ret, cardList, CardRulesPredicates.hasKeyword(keyword), PaperCard.FN_GET_RULES);
}
break;
case NAME:
ret = new ArrayList<PaperCard>();
String[] names = filterParam.split("\\|");
for (String name : names) {
addMatchingItems(ret, cardList, CardRulesPredicates.name(StringOp.EQUALS, name), PaperCard.FN_GET_RULES);
}
break;
default:
ret = Lists.newArrayList(cardList);
break;
}
return ret;
}
private static <T, U> void addMatchingItems(Collection<? super T> dest, Iterable<? extends T> source, Predicate<U> predicate, Function<T, U> fn) {
for (T item : Iterables.filter(source, Predicates.compose(predicate, fn))) {
dest.add(item);
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.util.IItemReader;
import forge.util.storage.StorageBase;
import forge.util.storage.StorageReaderBase;
public final class EditionCollection extends StorageBase<CardEdition> {
private final Map<String, CardEdition> aliasToEdition = new TreeMap<String, CardEdition>(String.CASE_INSENSITIVE_ORDER);
public EditionCollection(IItemReader<CardEdition> reader) {
super("Card editions", reader);
for (CardEdition ee : this) {
String alias = ee.getAlias();
if (null != alias) {
aliasToEdition.put(alias, ee);
}
aliasToEdition.put(ee.getCode2(), ee);
}
}
/**
* Gets a sets by code. It will search first by three letter codes, then by aliases and two-letter codes.
*
* @param code
* the code
* @return the sets the by code
*/
@Override
public CardEdition get(final String code) {
CardEdition baseResult = super.get(code);
return baseResult == null ? aliasToEdition.get(code) : baseResult;
}
public Iterable<CardEdition> getOrderedEditions() {
List<CardEdition> res = Lists.newArrayList(this);
Collections.sort(res);
Collections.reverse(res);
return res;
}
/**
* Gets the sets by code or throw.
*
* @param code
* the code
* @return the sets the by code or throw
*/
public CardEdition getEditionByCodeOrThrow(final String code) {
final CardEdition set = this.get(code);
if (null == set) {
throw new RuntimeException(String.format("Edition with code '%s' not found", code));
}
return set;
}
// used by image generating code
/**
* Gets the code2 by code.
*
* @param code
* the code
* @return the code2 by code
*/
public String getCode2ByCode(final String code) {
final CardEdition set = this.get(code);
return set == null ? "" : set.getCode2();
}
public final Function<String, CardEdition> FN_EDITION_BY_CODE = new Function<String, CardEdition>() {
@Override
public CardEdition apply(String code) {
return EditionCollection.this.get(code);
};
};
/**
* TODO: Write javadoc for this method.
* @return
*/
public IItemReader<SealedProductTemplate> getBoosterGenerator() {
// TODO Auto-generated method stub
return new StorageReaderBase<SealedProductTemplate>(null) {
@Override
public Map<String, SealedProductTemplate> readAll() {
Map<String, SealedProductTemplate> map = new TreeMap<String, SealedProductTemplate>(String.CASE_INSENSITIVE_ORDER);
for(CardEdition ce : EditionCollection.this) {
map.put(ce.getCode(), ce.getBoosterTemplate());
}
return map;
}
@Override
public String getItemKey(SealedProductTemplate item) {
return item.getEdition();
}
};
}
}

View File

@@ -0,0 +1,75 @@
package forge.card;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import forge.util.TextUtil;
import forge.util.storage.StorageReaderFile;
/**
* TODO: Write javadoc for this type.
*
*/
public class FatPackTemplate extends SealedProductTemplate {
private final int cntBoosters;
public int getCntBoosters() { return cntBoosters; }
private FatPackTemplate(String edition, int boosters, Iterable<Pair<String, Integer>> itrSlots)
{
super(edition, itrSlots);
cntBoosters = boosters;
}
public static final class Reader extends StorageReaderFile<FatPackTemplate> {
public Reader(String pathname) {
super(pathname, FatPackTemplate.FN_GET_NAME);
}
@Override
protected FatPackTemplate read(String line, int i) {
String[] headAndData = TextUtil.split(line, ':', 2);
final String edition = headAndData[0];
final String[] data = TextUtil.splitWithParenthesis(headAndData[1], ',');
int nBoosters = 6;
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
for(String slotDesc : data) {
String[] kv = TextUtil.split(slotDesc, ' ', 2);
if (kv[1].startsWith("Booster"))
nBoosters = Integer.parseInt(kv[0]);
else
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
}
return new FatPackTemplate(edition, nBoosters, slots);
}
}
@Override
public String toString() {
if (0 >= cntBoosters) {
return "no cards";
}
StringBuilder s = new StringBuilder();
for(Pair<String, Integer> p : slots) {
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
}
// trim the last comma and space
if( s.length() > 0 )
s.replace(s.length() - 2, s.length(), "");
if (0 < cntBoosters) {
if( s.length() > 0 )
s.append(" and ");
s.append(cntBoosters).append(" booster packs ");
}
return s.toString();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import forge.game.GameFormat;
import forge.util.storage.StorageReaderBase;
import forge.util.storage.StorageBase;
/**
* The Class FormatUtils.
*/
public final class FormatCollection extends StorageBase<GameFormat> {
/**
* TODO: Write javadoc for Constructor.
* @param io
*/
public FormatCollection(StorageReaderBase<GameFormat> reader) {
super("Format collections", reader);
}
/**
* Gets the standard.
*
* @return the standard
*/
public GameFormat getStandard() {
return this.map.get("Standard");
}
/**
* Gets the extended.
*
* @return the extended
*/
public GameFormat getExtended() {
return this.map.get("Extended");
}
/**
* Gets the modern.
*
* @return the modern
*/
public GameFormat getModern() {
return this.map.get("Modern");
}
/**
* Get a specified format.
* @return the requested format
*/
public GameFormat getFormat(String format) {
return this.map.get(format);
}
}
/**
* TODO: Write javadoc for this type.
*
*/

View File

@@ -0,0 +1,34 @@
package forge.card;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import com.google.common.base.Predicate;
import forge.item.PaperCard;
public interface ICardDatabase {
PaperCard tryGetCard(String cardName);
PaperCard tryGetCard(String cardName, boolean fromLastSet);
PaperCard tryGetCard(String cardName, String edition);
PaperCard tryGetCard(String cardName, String edition, int artIndex);
PaperCard tryGetCardPrintedByDate(String name0, boolean fromLatestSet, Date printedBefore);
PaperCard getCard(String cardName);
PaperCard getCard(String cardName, boolean fromLastSet);
PaperCard getCard(String cardName, String edition);
PaperCard getCard(String cardName, String edition, int artIndex);
PaperCard getCardPrintedByDate(String name0, boolean fromLatestSet, Date printedBefore);
PaperCard getFoiled(PaperCard cpi);
int getPrintCount(String cardName, String edition);
int getMaxPrintCount(String cardName);
Collection<PaperCard> getUniqueCards();
List<PaperCard> getAllCards();
List<PaperCard> getAllCards(Predicate<PaperCard> predicate);
Predicate<? super PaperCard> wasPrintedInSets(List<String> allowedSetCodes);
}

View File

@@ -0,0 +1,16 @@
package forge.card;
import java.util.List;
import com.google.common.base.Supplier;
import forge.item.PaperCard;
/**
* TODO: Write javadoc for this type.
*
*/
public interface IUnOpenedProduct extends Supplier<List<PaperCard>> {
public List<PaperCard> get();
}

View File

@@ -1,6 +1,8 @@
package forge.card;
import forge.Constant;
import java.util.List;
import com.google.common.collect.ImmutableList;
/**
* Holds byte values for each color magic has.
@@ -22,31 +24,31 @@ public class MagicColor {
public static byte fromName(String s) {
if( s == null ) return 0;
if (s.equalsIgnoreCase(Constant.Color.WHITE) || s.equalsIgnoreCase("w")) {
if (s.equalsIgnoreCase(Constant.WHITE) || s.equalsIgnoreCase("w")) {
return MagicColor.WHITE;
}
if (s.equalsIgnoreCase(Constant.Color.BLUE) || s.equalsIgnoreCase("u")) {
if (s.equalsIgnoreCase(Constant.BLUE) || s.equalsIgnoreCase("u")) {
return MagicColor.BLUE;
}
if (s.equalsIgnoreCase(Constant.Color.BLACK) || s.equalsIgnoreCase("b")) {
if (s.equalsIgnoreCase(Constant.BLACK) || s.equalsIgnoreCase("b")) {
return MagicColor.BLACK;
}
if (s.equalsIgnoreCase(Constant.Color.RED) || s.equalsIgnoreCase("r")) {
if (s.equalsIgnoreCase(Constant.RED) || s.equalsIgnoreCase("r")) {
return MagicColor.RED;
}
if (s.equalsIgnoreCase(Constant.Color.GREEN) || s.equalsIgnoreCase("g")) {
if (s.equalsIgnoreCase(Constant.GREEN) || s.equalsIgnoreCase("g")) {
return MagicColor.GREEN;
}
return 0; // colorless
}
public static String toShortString(String color) {
if (color.equalsIgnoreCase(Constant.Color.SNOW)) return "S"; // compatibility
if (color.equalsIgnoreCase(Constant.SNOW)) return "S"; // compatibility
return toShortString(fromName(color));
}
public static String toLongString(String color) {
if (color.equalsIgnoreCase("s")) return Constant.Color.SNOW; // compatibility
if (color.equalsIgnoreCase("s")) return Constant.SNOW; // compatibility
return toLongString(fromName(color));
}
@@ -63,12 +65,47 @@ public class MagicColor {
public static String toLongString(byte color) {
switch(color){
case GREEN: return Constant.Color.GREEN ;
case RED: return Constant.Color.RED;
case BLUE: return Constant.Color.BLUE;
case BLACK: return Constant.Color.BLACK;
case WHITE: return Constant.Color.WHITE;
default: return Constant.Color.COLORLESS;
case GREEN: return Constant.GREEN ;
case RED: return Constant.RED;
case BLUE: return Constant.BLUE;
case BLACK: return Constant.BLACK;
case WHITE: return Constant.WHITE;
default: return Constant.COLORLESS;
}
}
/**
* The Interface Color.
*/
public static class Constant {
/** The Black. */
public static final String BLACK = "black";
/** The Blue. */
public static final String BLUE = "blue";
/** The Green. */
public static final String GREEN = "green";
/** The Red. */
public static final String RED = "red";
/** The White. */
public static final String WHITE = "white";
/** The Colorless. */
public static final String COLORLESS = "colorless";
// color order "wubrg"
/** The only colors. */
public static final ImmutableList<String> ONLY_COLORS = ImmutableList.of(WHITE, BLUE, BLACK, RED, GREEN);
/** The Snow. */
public static final String SNOW = "snow";
/** The Basic lands. */
public static final List<String> BASIC_LANDS = ImmutableList.of("Plains", "Island", "Swamp", "Mountain", "Forest");
public static final List<String> SNOW_LANDS = ImmutableList.of("Snow-Covered Plains", "Snow-Covered Island", "Snow-Covered Swamp", "Snow-Covered Mountain", "Snow-Covered Forest");
}
}

View File

@@ -0,0 +1,131 @@
package forge.card;
import java.io.File;
import java.util.List;
import java.util.Map.Entry;
import java.util.ArrayList;
import java.util.Collection;
import com.google.common.base.Function;
import forge.util.ItemPool;
import forge.deck.CardPool;
import forge.item.PaperCard;
import forge.util.MyRandom;
import forge.util.storage.StorageReaderFileSections;
/**
* TODO: Write javadoc for this type.
*
*/
public class PrintSheet {
public static final Function<PrintSheet, String> FN_GET_KEY = new Function<PrintSheet, String>() {
@Override public final String apply(PrintSheet sheet) { return sheet.name; }
};
private final ItemPool<PaperCard> cardsWithWeights;
private final String name;
public PrintSheet(String name0) {
this(name0, null);
}
public PrintSheet(String name0, ItemPool<PaperCard> pool) {
name = name0;
cardsWithWeights = pool != null ? pool : new ItemPool<PaperCard>(PaperCard.class);
}
public void add(PaperCard card) {
add(card,1);
}
public void add(PaperCard card, int weight) {
cardsWithWeights.add(card, weight);
}
public void addAll(Iterable<PaperCard> cards) {
addAll(cards, 1);
}
public void addAll(Iterable<PaperCard> cards, int weight) {
for(PaperCard card : cards)
cardsWithWeights.add(card, weight);
}
/** Cuts cards out of a sheet - they won't be printed again.
* Please use mutable sheets for cubes only.*/
public void removeAll(Iterable<PaperCard> cards) {
for(PaperCard card : cards)
cardsWithWeights.remove(card);
}
private PaperCard fetchRoulette(int start, int roulette, Collection<PaperCard> toSkip) {
int sum = start;
boolean isSecondRun = start > 0;
for(Entry<PaperCard, Integer> cc : cardsWithWeights ) {
sum += cc.getValue().intValue();
if( sum > roulette ) {
if( toSkip != null && toSkip.contains(cc.getKey()))
continue;
return cc.getKey();
}
}
if( isSecondRun )
throw new IllegalStateException("Print sheet does not have enough unique cards");
return fetchRoulette(sum + 1, roulette, toSkip); // start over from beginning, in case last cards were to skip
}
public List<PaperCard> random(int number, boolean wantUnique) {
List<PaperCard> result = new ArrayList<PaperCard>();
int totalWeight = cardsWithWeights.countAll();
if( totalWeight == 0) {
System.err.println("No cards were found on sheet " + name);
return result;
}
// If they ask for 40 unique basic lands (to make a fatpack) out of 20 distinct possible, add the whole print run N times.
int uniqueCards = cardsWithWeights.countDistinct();
while ( number >= uniqueCards ) {
for(Entry<PaperCard, Integer> kv : cardsWithWeights) {
result.add(kv.getKey());
}
number -= uniqueCards;
}
List<PaperCard> uniques = wantUnique ? new ArrayList<PaperCard>() : null;
for(int iC = 0; iC < number; iC++) {
int index = MyRandom.getRandom().nextInt(totalWeight);
PaperCard toAdd = fetchRoulette(0, index, wantUnique ? uniques : null);
result.add(toAdd);
if( wantUnique )
uniques.add(toAdd);
}
return result;
}
public boolean isEmpty() {
return cardsWithWeights.isEmpty();
}
public Iterable<PaperCard> toFlatList() {
return cardsWithWeights.toFlatList();
}
public static class Reader extends StorageReaderFileSections<PrintSheet> {
public Reader(File file) {
super(file, PrintSheet.FN_GET_KEY);
}
@Override
protected PrintSheet read(String title, Iterable<String> body, int idx) {
return new PrintSheet(title, CardPool.fromCardList(body));
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.card;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.util.TextUtil;
import forge.util.storage.StorageReaderFile;
public class SealedProductTemplate {
@SuppressWarnings("unchecked")
public final static SealedProductTemplate genericBooster = new SealedProductTemplate(null, Lists.newArrayList(
Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3),
Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1)
));
protected final List<Pair<String, Integer>> slots;
protected final String name;
public final List<Pair<String, Integer>> getSlots() {
return slots;
}
public final String getEdition() {
return name;
}
public SealedProductTemplate(Iterable<Pair<String, Integer>> itrSlots)
{
this(null, itrSlots);
}
public SealedProductTemplate(String name0, Iterable<Pair<String, Integer>> itrSlots)
{
slots = Lists.newArrayList(itrSlots);
name = name0;
}
public SealedProductTemplate(String code, String boosterDesc) {
this(code, Reader.parseSlots(boosterDesc));
}
public int getNumberOfCardsExpected() {
int sum = 0;
for(Pair<String, Integer> p : slots) {
sum += p.getRight().intValue();
}
return sum;
}
public static final Function<? super SealedProductTemplate, String> FN_GET_NAME = new Function<SealedProductTemplate, String>() {
@Override
public String apply(SealedProductTemplate arg1) {
return arg1.name;
}
};
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("consisting of ");
for(Pair<String, Integer> p : slots) {
s.append(p.getRight()).append(" ").append(p.getLeft()).append(", ");
}
// trim the last comma and space
s.replace(s.length() - 2, s.length(), "");
// put an 'and' before the previous comma
int lastCommaIdx = s.lastIndexOf(",");
if (0 < lastCommaIdx) {
s.replace(lastCommaIdx+1, lastCommaIdx+1, " and");
}
return s.toString();
}
public final static class Reader extends StorageReaderFile<SealedProductTemplate> {
public Reader(File file) {
super(file, SealedProductTemplate.FN_GET_NAME);
}
public static List<Pair<String, Integer>> parseSlots(String data) {
final String[] dataz = TextUtil.splitWithParenthesis(data, ',');
List<Pair<String, Integer>> slots = new ArrayList<Pair<String,Integer>>();
for(String slotDesc : dataz) {
String[] kv = TextUtil.splitWithParenthesis(slotDesc, ' ', 2);
slots.add(ImmutablePair.of(kv[1], Integer.parseInt(kv[0])));
}
return slots;
}
@Override
protected SealedProductTemplate read(String line, int i) {
String[] headAndData = TextUtil.split(line, ':', 2);
return new SealedProductTemplate(headAndData[0], parseSlots(headAndData[1]));
}
}
}

View File

@@ -0,0 +1,171 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.deck;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.NoSuchElementException;
import org.apache.commons.lang3.StringUtils;
import forge.StaticData;
import forge.item.PaperCard;
import forge.util.ItemPool;
/**
* Deck section.
*
*/
public class CardPool extends ItemPool<PaperCard> {
/**
* Instantiates a new deck section.
*/
public CardPool() {
super(PaperCard.class);
}
/**
* Instantiates a new deck section.
*
* @param cards the cards
*/
public CardPool(final Iterable<Entry<PaperCard, Integer>> cards) {
this();
this.addAll(cards);
}
/**
* Adds the.
*
* @param cardName
* the card name
* @param setCode
* the set code
*/
public void add(final String cardName, final String setCode) {
this.add(cardName, setCode, 1);
}
/**
* Adds the.
*
* @param cardName the card name
* @param setCode the set code
* @param amount the amount
*/
public void add(final String cardName, final String setCode, final int amount) {
PaperCard cp = StaticData.instance().getCommonCards().tryGetCard(cardName, setCode);
if ( cp == null )
cp = StaticData.instance().getVariantCards().tryGetCard(cardName, setCode);
if ( cp != null)
this.add(cp, amount);
else
throw new RuntimeException(String.format("Card %s from %s is not supported by Forge, as it's neither a known common card nor one of casual variants' card.", cardName, setCode ));
}
/**
* Add all from a List of CardPrinted.
*
* @param list
* CardPrinteds to add
*/
public void add(final Iterable<PaperCard> list) {
for (PaperCard cp : list) {
this.add(cp);
}
}
/**
* TODO: Write javadoc for this method.
*
* @param cardName the card name
*/
public void add(final String cardName, int cnt) {
PaperCard cp = StaticData.instance().getCommonCards().tryGetCard(cardName);
if ( cp == null )
cp = StaticData.instance().getVariantCards().tryGetCard(cardName);
if ( cp != null)
this.add(cp, cnt);
else
throw new NoSuchElementException(String.format("Card %s is not supported by Forge, as it's neither a known common card nor one of casual variants' card.", cardName));
}
/**
* returns n-th card from this DeckSection. LINEAR time. No fixed order between changes
* @param i
* @return
*/
public PaperCard get(int n) {
for(Entry<PaperCard, Integer> e : this)
{
n -= e.getValue();
if ( n <= 0 ) return e.getKey();
}
return null;
}
@Override
public String toString() {
if (this.isEmpty()) return "[]";
boolean isFirst = true;
StringBuilder sb = new StringBuilder();
sb.append('[');
for (Entry<PaperCard, Integer> e : this) {
if ( isFirst ) isFirst = false;
else sb.append(", ");
sb.append(e.getValue()).append(" x ").append(e.getKey().getName());
}
return sb.append(']').toString();
}
public static CardPool fromCardList(final Iterable<String> lines) {
CardPool pool = new CardPool();
final Pattern p = Pattern.compile("((\\d+)\\s+)?(.*?)");
if (lines == null) {
return pool;
}
final Iterator<String> lineIterator = lines.iterator();
while (lineIterator.hasNext()) {
final String line = lineIterator.next();
if (line.startsWith(";") || line.startsWith("#")) { continue; } // that is a comment or not-yet-supported card
final Matcher m = p.matcher(line.trim());
m.matches();
final String sCnt = m.group(2);
final String cardName = m.group(3);
if (StringUtils.isBlank(cardName)) {
continue;
}
final int count = sCnt == null ? 1 : Integer.parseInt(sCnt);
pool.add(cardName, count);
}
return pool;
}
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.deck;

View File

@@ -0,0 +1,215 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.game;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.item.PaperCard;
import forge.item.IPaperCard;
import forge.util.FileSection;
import forge.util.storage.StorageReaderFileSections;
/**
* TODO: Write javadoc for this type.
*
*/
public class GameFormat implements Comparable<GameFormat> {
private final String name;
// contains allowed sets, when empty allows all sets
protected final List<String> allowedSetCodes;
protected final List<String> bannedCardNames;
protected final transient List<String> allowedSetCodes_ro;
protected final transient List<String> bannedCardNames_ro;
protected final transient Predicate<PaperCard> filterRules;
protected final transient Predicate<PaperCard> filterPrinted;
private final int index;
/**
* Instantiates a new game format.
*
* @param fName
* the f name
* @param sets
* the sets
* @param bannedCards
* the banned cards
*/
public GameFormat(final String fName, final Iterable<String> sets, final List<String> bannedCards) {
this(fName, sets, bannedCards, 0);
}
public GameFormat(final String fName, final Iterable<String> sets, final List<String> bannedCards, int compareIdx) {
this.index = compareIdx;
this.name = fName;
this.allowedSetCodes = sets == null ? new ArrayList<String>() : Lists.newArrayList(sets);
this.bannedCardNames = bannedCards == null ? new ArrayList<String>() : Lists.newArrayList(bannedCards);
this.allowedSetCodes_ro = Collections.unmodifiableList(allowedSetCodes);
this.bannedCardNames_ro = Collections.unmodifiableList(bannedCardNames);
this.filterRules = this.buildFilterRules();
this.filterPrinted = this.buildFilterPrinted();
}
private Predicate<PaperCard> buildFilterPrinted() {
final Predicate<PaperCard> banNames = Predicates.not(IPaperCard.Predicates.names(this.bannedCardNames));
if (this.allowedSetCodes == null || this.allowedSetCodes.isEmpty()) {
return banNames;
}
return Predicates.and(banNames, IPaperCard.Predicates.printedInSets(this.allowedSetCodes, true));
}
private Predicate<PaperCard> buildFilterRules() {
final Predicate<PaperCard> banNames = Predicates.not(IPaperCard.Predicates.names(this.bannedCardNames));
if (this.allowedSetCodes == null || this.allowedSetCodes.isEmpty()) {
return banNames;
}
return Predicates.and(banNames, StaticData.instance().getCommonCards().wasPrintedInSets(this.allowedSetCodes));
}
/**
* Gets the name.
*
* @return the name
*/
public String getName() {
return this.name;
}
/**
* Gets the set list (for GameFormatQuest).
*
* @return list of allowed set codes
*/
public List<String> getAllowedSetCodes() {
return this.allowedSetCodes_ro;
}
/**
* Gets the banned cards (for GameFormatQuest).
*
* @return list of banned card names
*/
public List<String> getBannedCardNames() {
return this.bannedCardNames_ro;
}
/**
* Gets the filter rules.
*
* @return the filter rules
*/
public Predicate<PaperCard> getFilterRules() {
return this.filterRules;
}
/**
* Gets the filter printed.
*
* @return the filter printed
*/
public Predicate<PaperCard> getFilterPrinted() {
return this.filterPrinted;
}
/**
* Checks if is sets the legal.
*
* @param setCode
* the set code
* @return true, if is sets the legal
*/
public boolean isSetLegal(final String setCode) {
return this.allowedSetCodes.isEmpty() || this.allowedSetCodes.contains(setCode);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.name + " (format)";
}
public static final Function<GameFormat, String> FN_GET_NAME = new Function<GameFormat, String>() {
@Override
public String apply(GameFormat arg1) {
return arg1.getName();
}
};
/* (non-Javadoc)
* just used for ordering -- comparing the name is sufficient
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(GameFormat other) {
if (null == other) {
return 1;
}
return index - other.index;
}
public int getIndex() {
return index;
}
/**
* Instantiates a new format utils.
*/
public static class Reader extends StorageReaderFileSections<GameFormat> {
public Reader(File file0) {
super(file0, GameFormat.FN_GET_NAME);
}
@Override
protected GameFormat read(String title, Iterable<String> body, int idx) {
List<String> sets = null; // default: all sets allowed
List<String> bannedCards = null; // default: nothing banned
FileSection section = FileSection.parse(body, ":");
String strSets = section.get("sets");
if ( null != strSets ) {
sets = Arrays.asList(strSets.split(", "));
}
String strCars = section.get("banned");
if ( strCars != null ) {
bannedCards = Arrays.asList(strCars.split("; "));
}
return new GameFormat(title, sets, bannedCards, 1 + idx);
}
}
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.game;

View File

@@ -0,0 +1,163 @@
package forge.item;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
//import forge.Card;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.util.PredicateString;
public interface IPaperCard extends InventoryItem {
/**
* Number of filters based on CardPrinted values.
*/
public abstract static class Predicates {
public static Predicate<PaperCard> rarity(final boolean isEqual, final CardRarity value) {
return new PredicateRarity(value, isEqual);
}
public static Predicate<PaperCard> printedInSets(final String[] sets) {
return printedInSets(Lists.newArrayList(sets), true);
}
public static Predicate<PaperCard> printedInSets(final List<String> value, final boolean shouldContain) {
if ((value == null) || value.isEmpty()) {
return com.google.common.base.Predicates.alwaysTrue();
}
return new PredicateSets(value, shouldContain);
}
public static Predicate<PaperCard> printedInSet(final String value) {
if (StringUtils.isEmpty(value)) {
return com.google.common.base.Predicates.alwaysTrue();
}
return new PredicateSets(Lists.newArrayList(value), true);
}
public static Predicate<PaperCard> name(final String what) {
return new PredicateName(PredicateString.StringOp.EQUALS_IC, what);
}
public static Predicate<PaperCard> name(final PredicateString.StringOp op, final String what) {
return new PredicateName(op, what);
}
public static Predicate<PaperCard> names(final List<String> what) {
return new PredicateNames(what);
}
private static class PredicateRarity implements Predicate<PaperCard> {
private final CardRarity operand;
private final boolean shouldBeEqual;
@Override
public boolean apply(final PaperCard card) {
return (card.getRarity() == this.operand) == this.shouldBeEqual;
}
public PredicateRarity(final CardRarity type, final boolean wantEqual) {
this.operand = type;
this.shouldBeEqual = wantEqual;
}
}
private static class PredicateSets implements Predicate<PaperCard> {
private final Set<String> sets;
private final boolean mustContain;
@Override
public boolean apply(final PaperCard card) {
return this.sets.contains(card.getEdition()) == this.mustContain;
}
public PredicateSets(final List<String> wantSets, final boolean shouldContain) {
this.sets = new HashSet<String>(wantSets);
this.mustContain = shouldContain;
}
}
private static class PredicateName extends PredicateString<PaperCard> {
private final String operand;
@Override
public boolean apply(final PaperCard card) {
return this.op(card.getName(), this.operand);
}
public PredicateName(final PredicateString.StringOp operator, final String operand) {
super(operator);
this.operand = operand;
}
}
private static class PredicateNames extends PredicateString<PaperCard> {
private final List<String> operand;
@Override
public boolean apply(final PaperCard card) {
final String cardName = card.getName();
for (final String element : this.operand) {
if (this.op(cardName, element)) {
return true;
}
}
return false;
}
public PredicateNames(final List<String> operand) {
super(StringOp.EQUALS);
this.operand = operand;
}
}
/**
* Pre-built predicates are stored here to allow their re-usage and
* easier access from code.
*/
public abstract static class Presets {
// Think twice before using these, since rarity is a prop of printed
// card.
/** The Constant isCommon. */
public static final Predicate<PaperCard> IS_COMMON = Predicates.rarity(true, CardRarity.Common);
/** The Constant isUncommon. */
public static final Predicate<PaperCard> IS_UNCOMMON = Predicates.rarity(true, CardRarity.Uncommon);
/** The Constant isRare. */
public static final Predicate<PaperCard> IS_RARE = Predicates.rarity(true, CardRarity.Rare);
/** The Constant isMythicRare. */
public static final Predicate<PaperCard> IS_MYTHIC_RARE = Predicates.rarity(true, CardRarity.MythicRare);
/** The Constant isRareOrMythic. */
public static final Predicate<PaperCard> IS_RARE_OR_MYTHIC = com.google.common.base.Predicates.or(Presets.IS_RARE,
Presets.IS_MYTHIC_RARE);
/** The Constant isSpecial. */
public static final Predicate<PaperCard> IS_SPECIAL = Predicates.rarity(true, CardRarity.Special);
/** The Constant exceptLands. */
public static final Predicate<PaperCard> IS_BASIC_LAND = Predicates.rarity(true, CardRarity.BasicLand);
}
}
public abstract String getName();
public abstract String getEdition();
public abstract int getArtIndex();
public abstract boolean isFoil();
public abstract boolean isToken();
public abstract CardRules getRules();
public abstract CardRarity getRarity();
public abstract String getItemType();
}

View File

@@ -0,0 +1,34 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
import forge.util.IHasName;
/**
* Interface to define a player's inventory may hold. Should include
* CardPrinted, Booster, Pets, Plants... etc
*/
public interface InventoryItem extends IHasName
{
/**
* Return type as a string.
*
* @return the type
*/
String getItemType();
}

View File

@@ -0,0 +1,31 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
/**
* Interface to define a player's inventory may hold. Should include
* CardPrinted, Booster, Pets, Plants... etc
*/
public interface InventoryItemFromSet extends InventoryItem {
/**
* An item belonging to a set should return its set as well.
*
* @return the sets the
*/
String getEdition();
}

View File

@@ -0,0 +1,196 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.item;
import com.google.common.base.Function;
import forge.card.CardRarity;
import forge.card.CardRules;
/**
* A viciously lightweight version of a card, for instances
* where a full set of meta and rules is not needed.
* <br><br>
* The full set of rules is in the CardRules class.
*
* @author Forge
* @version $Id: CardReference.java 9708 2011-08-09 19:34:12Z jendave $
*/
public final class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet, IPaperCard {
// Reference to rules
private final transient CardRules card;
// These fields are kinda PK for PrintedCard
public final String name;
public final String edition;
public final int artIndex;
public final boolean foil;
// Calculated fields are below:
private final transient CardRarity rarity; // rarity is given in ctor when set is assigned
@Override
public String getName() {
return this.name;
}
@Override
public String getEdition() {
return this.edition;
}
@Override
public int getArtIndex() {
return this.artIndex;
}
@Override
public boolean isFoil() {
return this.foil;
}
@Override
public boolean isToken() {
return false;
}
@Override
public CardRules getRules() {
return this.card;
}
@Override
public CardRarity getRarity() {
return this.rarity;
}
// @Override
// public String getImageKey() {
// return getImageLocator(getImageName(), getArtIndex(), true, false);
// }
@Override
public String getItemType() {
return "Card";
}
/**
* Lambda to get rules for selects from list of printed cards.
*/
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
@Override
public CardRules apply(final PaperCard from) {
return from.card;
}
};
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
@Override
public String apply(final PaperCard from) {
return from.getName();
}
};
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index) {
this(c, edition0, rare, index, false);
}
public PaperCard(final CardRules c, final String edition0, final CardRarity rare, final int index, final boolean foil) {
if ( edition0 == null || c == null || rare == null )
throw new IllegalArgumentException("Cannot create card without rules, edition or rarity");
this.card = c;
this.name = c.getName();
this.edition = edition0;
this.artIndex = index;
this.foil = foil;
this.rarity = rare;
}
// Want this class to be a key for HashTable
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (this.getClass() != obj.getClass()) {
return false;
}
final PaperCard other = (PaperCard) obj;
if (!this.name.equals(other.name)) {
return false;
}
if (!this.edition.equals(other.edition)) {
return false;
}
if ((other.foil != this.foil) || (other.artIndex != this.artIndex)) {
return false;
}
return true;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int code = (this.name.hashCode() * 11) + (this.edition.hashCode() * 59) + (this.artIndex * 2);
if (this.foil) {
return code + 1;
}
return code;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.name;
// cannot still decide, if this "name|set" format is needed anymore
// return String.format("%s|%s", name, cardSet);
}
/*
* (non-Javadoc)
*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
@Override
public int compareTo(final IPaperCard o) {
final int nameCmp = this.getName().compareToIgnoreCase(o.getName());
if (0 != nameCmp) {
return nameCmp;
}
// TODO compare sets properly
return this.edition.compareTo(o.getEdition());
}
}

View File

@@ -0,0 +1,64 @@
package forge.item;
import java.util.Locale;
import forge.card.CardEdition;
import forge.card.CardRarity;
import forge.card.CardRules;
public class PaperToken implements InventoryItemFromSet, IPaperCard {
private String name;
private CardEdition edition;
private String imageFileName;
private CardRules card;
// takes a string of the form "<colors> <power> <toughness> <name>" such as: "B 0 0 Germ"
public static String makeTokenFileName(String in) {
StringBuffer out = new StringBuffer();
char c;
for (int i = 0; i < in.length(); i++) {
c = in.charAt(i);
if ((c == ' ') || (c == '-') || (c == '_')) {
out.append('_');
} else if (Character.isLetterOrDigit(c)) {
out.append(c);
}
}
return out.toString().toLowerCase(Locale.ENGLISH);
}
public static String makeTokenFileName(String colors, int power, int toughness, String name) {
return makeTokenFileName(colors, String.valueOf(power), String.valueOf(toughness), name);
}
public static String makeTokenFileName(String colors, String power, String toughness, String name) {
StringBuilder fileName = new StringBuilder();
fileName.append(colors).append('_').append(power).append('_').append(toughness).append('_').append(name);
return makeTokenFileName(fileName.toString());
}
public PaperToken(final CardRules c, CardEdition edition0, final String imageFileName) {
this.card = c;
this.name = c.getName();
this.edition = edition0;
this.imageFileName = String.format("%s%s", null == edition || CardEdition.UNKNOWN == edition ? "" : edition.getCode(), imageFileName);
}
@Override public String getName() { return name; }
@Override public String getEdition() { return edition.getCode(); }
@Override public int getArtIndex() { return 0; } // This might change however
@Override public boolean isFoil() { return false; }
@Override public CardRules getRules() { return card; }
@Override public CardRarity getRarity() { return CardRarity.Common; } // They don't have rarity though!
// Unfortunately this is a property of token, cannot move it outside of class
public String getImageFilename() { return imageFileName; }
@Override public String getItemType() { return "Token"; }
@Override public boolean isToken() { return true; }
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.item;

View File

@@ -0,0 +1,174 @@
package forge.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import com.google.common.base.Function;
/**
* TODO: Write javadoc for this type.
*
*/
public class Aggregates {
// Returns the value matching predicate conditions with the maximum value of whatever valueAccessor returns.
public static final <T> Integer max(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
if (source == null) { return null; }
int max = Integer.MIN_VALUE;
for (final T c : source) {
int value = valueAccessor.apply(c);
if (value > max) {
max = value;
}
}
return max;
}
public static final <T> Integer min(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
if (source == null) { return null; }
int max = Integer.MAX_VALUE;
for (final T c : source) {
int value = valueAccessor.apply(c);
if (value < max) {
max = value;
}
}
return max;
}
public static final <T> T itemWithMax(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
if (source == null) { return null; }
int max = Integer.MIN_VALUE;
T result = null;
for (final T c : source) {
int value = valueAccessor.apply(c);
if (value > max) {
max = value;
result = c;
}
}
return result;
}
public static final <T> int sum(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
int result = 0;
if (source != null) {
for (final T c : source) {
result += valueAccessor.apply(c);
}
}
return result;
}
// Random - algorithm adapted from Braid's GeneratorFunctions
/**
* Random.
*
* @param source
* the source
* @return the t
*/
public static final <T> T random(final Iterable<T> source) {
if( null == source )
return null;
Random rnd = MyRandom.getRandom();
if ( source instanceof List<?> )
{
List<T> src = (List<T>)source;
int len = src.size();
switch(len) {
case 0: return null;
case 1: return src.get(0);
default: return src.get(rnd.nextInt(len));
}
}
int n = 0;
T candidate = null;
for (final T item : source) {
if ((rnd.nextDouble() * ++n) < 1) {
candidate = item;
}
}
return candidate;
}
// Get several random values
// should improve to make 1 pass over source and track N candidates at once
public static final <T> List<T> random(final Iterable<T> source, final int count) {
final List<T> result = new ArrayList<T>();
for (int i = 0; i < count; ++i) {
final T toAdd = Aggregates.random(source);
if (toAdd == null) {
break;
}
result.add(toAdd);
}
return result;
}
public static final <K, U> Iterable<U> uniqueByLast(final Iterable<U> source, final Function<U, K> fnUniqueKey) { // this might be exotic
final Map<K, U> uniques = new Hashtable<K, U>();
for (final U c : source) {
uniques.put(fnUniqueKey.apply(c), c);
}
return uniques.values();
}
public static <T> T itemWithMin(final Iterable<T> source, final Function<T, Integer> valueAccessor) {
if (source == null) { return null; }
int max = Integer.MAX_VALUE;
T result = null;
for (final T c : source) {
int value = valueAccessor.apply(c);
if (value < max) {
max = value;
result = c;
}
}
return result;
}
public static <TItem, TField> TItem firstFieldEquals(List<TItem> source, Function<TItem, TField> valueAccessor, TField valueEquals) {
if (source == null) { return null; }
if (valueEquals == null) {
for (final TItem c : source) {
if (null == valueAccessor.apply(c)) {
return c;
}
}
} else {
for (final TItem c : source) {
if (valueEquals.equals(valueAccessor.apply(c))) {
return c;
}
}
}
return null;
}
public static <T, U> Iterable<Entry<U, Integer>> groupSumBy(Iterable<Entry<T, Integer>> source, Function<T, U> fnGetField) {
Map<U, Integer> result = new HashMap<U, Integer>();
for(Entry<T, Integer> kv : source) {
U k = fnGetField.apply(kv.getKey());
Integer v = kv.getValue();
Integer sum = result.get(k);
int n = v == null ? 0 : v.intValue();
int s = sum == null ? 0 : sum.intValue();
result.put(k, Integer.valueOf(s + n));
}
return result.entrySet();
}
}

View File

@@ -0,0 +1,42 @@
package forge.util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.TreeSet;
import com.google.common.base.Supplier;
/**
* TODO: Write javadoc for this type.
*
*/
public abstract class CollectionSuppliers {
public static <T> Supplier<ArrayList<T>> arrayLists() {
return new Supplier<ArrayList<T>>() {
@Override
public ArrayList<T> get() {
return new ArrayList<T>();
}
};
}
public static <T> Supplier<HashSet<T>> hashSets() {
return new Supplier<HashSet<T>>() {
@Override
public HashSet<T> get() {
return new HashSet<T>();
}
};
}
public static <T extends Comparable<T>> Supplier<TreeSet<T>> treeSets() {
return new Supplier<TreeSet<T>>() {
@Override
public TreeSet<T> get() {
return new TreeSet<T>();
}
};
}
}

View File

@@ -0,0 +1,34 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 MaxMtg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
/**
* Possible operators for comparables.
*
* @author Max
*
*/
public enum ComparableOp {
EQUALS,
NOT_EQUALS,
GREATER_THAN,
LESS_THAN,
GT_OR_EQUAL,
LT_OR_EQUAL
}

View File

@@ -0,0 +1,216 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
/**
* TODO: Write javadoc for this type.
*
*/
public class FileSection {
/** The lines. */
private final Map<String, String> lines;
/**
* Gets the lines.
*
* @return the lines
*/
protected final Map<String, String> getLines() {
return this.lines;
}
/**
* Instantiates a new file section.
*/
protected FileSection() {
this(new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER));
}
protected FileSection(Map<String, String> lines0) {
lines = lines0;
}
/**
* Parses the.
*
* @param line the line
* @param kvSeparator the kv separator
* @param pairSeparator the pair separator
* @return the file section
*/
public static FileSection parse(final String line, final String kvSeparator, final String pairSeparator) {
Map<String, String> map = parseToMap(line, kvSeparator, pairSeparator);
return new FileSection(map);
}
public static Map<String, String> parseToMap(final String line, final String kvSeparator, final String pairSeparator) {
final String[] pairs = line.split(Pattern.quote(pairSeparator));
final Pattern splitter = Pattern.compile(Pattern.quote(kvSeparator));
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
for (final String dd : pairs) {
final String[] v = splitter.split(dd, 2);
result.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
}
return result;
}
/**
* Parses the.
*
* @param lines the lines
* @param kvSeparator the kv separator
* @return the file section
*/
public static FileSection parse(final Iterable<String> lines, final String kvSeparator) {
final FileSection result = new FileSection();
final Pattern splitter = Pattern.compile(Pattern.quote(kvSeparator));
for (final String dd : lines) {
final String[] v = splitter.split(dd, 2);
result.lines.put(v[0].trim(), v.length > 1 ? v[1].trim() : "");
}
return result;
}
/**
* Gets the.
*
* @param fieldName the field name
* @return the string
*/
public String get(final String fieldName) {
return this.lines.get(fieldName);
}
public String get(final String fieldName, final String defaultValue) {
return lines.containsKey(fieldName) ? this.lines.get(fieldName) : defaultValue;
}
public boolean contains(String keyName) {
return lines.containsKey(keyName);
}
/**
* Gets the int.
*
* @param fieldName the field name
* @return the int
*/
public int getInt(final String fieldName) {
return this.getInt(fieldName, 0);
}
/**
* Gets the int.
*
* @param fieldName the field name
* @param defaultValue the default value
* @return the int
*/
public int getInt(final String fieldName, final int defaultValue) {
try {
return Integer.parseInt(this.get(fieldName));
} catch (final NumberFormatException ex) {
return defaultValue;
}
}
/**
* Gets the boolean.
*
* @param fieldName the field name
* @return the boolean
*/
public boolean getBoolean(final String fieldName) {
return this.getBoolean(fieldName, false);
}
/**
* Gets the boolean.
*
* @param fieldName the field name
* @param defaultValue the default value
* @return the boolean
*/
public boolean getBoolean(final String fieldName, final boolean defaultValue) {
final String s = this.get(fieldName);
if (s == null) {
return defaultValue;
}
return "true".equalsIgnoreCase(s);
}
/**
* Parses the sections.
*
* @param source
* the source
* @return the map
*/
@SuppressWarnings("unchecked")
public static Map<String, List<String>> parseSections(final List<String> source) {
final Map<String, List<String>> result = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
String currentSection = "";
List<String> currentList = null;
for (final String s : source) {
final String st = s.trim();
if (st.length() == 0) {
continue;
}
if (st.startsWith("[") && st.endsWith("]")) {
if ((currentList != null) && (currentList.size() > 0)) {
final Object oldVal = result.get(currentSection);
if ((oldVal != null) && (oldVal instanceof List<?>)) {
currentList.addAll((List<String>) oldVal);
}
result.put(currentSection, currentList);
}
final String newSection = st.substring(1, st.length() - 1);
currentSection = newSection;
currentList = null;
} else {
if (currentList == null) {
currentList = new ArrayList<String>();
}
currentList.add(st);
}
}
// save final block
if ((currentList != null) && (currentList.size() > 0)) {
final Object oldVal = result.get(currentSection);
if ((oldVal != null) && (oldVal instanceof List<?>)) {
currentList.addAll((List<String>) oldVal);
}
result.put(currentSection, currentList);
}
return result;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
/**
* TODO: Write javadoc for this type.
*
*/
public class FileSectionManual extends FileSection {
/**
* Put.
*
* @param key the key
* @param value the value
*/
public void put(final String key, final String value) {
this.getLines().put(key, value);
}
}

View File

@@ -0,0 +1,210 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
/**
* <p>
* FileUtil class.
* </p>
*
* @author Forge
* @version $Id$
*/
public final class FileUtil {
private FileUtil() {
throw new AssertionError();
}
/**
* Takes two paths and combines them into a valid path string
* for the current OS.
* <p>
* Similar to the Path.Combine() function in .Net.
*/
public static String pathCombine (String path1, String path2) {
File file1 = new File(path1);
File file2 = new File(file1, path2);
return file2.getPath();
}
/**
* <p>
* doesFileExist.
* </p>
*
* @param filename
* a {@link java.lang.String} object.
* @return a boolean.
*/
public static boolean doesFileExist(final String filename) {
final File f = new File(filename);
return f.exists();
}
/**
* <p>
* writeFile.
* </p>
*
* @param filename
* a {@link java.lang.String} object.
* @param data
* a {@link java.util.List} object.
*/
public static void writeFile(final String filename, final List<String> data) {
FileUtil.writeFile(new File(filename), data);
}
// writes each element of ArrayList on a separate line
// this is used to write a file of Strings
// this will create a new file if needed
// if filename already exists, it is deleted
/**
* <p>
* writeFile.
* </p>
*
* @param file
* a {@link java.io.File} object.
* @param data
* a {@link java.util.List} object.
*/
public static void writeFile(File file, Collection<?> data) {
try {
PrintWriter p = new PrintWriter(file);
for (Object o : data) {
p.println(o);
}
p.close();
} catch (final Exception ex) {
throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + file + " : " + ex);
}
} // writeAllDecks()
public static String readFileToString(String filename) {
StringBuilder s = new StringBuilder();
for (String line : readFile(filename)) {
s.append(line).append('\n');
}
return s.toString();
}
public static List<String> readFile(final String filename) {
return FileUtil.readFile(new File(filename));
}
// reads line by line and adds each line to the ArrayList
// this will return blank lines as well
// if filename not found, returns an empty ArrayList
/**
* <p>
* readFile.
* </p>
*
* @param file
* a {@link java.io.File} object.
* @return a {@link java.util.ArrayList} object.
*/
public static List<String> readFile(final File file) {
try {
if ((file == null) || !file.exists()) {
return new ArrayList<String>();
}
return FileUtil.readAllLines(new FileReader(file), false);
} catch (final Exception ex) {
throw new RuntimeException("FileUtil : readFile() error, " + ex);
}
} // readFile()
/**
* Read all lines.
*
* @param reader the reader
* @return the list
*/
public static List<String> readAllLines(final Reader reader) {
return FileUtil.readAllLines(reader, false);
}
/**
* Reads all lines from given reader to a list of strings.
*
* @param reader is a reader (e.g. FileReader, InputStreamReader)
* @param mayTrim defines whether to trim lines.
* @return list of strings
*/
public static List<String> readAllLines(final Reader reader, final boolean mayTrim) {
final ArrayList<String> list = new ArrayList<String>();
try {
final BufferedReader in = new BufferedReader(reader);
String line;
while ((line = in.readLine()) != null) {
if (mayTrim) {
line = line.trim();
}
list.add(line);
}
in.close();
} catch (final IOException ex) {
throw new RuntimeException("FileUtil : readAllLines() error, " + ex);
}
return list;
}
// returns a list of <name, url> pairs. if the name is not in the file, it is synthesized from the url
public static List<Pair<String, String>> readNameUrlFile(String nameUrlFile) {
Pattern lineSplitter = Pattern.compile(Pattern.quote(" "));
Pattern replacer = Pattern.compile(Pattern.quote("%20"));
List<Pair<String, String>> list = new ArrayList<Pair<String, String>>();
for (String line : readFile(nameUrlFile)) {
if (StringUtils.isBlank(line) || line.startsWith("#")) {
continue;
}
String[] parts = lineSplitter.split(line, 2);
if (2 == parts.length) {
list.add(Pair.of(replacer.matcher(parts[0]).replaceAll(" "), parts[1]));
} else {
// figure out the filename from the URL
Pattern pathSplitter = Pattern.compile(Pattern.quote("/"));
String[] pathParts = pathSplitter.split(parts[0]);
String last = pathParts[pathParts.length - 1];
list.add(Pair.of(replacer.matcher(last).replaceAll(" "), parts[0]));
}
}
return list;
}
}

View File

@@ -0,0 +1,9 @@
package forge.util;
/**
* TODO: Write javadoc for this type.
*
*/
public interface IHasName {
String getName();
}

View File

@@ -0,0 +1,49 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.io.File;
import java.util.Map;
/**
* The Interface IItemReader.
*
* @param <T> the generic type
*/
public interface IItemReader<T> {
/**
* Read all.
*
* @return the map
*/
Map<String, T> readAll();
// T read(File file);
/**
* Gets the item key.
*
* @param item the item
* @return the item key
*/
String getItemKey(T item);
Iterable<File> getSubFolders();
IItemReader<T> getReaderForFolder(File subfolder);
}

View File

@@ -0,0 +1,45 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.io.File;
/**
* TODO: Write javadoc for this type.
*
* @param <T> the generic type
*/
public interface IItemSerializer<T> extends IItemReader<T> {
/**
* Save.
*
* @param unit the unit
*/
void save(T unit);
/**
* Erase.
*
* @param unit the unit
*/
void erase(T unit);
File getDirectory();
}

View File

@@ -0,0 +1,229 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.util.Collections;
import java.util.Map.Entry;
import forge.item.InventoryItem;
/**
* <p>
* ItemPool class.
* </p>
* Represents a list of items with amount of each
*
* @param <T>
* an Object
*/
public class ItemPool<T extends InventoryItem> extends ItemPoolView<T> {
// Constructors here
/**
*
* ItemPool Constructor.
*
* @param cls
* a T
*/
public ItemPool(final Class<T> cls) {
super(cls);
}
@SuppressWarnings("unchecked")
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final ItemPoolView<Tin> from, final Class<Tout> clsHint) {
final ItemPool<Tout> result = new ItemPool<Tout>(clsHint);
if (from != null) {
for (final Entry<Tin, Integer> e : from) {
final Tin srcKey = e.getKey();
if (clsHint.isInstance(srcKey)) {
result.put((Tout) srcKey, e.getValue());
}
}
}
return result;
}
@SuppressWarnings("unchecked")
public static <Tin extends InventoryItem, Tout extends InventoryItem> ItemPool<Tout> createFrom(final Iterable<Tin> from, final Class<Tout> clsHint) {
final ItemPool<Tout> result = new ItemPool<Tout>(clsHint);
if (from != null) {
for (final Tin srcKey : from) {
if (clsHint.isInstance(srcKey)) {
result.put((Tout) srcKey, Integer.valueOf(1));
}
}
}
return result;
}
// get
/**
*
* Get item view.
*
* @return a ItemPoolView
*/
public ItemPoolView<T> getView() {
return new ItemPoolView<T>(Collections.unmodifiableMap(this.getItems()), this.getMyClass());
}
// Items manipulation
/**
*
* Add a single item.
*
* @param item
* a T
*/
public void add(final T item) {
this.add(item, 1);
}
/**
*
* Add multiple items.
*
* @param item
* a T
* @param amount
* a int
*/
public void add(final T item, final int amount) {
if (amount <= 0) {
return;
}
this.getItems().put(item, Integer.valueOf(this.count(item) + amount));
this.isListInSync = false;
}
private void put(final T item, final int amount) {
this.getItems().put(item, amount);
this.isListInSync = false;
}
/**
* addAllFlat.
*
* @param <U>
* a InventoryItem
* @param items
* a Iterable<U>
*/
@SuppressWarnings("unchecked")
public <U extends InventoryItem> void addAllFlat(final Iterable<U> items) {
for (final U cr : items) {
if (this.getMyClass().isInstance(cr)) {
this.add((T) cr);
}
}
this.isListInSync = false;
}
/**
* addAll.
*
* @param <U>
* an InventoryItem
* @param map
* a Iterable<Entry<U, Integer>>
*/
@SuppressWarnings("unchecked")
public <U extends InventoryItem> void addAll(final Iterable<Entry<U, Integer>> map) {
Class<T> myClass = this.getMyClass();
for (final Entry<U, Integer> e : map) {
if (myClass.isInstance(e.getKey())) {
this.add((T) e.getKey(), e.getValue());
}
}
this.isListInSync = false;
}
/**
*
* Remove.
*
* @param item
* a T
*/
public boolean remove(final T item) {
return this.remove(item, 1);
}
/**
*
* Remove.
*
* @param item
* a T
* @param amount
* a int
*/
public boolean remove(final T item, final int amount) {
final int count = this.count(item);
if ((count == 0) || (amount <= 0)) {
return false;
}
if (count <= amount) {
this.getItems().remove(item);
} else {
this.getItems().put(item, count - amount);
}
this.isListInSync = false;
return true;
}
/**
*
* RemoveAll.
*
* @param map
* a T
*/
public void removeAll(final Iterable<Entry<T, Integer>> map) {
for (final Entry<T, Integer> e : map) {
this.remove(e.getKey(), e.getValue());
}
// need not set out-of-sync: either remove did set, or nothing was removed
}
/**
*
* TODO: Write javadoc for this method.
* @param flat Iterable<T>
*/
public void removeAllFlat(final Iterable<T> flat) {
for (final T e : flat) {
this.remove(e);
}
// need not set out-of-sync: either remove did set, or nothing was removed
}
/**
*
* Clear.
*/
public void clear() {
this.getItems().clear();
this.isListInSync = false;
}
}

View File

@@ -0,0 +1,87 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.util.Comparator;
import java.util.Map.Entry;
import com.google.common.base.Function;
import forge.item.PaperCard;
/**
* <p>
* TableSorter class.
* </p>
*
* @param <T>
* the generic type
* @author Forge
* @version $Id: TableSorter.java 21966 2013-06-05 06:58:32Z Max mtg $
*/
@SuppressWarnings("unchecked")
// Comparable needs <type>
public class ItemPoolSorter<T> implements Comparator<Entry<T, Integer>> {
private final boolean ascending;
private final Function<Entry<T, Integer>, Comparable<?>> field;
/**
* <p>
* Constructor for TableSorter.
* </p>
*
* @param field
* the field
* @param inAscending
* a boolean.
*/
public ItemPoolSorter(final Function<Entry<T, Integer>, Comparable<?>> field, final boolean inAscending) {
this.field = field;
this.ascending = inAscending;
}
/** The Constant byNameThenSet. */
public static final ItemPoolSorter<PaperCard> BY_NAME_THEN_SET = new ItemPoolSorter<PaperCard>(
new Function<Entry<PaperCard, Integer>, Comparable<?>>() {
@Override
public Comparable<?> apply(final Entry<PaperCard, Integer> from) {
return from.getKey();
}
}, true);
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@SuppressWarnings("rawtypes")
@Override
public final int compare(final Entry<T, Integer> arg0, final Entry<T, Integer> arg1) {
final Comparable obj1 = this.field.apply(arg0);
final Comparable obj2 = this.field.apply(arg1);
if (obj1 == null) {
return -1;
}
if (obj2 == null) {
return 1;
}
//System.out.println(String.format("%s vs %s _______ %s vs %s", arg0, arg1, obj1, obj2));
return this.ascending ? obj1.compareTo(obj2) : obj2.compareTo(obj1);
}
}

View File

@@ -0,0 +1,256 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import forge.item.InventoryItem;
/**
* <p>
* ItemPoolView class.
* </p>
*
* @param <T>
* an InventoryItem
* @author Forge
* @version $Id: ItemPoolView.java 9708 2011-08-09 19:34:12Z jendave $
*/
public class ItemPoolView<T extends InventoryItem> implements Iterable<Entry<T, Integer>> {
/** The fn to printed. */
public final transient Function<Entry<T, Integer>, T> FN_GET_KEY = new Function<Entry<T, Integer>, T>() {
@Override
public T apply(final Entry<T, Integer> from) {
return from.getKey();
}
};
/** The fn to item name. */
public final transient Function<Entry<T, Integer>, String> FN_GET_NAME = new Function<Entry<T, Integer>, String>() {
@Override
public String apply(final Entry<T, Integer> from) {
return from.getKey().getName();
}
};
/** The fn to count. */
public final transient Function<Entry<T, Integer>, Integer> FN_GET_COUNT = new Function<Entry<T, Integer>, Integer>() {
@Override
public Integer apply(final Entry<T, Integer> from) {
return from.getValue();
}
};
// Constructors
public ItemPoolView(final Class<T> cls) {
this(new Hashtable<T, Integer>(), cls);
}
public ItemPoolView(final Map<T, Integer> inMap, final Class<T> cls) {
this.items = inMap;
this.myClass = cls;
}
// Data members
/** The items. */
private final Map<T, Integer> items;
/** The my class. */
private final Class<T> myClass; // class does not keep this in runtime by
// itself
// same thing as above, it was copied to provide sorting (needed by table
// views in deck editors)
/** The items ordered. */
private final transient List<Entry<T, Integer>> itemsOrdered = new ArrayList<Map.Entry<T, Integer>>();
/** Whether list is in sync. */
protected transient boolean isListInSync = false;
/**
* iterator.
*
* @return Iterator<Entry<T, Integer>>
*/
@Override
public final Iterator<Entry<T, Integer>> iterator() {
return this.items.entrySet().iterator();
}
// Items read only operations
/**
*
* contains.
*
* @param item
* a T
* @return boolean
*/
public final boolean contains(final T item) {
if (this.items == null) {
return false;
}
return this.items.containsKey(item);
}
/**
*
* count.
*
* @param item
* a T
* @return int
*/
public final int count(final T item) {
if (this.items == null) {
return 0;
}
final Integer boxed = this.items.get(item);
return boxed == null ? 0 : boxed.intValue();
}
/**
*
* countAll.
*
* @return int
*/
public final int countAll() {
return countAll(null, myClass);
}
public final int countAll(Predicate<T> condition) {
return countAll(condition, myClass);
}
public final <U extends InventoryItem> int countAll(Predicate<U> condition, Class<U> cls) {
int result = 0;
if (this.items != null) {
final boolean isSameClass = cls == myClass;
for (final Entry<T, Integer> kv : this) {
final T key = kv.getKey();
@SuppressWarnings("unchecked")
final U castKey = isSameClass || cls.isInstance(key) ? (U)key : null;
if (null == condition || castKey != null && condition.apply(castKey))
result += kv.getValue();
}
}
return result;
}
/**
*
* countDistinct.
*
* @return int
*/
public final int countDistinct() {
return this.items.size();
}
/**
*
* isEmpty.
*
* @return boolean
*/
public final boolean isEmpty() {
return (this.items == null) || this.items.isEmpty();
}
/**
*
* getOrderedList.
*
* @return List<Entry<T, Integer>>
*/
public final List<Entry<T, Integer>> getOrderedList() {
if (!this.isListInSync) {
this.rebuildOrderedList();
}
return this.itemsOrdered;
}
private void rebuildOrderedList() {
this.itemsOrdered.clear();
if (this.items != null) {
for (final Entry<T, Integer> e : this.items.entrySet()) {
this.itemsOrdered.add(e);
}
}
this.isListInSync = true;
}
/**
*
* toFlatList.
*
* @return List<T>
*/
public final List<T> toFlatList() {
final List<T> result = new ArrayList<T>();
for (final Entry<T, Integer> e : this) {
for (int i = 0; i < e.getValue(); i++) {
result.add(e.getKey());
}
}
return result;
}
/**
* Gets the items.
*
* @return the items
*/
protected Map<T, Integer> getItems() {
return this.items;
}
/**
* Gets the my class.
*
* @return the myClass
*/
public Class<T> getMyClass() {
return this.myClass;
}
/**
* To item list string.
*
* @return the iterable
*/
public Iterable<String> toItemListString() {
final List<String> list = new ArrayList<String>();
for (final Entry<T, Integer> e : this.items.entrySet()) {
list.add(String.format("%d x %s", e.getValue(), e.getKey().getName()));
}
return list;
}
}

View File

@@ -0,0 +1,134 @@
package forge.util;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
/**
* TODO: Write javadoc for this type.
*
*/
public class Lang {
/**
* TODO: Write javadoc for this method.
* @param position
* @return
*/
public static String getOrdinal(int position) {
String[] sufixes = new String[] { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
switch (position % 100) {
case 11:
case 12:
case 13:
return position + "th";
default:
return position + sufixes[position % 10];
}
}
public static <T> String joinHomogenous(String s1, String s2) {
boolean has1 = StringUtils.isNotBlank(s1);
boolean has2 = StringUtils.isNotBlank(s2);
return has1 ? (has2 ? s1 + " and " + s2 : s1) : (has2 ? s2 : "");
}
public static <T> String joinHomogenous(Iterable<T> objects) { return joinHomogenous(Lists.newArrayList(objects)); }
public static <T> String joinHomogenous(Collection<T> objects) { return joinHomogenous(objects, null, "and"); }
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor) {
return joinHomogenous(objects, accessor, "and");
}
public static <T> String joinHomogenous(Collection<T> objects, Function<T, String> accessor, String lastUnion) {
int remaining = objects.size();
StringBuilder sb = new StringBuilder();
for(T obj : objects) {
remaining--;
if( accessor != null )
sb.append(accessor.apply(obj));
else
sb.append(obj);
if( remaining > 1 ) sb.append(", ");
if( remaining == 1 ) sb.append(" ").append(lastUnion).append(" ");
}
return sb.toString();
}
public static <T> String joinVerb(List<T> subjects, String verb) {
return subjects.size() > 1 || !subjectIsSingle3rdPerson(Iterables.getFirst(subjects, "it").toString()) ? verb : verbs3rdPersonSingular(verb);
}
public static String joinVerb(String subject, String verb) {
return !Lang.subjectIsSingle3rdPerson(subject) ? verb : verbs3rdPersonSingular(verb);
}
public static boolean subjectIsSingle3rdPerson(String subject) {
// Will be most simple
return !"You".equalsIgnoreCase(subject);
}
public static String verbs3rdPersonSingular(String verb) {
// English is simple - just add (s) for multiple objects.
return verb + "s";
}
public static String getPlural(String noun) {
return noun + ( noun.endsWith("s") || noun.endsWith("x") ? "es" : "s");
}
public static <T> String nounWithAmount(int cnt, String noun) {
String countedForm = cnt <= 1 ? noun : getPlural(noun);
final String strCount;
if( cnt == 1 )
strCount = startsWithVowel(noun) ? "an " : "a ";
else
strCount = String.valueOf(cnt) + " ";
return strCount + countedForm;
}
public static <T> String nounWithNumeral(int cnt, String noun) {
String countedForm = cnt <= 1 ? noun : getPlural(noun);
return getNumeral(cnt) + " " + countedForm;
}
public static String getPossesive(String name) {
if ("You".equalsIgnoreCase(name)) return name + "r"; // to get "your"
return name.endsWith("s") ? name + "'" : name + "'s";
}
public static boolean startsWithVowel(String word) {
return isVowel(word.trim().charAt(0));
}
private static final char[] vowels = { 'a', 'i', 'e', 'o', 'u' };
public static boolean isVowel(char letter) {
char l = Character.toLowerCase(letter);
for(char c : vowels)
if ( c == l ) return true;
return false;
}
public final static String[] numbers0 = new String[] {
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
"ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eightteen", "nineteen" };
public final static String[] numbers20 = new String[] {"twenty", "thirty", "fourty", "fifty", "sixty", "seventy", "eighty", "ninety" };
public static String getNumeral(int n) {
String prefix = n < 0 ? "minus " : "";
n = Math.abs(n);
if ( n >= 0 && n < 20 )
return prefix + numbers0[n];
if ( n < 100 ) {
int n1 = n % 10;
String ones = n1 == 0 ? "" : numbers0[n1];
return prefix + numbers20[(n / 10) - 2] + " " + ones;
}
return Integer.toString(n);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import java.security.SecureRandom;
import java.util.Random;
/**
* <p>
* MyRandom class.<br>
* Preferably all Random numbers should be retrieved using this wrapper class
* </p>
*
* @author Forge
* @version $Id$
*/
public class MyRandom {
/** Constant <code>random</code>. */
private static Random random = new SecureRandom();
/**
* <p>
* percentTrue.<br>
* If percent is like 30, then 30% of the time it will be true.
* </p>
*
* @param percent
* a int.
* @return a boolean.
*/
public static boolean percentTrue(final int percent) {
return percent > MyRandom.getRandom().nextInt(100);
}
/**
* Gets the random.
*
* @return the random
*/
public static Random getRandom() {
return MyRandom.random;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 MaxMtg
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
/**
* Special predicate class to perform string operations.
*
* @param <T>
* the generic type
*/
public abstract class PredicateString<T> implements Predicate<T> {
/** Possible operators for string operands. */
public enum StringOp {
/** The CONTAINS. */
CONTAINS,
/** The CONTAINS ignore case. */
CONTAINS_IC,
/** The EQUALS. */
EQUALS,
/** The EQUALS. */
EQUALS_IC
}
/** The operator. */
private final StringOp operator;
/**
* Op.
*
* @param op1
* the op1
* @param op2
* the op2
* @return true, if successful
*/
protected final boolean op(final String op1, final String op2) {
switch (this.getOperator()) {
case CONTAINS_IC:
return StringUtils.containsIgnoreCase(op1, op2);
case CONTAINS:
return StringUtils.contains(op1, op2);
case EQUALS:
return op1.equals(op2);
case EQUALS_IC:
return op1.equalsIgnoreCase(op2);
default:
return false;
}
}
/**
* Instantiates a new predicate string.
*
* @param operator
* the operator
*/
public PredicateString(final StringOp operator) {
this.operator = operator;
}
/**
* @return the operator
*/
public StringOp getOperator() {
return operator;
}
public static PredicateString<String> contains(final String what) {
return new PredicateString<String>(StringOp.CONTAINS) {
@Override
public boolean apply(String subject) {
return op(subject, what);
}
};
}
public static PredicateString<String> containsIgnoreCase(final String what) {
return new PredicateString<String>(StringOp.CONTAINS_IC) {
@Override
public boolean apply(String subject) {
return op(subject, what);
}
};
}
public static PredicateString<String> equals(final String what) {
return new PredicateString<String>(StringOp.EQUALS) {
@Override
public boolean apply(String subject) {
return op(subject, what);
}
};
}
}

View File

@@ -0,0 +1,122 @@
package forge.util;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import forge.item.PaperCard;
/**
* TODO: Write javadoc for this type.
*
*/
public class TextUtil {
/**
* Safely converts an object to a String.
*
* @param obj
* to convert; may be null
*
* @return "null" if obj is null, obj.toString() otherwise
*/
public static String safeToString(final Object obj) {
return obj == null ? "null" : obj.toString();
}
public static String mapToString(Map<String, ?> map) {
StringBuilder mapAsString = new StringBuilder();
boolean isFirst = true;
for (Entry<String, ?> p : map.entrySet()) {
if (isFirst) {
isFirst = false;
} else {
mapAsString.append("; ");
}
mapAsString.append(p.getKey() + " => " + (p.getValue() == null ? "(null)" : p.getValue().toString()));
}
return mapAsString.toString();
}
public static String[] split(CharSequence input, char delimiter) {
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '\0', '\0', true);
}
public static String[] split(CharSequence input, char delimiter, int limit) {
return splitWithParenthesis(input, delimiter, limit, '\0', '\0', true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter) {
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, '(', ')', true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter, char openPar, char closePar) {
return splitWithParenthesis(input, delimiter, Integer.MAX_VALUE, openPar, closePar, true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int limit) {
return splitWithParenthesis(input, delimiter, limit, '(', ')', true);
}
public static String[] splitWithParenthesis(CharSequence input, char delimiter, char openPar, char closePar, int limit) {
return splitWithParenthesis(input, delimiter, limit, openPar, closePar, true);
}
/**
* Split string separated by a single char delimiter, can take parenthesis in account
* It's faster than String.split, and allows parenthesis
*/
public static String[] splitWithParenthesis(CharSequence input, char delimiter, int maxEntries, char openPar, char closePar, boolean skipEmpty) {
List<String> result = new ArrayList<String>();
// Assume that when equal non-zero parenthesis are passed, they need to be discarded
boolean trimParenthesis = openPar == closePar && openPar > 0;
int nPar = 0;
int len = input.length();
int start = 0;
int idx = 1;
for (int iC = 0; iC < len; iC++ ) {
char c = input.charAt(iC);
if( closePar > 0 && c == closePar && nPar > 0 ) { nPar--; }
else if( openPar > 0 && c == openPar ) nPar++;
if( c == delimiter && nPar == 0 && idx < maxEntries) {
if( iC > start || !skipEmpty ) {
result.add(input.subSequence(start, iC).toString());
idx++;
}
start = iC + 1;
}
}
if( len > start || !skipEmpty )
result.add(input.subSequence(start, len).toString());
String[] toReturn = result.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
return trimParenthesis ? StringUtils.stripAll(toReturn, String.valueOf(openPar)) : toReturn;
}
/**
* Converts an enum value to a printable label but upcasing the first letter
* and lcasing all subsequent letters
*/
public static String enumToLabel(Enum<?> val) {
return val.toString().substring(0, 1).toUpperCase(Locale.ENGLISH) +
val.toString().substring(1).toLowerCase(Locale.ENGLISH);
}
public static String buildFourColumnList(String firstLine, Iterable<PaperCard> cAnteRemoved) {
StringBuilder sb = new StringBuilder(firstLine);
int i = 0;
for(PaperCard cp: cAnteRemoved) {
if ( i != 0 ) sb.append(", ");
if ( i % 4 == 0 ) sb.append("\n");
sb.append(cp);
i++;
}
return sb.toString();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util.storage;
import java.util.Collection;
import com.google.common.base.Predicate;
import forge.util.IHasName;
/**
* TODO: Write javadoc for this type.
*
* @param <T> the generic type
*/
public interface IStorage<T> extends Iterable<T>, IHasName {
T get(final String name);
T find(final Predicate<T> condition);
// todo: find(final Predicate<T> condition, boolean recursive).
Collection<String> getItemNames();
boolean contains(final String name);
int size();
void add(final T deck);
void delete(final String deckName);
IStorage<IStorage<T>> getFolders();
}

View File

@@ -0,0 +1,115 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Forge Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util.storage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.util.IItemReader;
//reads and writeDeck Deck objects
/**
* <p>
* DeckManager class.
* </p>
*
* @param <T> the generic type
* @author Forge
* @version $Id: DeckManager.java 13590 2012-01-27 20:46:27Z Max mtg $
*/
public class StorageBase<T> implements IStorage<T> {
protected final Map<String, T> map;
public final static StorageBase<?> emptyMap = new StorageBase<Object>("Empty", new HashMap<String, Object>());
public final String name;
public StorageBase(final String name, final IItemReader<T> io) {
this.name = name;
this.map = io.readAll();
}
public StorageBase(final String name, final Map<String, T> inMap) {
this.name = name;
this.map = inMap;
}
@Override
public T get(final String name) {
return this.map.get(name);
}
@Override
public final Collection<String> getItemNames() {
return new ArrayList<String>(this.map.keySet());
}
@Override
public Iterator<T> iterator() {
return this.map.values().iterator();
}
@Override
public boolean contains(String name) {
return name == null ? false : this.map.containsKey(name);
}
@Override
public int size() {
return this.map.size();
}
@Override
public T find(Predicate<T> condition) {
return Iterables.tryFind(map.values(), condition).orNull();
}
@Override
public void add(T deck) {
throw new UnsupportedOperationException("This is a read-only storage");
}
@Override
public void delete(String deckName) {
throw new UnsupportedOperationException("This is a read-only storage");
}
// we don't have nested folders unless that's overridden in a derived class
@SuppressWarnings("unchecked")
@Override
public IStorage<IStorage<T>> getFolders() {
return (IStorage<IStorage<T>>) emptyMap;
}
/* (non-Javadoc)
* @see forge.util.IHasName#getName()
*/
@Override
public String getName() {
// TODO Auto-generated method stub
return name;
}
}

View File

@@ -0,0 +1,27 @@
package forge.util.storage;
import java.io.File;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import forge.util.IItemReader;
public abstract class StorageReaderBase<T> implements IItemReader<T> {
protected final Function<? super T, String> keySelector;
public StorageReaderBase(final Function<? super T, String> keySelector0) {
keySelector = keySelector0;
}
@Override
public Iterable<File> getSubFolders() {
// TODO Auto-generated method stub
return ImmutableList.of();
}
@Override
public IItemReader<T> getReaderForFolder(File subfolder) {
throw new UnsupportedOperationException("This reader is not supposed to have nested folders");
}
}

View File

@@ -0,0 +1,119 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Nate
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util.storage;
import java.io.File;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import forge.util.FileUtil;
/**
* This class treats every line of a given file as a source for a named object.
*
* @param <T>
* the generic type
*/
public abstract class StorageReaderFile<T> extends StorageReaderBase<T> {
private final File file;
/**
* Instantiates a new storage reader file.
*
* @param pathname the pathname
* @param keySelector0 the key selector0
*/
public StorageReaderFile(final String pathname, final Function<? super T, String> keySelector0) {
this(new File(pathname), keySelector0);
}
/**
* Instantiates a new storage reader file.
*
* @param file0 the file0
* @param keySelector0 the key selector0
*/
public StorageReaderFile(final File file0, final Function<? super T, String> keySelector0) {
super(keySelector0);
this.file = file0;
}
/* (non-Javadoc)
* @see forge.util.IItemReader#readAll()
*/
@Override
public Map<String, T> readAll() {
final Map<String, T> result = new TreeMap<String, T>();
int idx = 0;
for (final String s : FileUtil.readFile(this.file)) {
if (!this.lineContainsObject(s)) {
continue;
}
final T item = this.read(s, idx);
if (null == item) {
final String msg = "An object stored in " + this.file.getPath() + " failed to load.\nPlease submit this as a bug with the mentioned file attached.";
throw new RuntimeException(msg);
}
idx++;
String newKey = keySelector.apply(item);
if( result.containsKey(newKey))
System.err.println("StorageReader: Overwriting an object with key " + newKey);
result.put(newKey, item);
}
return result;
}
/**
* TODO: Write javadoc for this method.
*
* @param line
* the line
* @return the t
*/
protected abstract T read(String line, int idx);
/**
* Line contains object.
*
* @param line
* the line
* @return true, if successful
*/
protected boolean lineContainsObject(final String line) {
return !StringUtils.isBlank(line) && !line.trim().startsWith("#");
}
/* (non-Javadoc)
* @see forge.util.IItemReader#getItemKey(java.lang.Object)
*/
@Override
public String getItemKey(final T item) {
return this.keySelector.apply(item);
}
}

View File

@@ -0,0 +1,136 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Nate
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util.storage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import forge.util.FileUtil;
/**
* This class treats every line of a given file as a source for a named object.
*
* @param <T>
* the generic type
*/
public abstract class StorageReaderFileSections<T> extends StorageReaderBase<T> {
private final File file;
public StorageReaderFileSections(final String pathname, final Function<? super T, String> keySelector0) {
this(new File(pathname), keySelector0);
}
public StorageReaderFileSections(final File file0, final Function<? super T, String> keySelector0) {
super(keySelector0);
this.file = file0;
}
/* (non-Javadoc)
* @see forge.util.IItemReader#readAll()
*/
@Override
public Map<String, T> readAll() {
final Map<String, T> result = new TreeMap<String, T>();
int idx = 0;
Iterable<String> file = FileUtil.readFile(this.file);
List<String> accumulator = new ArrayList<String>();
String header = null;
for (final String s : file) {
if (!this.lineContainsObject(s)) {
continue;
}
if(s.charAt(0) == '[') {
if( header != null ) {
// read previously collected item
T item = readItem(header, accumulator, idx);
if( item != null ) {
result.put(this.keySelector.apply(item), item);
idx++;
}
}
header = StringUtils.strip(s, "[] ");
accumulator.clear();
} else
accumulator.add(s);
}
// store the last item
if ( !accumulator.isEmpty() ) {
T item = readItem(header, accumulator, idx);
if( item != null ) {
String newKey = keySelector.apply(item);
if( result.containsKey(newKey))
System.err.println("StorageReader: Overwriting an object with key " + newKey);
result.put(newKey, item);
}
}
return result;
}
private final T readItem(String header, Iterable<String> accumulator, int idx) {
final T item = this.read(header, accumulator, idx);
if (null != item) return item;
final String msg = "An object stored in " + this.file.getPath() + " failed to load.\nPlease submit this as a bug with the mentioned file attached.";
throw new RuntimeException(msg);
}
/**
* TODO: Write javadoc for this method.
*
* @param line
* the line
* @return the t
*/
protected abstract T read(String title, Iterable<String> body, int idx);
/**
* Line contains object.
*
* @param line
* the line
* @return true, if successful
*/
protected boolean lineContainsObject(final String line) {
return !StringUtils.isBlank(line) && !line.trim().startsWith("#");
}
/* (non-Javadoc)
* @see forge.util.IItemReader#getItemKey(java.lang.Object)
*/
@Override
public String getItemKey(final T item) {
return this.keySelector.apply(item);
}
}

View File

@@ -0,0 +1,142 @@
/*
* Forge: Play Magic: the Gathering.
* Copyright (C) 2011 Nate
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package forge.util.storage;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeMap;
import com.google.common.base.Function;
/**
* This class treats every file in the given folder as a source for a named
* object. The descendant should implement read method to deserialize a single
* item. So that readAll will return a map of Name => Object as read from disk
*
* @param <T> the generic type
*/
public abstract class StorageReaderFolder<T> extends StorageReaderBase<T> {
/**
* @return the directory
*/
public File getDirectory() {
return directory;
}
protected final File directory;
/**
* Instantiates a new storage reader folder.
*
* @param deckDir0 the deck dir0
*/
public StorageReaderFolder(final File deckDir0, Function<? super T, String> keySelector0) {
super(keySelector0);
this.directory = deckDir0;
if (this.directory == null) {
throw new IllegalArgumentException("No directory specified");
}
try {
if (this.directory.isFile()) {
throw new IOException("Not a directory");
} else {
this.directory.mkdirs();
if (!this.directory.isDirectory()) {
throw new IOException("Directory can't be created");
}
}
} catch (final IOException ex) {
throw new RuntimeException("StorageReaderFolder.ctor() error, " + ex.getMessage());
}
}
public final List<String> objectsThatFailedToLoad = new ArrayList<String>();
/* (non-Javadoc)
* @see forge.util.IItemReader#readAll()
*/
@Override
public Map<String, T> readAll() {
final Map<String, T> result = new TreeMap<String, T>();
final File[] files = this.directory.listFiles(this.getFileFilter());
for (final File file : files) {
try {
final T newDeck = this.read(file);
if (null == newDeck) {
final String msg = "An object stored in " + file.getPath() + " failed to load.\nPlease submit this as a bug with the mentioned file/directory attached.";
throw new RuntimeException(msg);
}
String newKey = keySelector.apply(newDeck);
if( result.containsKey(newKey))
System.err.println("StorageReader: Overwriting an object with key " + newKey);
result.put(newKey, newDeck);
} catch (final NoSuchElementException ex) {
final String message = String.format("%s failed to load because ---- %s", file.getName(), ex.getMessage());
objectsThatFailedToLoad.add(message);
}
}
return result;
}
/**
* Read the object from file.
*
* @param file the file
* @return the object deserialized by inherited class
*/
protected abstract T read(File file);
/**
* TODO: Write javadoc for this method.
*
* @return FilenameFilter to pick only relevant objects for deserialization
*/
protected abstract FilenameFilter getFileFilter();
@Override
public String getItemKey(T item) {
return keySelector.apply(item);
}
// methods handling nested folders are provided. It's up to consumer whether to use these or not.
@Override
public Iterable<File> getSubFolders() {
File[] list = this.directory.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() && !file.isHidden();
}
});
return Arrays.asList(list);
}
}

View File

@@ -0,0 +1,8 @@
/**
*
*/
/**
* @author Max
*
*/
package forge.util.storage;