diff --git a/src/main/java/forge/CardReader.java b/src/main/java/forge/CardReader.java index a369db7e4ba..238e13e5a14 100644 --- a/src/main/java/forge/CardReader.java +++ b/src/main/java/forge/CardReader.java @@ -8,7 +8,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; @@ -25,6 +27,8 @@ import net.slightlymagic.braids.util.progress_monitor.StderrProgressMonitor; import com.google.code.jyield.Generator; import com.google.code.jyield.YieldUtils; +import forge.card.CardRules; +import forge.card.CardRulesReader; import forge.card.trigger.TriggerHandler; import forge.error.ErrorViewer; import forge.properties.NewConstants; @@ -59,10 +63,12 @@ public class CardReader protected static final int UNKNOWN_NUMBER_OF_FILES_REMAINING = -1; // NOPMD by Braids on 8/18/11 10:54 PM private transient Map mapToFill; + private transient List listRulesToFill; private transient File cardsfolder; private transient ZipFile zip; private transient Charset charset; + private transient CardRulesReader rulesReader; private transient Enumeration zipEnum; @@ -72,6 +78,10 @@ public class CardReader private transient Iterable findNonDirsIterable; // NOPMD by Braids on 8/18/11 10:56 PM + + public CardReader(final File theCardsFolder, final Map theMapToFill ) { + this(theCardsFolder, theMapToFill, null, true); + } /** * This is a convenience for CardReader(cardsfolder, mapToFill, true); . @@ -82,8 +92,9 @@ public class CardReader * place the cards once read * */ - public CardReader(final File theCardsFolder, final Map theMapToFill) { - this(theCardsFolder, theMapToFill, true); + public CardReader(final File theCardsFolder, final Map theMapToFill, + final List listRules2Fill) { + this(theCardsFolder, theMapToFill, listRules2Fill, true); } /** @@ -96,11 +107,16 @@ public class CardReader * * @param useZip if true, attempts to load cards from a zip file, if one exists. */ - public CardReader(final File theCardsFolder, final Map theMapToFill, final boolean useZip) { + public CardReader(final File theCardsFolder, final Map theMapToFill, + final List listRules2Fill, final boolean useZip) + { if (theMapToFill == null) { throw new NullPointerException("theMapToFill must not be null."); // NOPMD by Braids on 8/18/11 10:53 PM } this.mapToFill = theMapToFill; + // These read data for lightweight classes. + this.listRulesToFill = listRules2Fill == null ? new ArrayList() : listRules2Fill; + this.rulesReader = new CardRulesReader(); if (!theCardsFolder.exists()) { throw new RuntimeException(// NOPMD by Braids on 8/18/11 10:53 PM @@ -168,7 +184,7 @@ public class CardReader /** * Reads the rest of ALL the cards into memory. This is not lazy. */ - public final void run() { + public final void run() { loadCardsUntilYouFind(null); } @@ -294,6 +310,7 @@ public class CardReader */ protected final Card loadCard(final InputStream inputStream) { final Card card = new Card(); + rulesReader.reset(); InputStreamReader inputStreamReader = null; BufferedReader reader = null; @@ -303,6 +320,7 @@ public class CardReader String line = readLine(reader); while (!"End".equals(line)) { + rulesReader.parseLine(line); if (line.charAt(0) == '#') { // NOPMD by Braids on 8/18/11 10:59 PM //no need to do anything, this indicates a comment line } else if (line.startsWith("Name:")) { @@ -386,6 +404,7 @@ public class CardReader } } + listRulesToFill.add(rulesReader.getCard()); mapToFill.put(card.getName(), card); return card; } diff --git a/src/main/java/forge/card/CardDb.java b/src/main/java/forge/card/CardDb.java index 1446c31e9a5..8fbe2795803 100644 --- a/src/main/java/forge/card/CardDb.java +++ b/src/main/java/forge/card/CardDb.java @@ -23,14 +23,20 @@ public final class CardDb { private static volatile CardDb onlyInstance = null; // 'volatile' keyword makes this working public static CardDb instance() { if (onlyInstance == null) { - synchronized (CardDb.class) { - if (onlyInstance == null) { // It's broken under 1.4 and below, on 1.5+ works again! - onlyInstance = new CardDb(); - } - } + throw new NullPointerException("CardDb has not yet been initialized, run setup() first"); } return onlyInstance; } + public static void setup(final Iterator list) { + if (onlyInstance != null) { + throw new RuntimeException("CardDb has already been initialized, don't do it twice please"); + } + synchronized (CardDb.class) { + if (onlyInstance == null) { // It's broken under 1.4 and below, on 1.5+ works again! + onlyInstance = new CardDb(list); + } + } + } // Here oracle cards private final Map cards = new Hashtable(); @@ -96,7 +102,7 @@ public final class CardDb { throw new NoSuchElementException(String.format("Card '%s' not found in our database.", name)); } // Advanced fetch by name+set - public CardPrinted getCard(final String name, final String set) { return getCard(name, set, 1); } + public CardPrinted getCard(final String name, final String set) { return getCard(name, set, 0); } public CardPrinted getCard(final String name, final String set, final int artIndex) { // 1. get set Map cardsFromset = allCardsBySet.get(set); @@ -111,7 +117,7 @@ public final class CardDb { throw new NoSuchElementException(err); } // 3. Get the proper copy - if (artIndex > 0 && artIndex <= cards.length) { return cards[artIndex]; } + if (artIndex >= 0 && artIndex <= cards.length) { return cards[artIndex]; } String err = String.format("Asked for '%s' from '%s' #%d: db didn't find that copy.", name, set, artIndex); throw new NoSuchElementException(err); } diff --git a/src/main/java/forge/card/CardManaCost.java b/src/main/java/forge/card/CardManaCost.java index e5953ac5638..ab3339ea472 100644 --- a/src/main/java/forge/card/CardManaCost.java +++ b/src/main/java/forge/card/CardManaCost.java @@ -21,7 +21,8 @@ public final class CardManaCost implements Comparable { private Float compareWeight = null; - + public static final CardManaCost empty = new CardManaCost(); + // pass mana cost parser here private CardManaCost() { isEmpty = true; @@ -29,12 +30,7 @@ public final class CardManaCost implements Comparable { stringValue = ""; } - public static final CardManaCost empty = new CardManaCost(); - - public CardManaCost(final String cost) { - this(new ParserMtgData(cost)); - } - + // public ctor, should give it a mana parser public CardManaCost(final ManaParser parser) { if (!parser.hasNext()) { throw new RuntimeException("Empty manacost passed to parser (this should have been handled before)"); @@ -49,19 +45,19 @@ public final class CardManaCost implements Comparable { } private String getSimpleString() { - if (shards.isEmpty()) return Integer.toString(genericCost); - + if (shards.isEmpty()) { return Integer.toString(genericCost); } + StringBuilder sb = new StringBuilder(); boolean isFirst = false; if (genericCost > 0) { sb.append(genericCost); isFirst = false; } for (CardManaCostShard s : shards) { - if ( !isFirst ) { sb.append(' '); } + if (!isFirst) { sb.append(' '); } else { isFirst = false; } sb.append(s.toString()); } return sb.toString(); } - + public int getCMC() { int sum = 0; for (CardManaCostShard s : shards) { sum += s.cmc; } @@ -98,61 +94,6 @@ public final class CardManaCost implements Comparable { int getTotalColorlessCost(); } - public static class ParserMtgData implements ManaParser { - private final String cost; - private int nextBracket; - private int colorlessCost; - - - public ParserMtgData(final String cost) { - this.cost = cost; - // System.out.println(cost); - nextBracket = cost.indexOf('{'); - colorlessCost = 0; - } - - public int getTotalColorlessCost() { - if ( hasNext() ) { - throw new RuntimeException("Colorless cost should be obtained after iteration is complete"); - } - return colorlessCost; - } - - @Override - public boolean hasNext() { return nextBracket != -1; } - - @Override - public CardManaCostShard next() { - int closeBracket = cost.indexOf('}', nextBracket); - String unparsed = cost.substring(nextBracket + 1, closeBracket); - nextBracket = cost.indexOf('{', closeBracket + 1); - - // System.out.println(unparsed); - if (StringUtils.isNumeric(unparsed)) { - colorlessCost += Integer.parseInt(unparsed); - return null; - } - - int atoms = 0; - for (int iChar = 0; iChar < unparsed.length(); iChar++) { - switch (unparsed.charAt(iChar)) { - case 'W': atoms |= CardManaCostShard.Atom.WHITE; break; - case 'U': atoms |= CardManaCostShard.Atom.BLUE; break; - case 'B': atoms |= CardManaCostShard.Atom.BLACK; break; - case 'R': atoms |= CardManaCostShard.Atom.RED; break; - case 'G': atoms |= CardManaCostShard.Atom.GREEN; break; - case '2': atoms |= CardManaCostShard.Atom.OR_2_COLORLESS; break; - case 'P': atoms |= CardManaCostShard.Atom.OR_2_LIFE; break; - case 'X': atoms |= CardManaCostShard.Atom.IS_X; break; - default: break; - } - } - return CardManaCostShard.valueOf(atoms); - } - - @Override - public void remove() { } // unsuported - } } diff --git a/src/main/java/forge/card/CardRules.java b/src/main/java/forge/card/CardRules.java index a4c8893f469..2868180ff10 100644 --- a/src/main/java/forge/card/CardRules.java +++ b/src/main/java/forge/card/CardRules.java @@ -35,8 +35,8 @@ public final class CardRules { private Map setsPrinted = null; - private boolean removedFromAIDecks = false; - private boolean removedFromRandomDecks = false; + private boolean isRemovedFromAIDecks = false; + private boolean isRemovedFromRandomDecks = false; // Ctor and builders are needed here public String getName() { return name; } @@ -46,27 +46,13 @@ public final class CardRules { public String[] getRules() { return rules; } public Set> getSetsPrinted() { return setsPrinted.entrySet(); } - public String getPower() { return power; } - public int getIntPower() { return iPower; } - public String getToughness() { return toughness; } - public int getIntToughness() { return iToughness; } - public String getLoyalty() { return loyalty; } - - /** - * Getter for removedFromAIDecks. - * @return the removedFromAIDecks value - */ - public final boolean isRemovedFromAIDecks() { - return removedFromAIDecks; - } - - /** - * Getter for removedFromRandomDecks. - * @return the removedFromRandomDecks value - */ - public final boolean isRemovedFromRandomDecks() { - return removedFromRandomDecks; - } + public final String getPower() { return power; } + public final int getIntPower() { return iPower; } + public final String getToughness() { return toughness; } + public final int getIntToughness() { return iToughness; } + public final String getLoyalty() { return loyalty; } + public final boolean getemovedFromAIDecks() { return isRemovedFromAIDecks; } + public final boolean getRemovedFromRandomDecks() { return isRemovedFromRandomDecks; } public String getPTorLoyalty() { if (getType().isCreature()) { return power + "/" + toughness; } @@ -74,18 +60,20 @@ public final class CardRules { return ""; } - public CardRules(final String cardName, final CardType cardType, final String manacost, + public CardRules(final String cardName, final CardType cardType, final CardManaCost manacost, final String ptLine, final String[] cardRules, final Map setsData, - final boolean removedFromRandomDecks0, final boolean removedFromAIDecks0) + final boolean removedFromRandomDecks, final boolean removedFromAIDecks) { this.name = cardName; this.type = cardType; - this.cost = manacost == null ? CardManaCost.empty : new CardManaCost(manacost); + this.cost = manacost; this.rules = cardRules; this.color = new CardColor(cost); - this.removedFromAIDecks = removedFromAIDecks0; - this.removedFromRandomDecks = removedFromRandomDecks0; + this.isRemovedFromAIDecks = removedFromAIDecks; + this.isRemovedFromRandomDecks = removedFromRandomDecks; + //System.out.println(cardName); + if (cardType.isCreature()) { int slashPos = ptLine.indexOf('/'); if (slashPos == -1) { @@ -123,7 +111,11 @@ public final class CardRules { CardInSet cis = setsPrinted.get(getLatestSetPrinted()); return cis.getRarity(); } - + + public String getAiStatus() { + return isRemovedFromAIDecks ? (isRemovedFromRandomDecks ? "AI ?" : "AI") : (isRemovedFromRandomDecks ? "?" :""); + } + public abstract static class Predicates { // Static builder methods - they choose concrete implementation by themselves diff --git a/src/main/java/forge/card/CardRulesReader.java b/src/main/java/forge/card/CardRulesReader.java index ed3b96b0524..18d1707bdbf 100644 --- a/src/main/java/forge/card/CardRulesReader.java +++ b/src/main/java/forge/card/CardRulesReader.java @@ -1,3 +1,4 @@ + package forge.card; import java.io.BufferedReader; @@ -11,9 +12,12 @@ import java.util.Locale; import java.util.Map; import java.util.TreeMap; import java.util.regex.Pattern; + +import org.apache.commons.lang3.StringUtils; //import java.util.zip.ZipEntry; //import java.util.zip.ZipFile; +import forge.card.CardManaCost.ManaParser; import forge.error.ErrorViewer; import forge.properties.NewConstants; @@ -25,358 +29,64 @@ import forge.properties.NewConstants; * * @version $Id$ */ -public class CardRulesReader - //implements Runnable, // NOPMD by Braids on 8/18/11 10:55 PM - implements NewConstants -{ - private static final String CARD_FILE_DOT_EXTENSION = ".txt"; // NOPMD by Braids on 8/18/11 11:04 PM - - /** Default charset when loading from files. */ - public static final String DEFAULT_CHARSET_NAME = "US-ASCII"; // NOPMD by Braids on 8/18/11 10:54 PM - - /** Regex that matches a single hyphen (-) or space. */ - public static final Pattern HYPHEN_OR_SPACE = Pattern.compile("[ -]"); - - /** Regex for punctuation that we omit from card file names. */ - public static final Pattern PUNCTUATION_TO_ZAP = Pattern.compile("[,'\"]"); // NOPMD by Braids on 8/18/11 10:54 PM - - /** Regex that matches two or more underscores (_). */ - public static final Pattern MULTIPLE_UNDERSCORES = Pattern.compile("__+"); // NOPMD by Braids on 8/18/11 10:54 PM - - /** Special value for estimatedFilesRemaining. */ - protected static final int UNKNOWN_NUMBER_OF_FILES_REMAINING = -1; // NOPMD by Braids on 8/18/11 10:54 PM - - //private transient Map mapToFill; - //private transient File cardsfolder; - - //private transient ZipFile zip; - private transient Charset charset; - - //private transient Enumeration zipEnum; - - /* - private transient long estimatedFilesRemaining = // NOPMD by Braids on 8/18/11 10:56 PM - UNKNOWN_NUMBER_OF_FILES_REMAINING; - */ +public class CardRulesReader { - private transient Iterable findNonDirsIterable; // NOPMD by Braids on 8/18/11 10:56 PM + private String cardName = null; + private CardType cardType = null; + private CardManaCost manaCost = CardManaCost.empty; + private String ptLine = null; + private String[] cardRules = null; + private Map setsData = new TreeMap(); + private boolean removedFromAIDecks = false; + private boolean removedFromRandomDecks = false; - - - /** - * This is a convenience for CardReader(cardsfolder, mapToFill, true); . - * - * @param theCardsFolder indicates location of the cardsFolder - * - * @param theMapToFill maps card names to Card instances; this is where we - * place the cards once read - * - public CardReader(final File theCardsFolder, final Map theMapToFill) { - this(theCardsFolder, theMapToFill, true); - } - */ - - /** - *

Constructor for CardReader.

- * - * @param theCardsFolder indicates location of the cardsFolder - * - * @param theMapToFill maps card names to Card instances; this is where we - * place the cards once read - * - * @param useZip if true, attempts to load cards from a zip file, if one exists. - public CardReader(final File theCardsFolder, final Map theMapToFill, final boolean useZip) { - if (theMapToFill == null) { - throw new NullPointerException("theMapToFill must not be null."); // NOPMD by Braids on 8/18/11 10:53 PM - } - this.mapToFill = theMapToFill; - - if (!theCardsFolder.exists()) { - throw new RuntimeException(// NOPMD by Braids on 8/18/11 10:53 PM - "CardReader : constructor error -- file not found -- filename is " - + theCardsFolder.getAbsolutePath()); - } - - if (!theCardsFolder.isDirectory()) { - throw new RuntimeException(// NOPMD by Braids on 8/18/11 10:53 PM - "CardReader : constructor error -- not a directory -- " - + theCardsFolder.getAbsolutePath()); - } - - this.cardsfolder = theCardsFolder; - - - final File zipFile = new File(theCardsFolder, "cardsfolder.zip"); - - // Prepare resources to read cards lazily. - if (useZip && zipFile.exists()) { - try { - this.zip = new ZipFile(zipFile); - } catch (Exception exn) { - System.err.println("Error reading zip file \"" // NOPMD by Braids on 8/18/11 10:53 PM - + zipFile.getAbsolutePath() + "\": " + exn + ". " - + "Defaulting to txt files in \"" - + theCardsFolder.getAbsolutePath() - + "\"." - ); - } - - } - - if (useZip && zip != null) { - zipEnum = zip.entries(); - estimatedFilesRemaining = zip.size(); - } - - setEncoding(DEFAULT_CHARSET_NAME); - - } //CardReader() - */ - - - /** - * This finalizer helps assure there is no memory or thread leak with - * findNonDirsIterable, which was created with YieldUtils.toIterable. - * - * @throws Throwable indirectly - */ - protected final void finalize() throws Throwable { - try { - if (findNonDirsIterable != null) { - for (@SuppressWarnings("unused") File ignored - : findNonDirsIterable) - { - // Do nothing; just exercising the Iterable. - } - } - } finally { - super.finalize(); - } + // Reset all fields to parse next card (to avoid allocating new CardRulesReader N times) + public final void reset() { + cardName = null; + cardType = null; + manaCost = CardManaCost.empty; + ptLine = null; + cardRules = null; + setsData = new TreeMap(); + removedFromAIDecks = false; + removedFromRandomDecks = false; } - - /** - * Reads the rest of ALL the cards into memory. This is not lazy. - public final void run() { - loadCardsUntilYouFind(null); + public final CardRules getCard() { + return new CardRules(cardName, cardType, manaCost, ptLine, cardRules, setsData, removedFromRandomDecks, removedFromAIDecks); } - */ + - /** - * Starts reading cards into memory until the given card is found. - * - * After that, we save our place in the list of cards (on disk) in case we - * need to load more. - * - * @param cardName the name to find; if null, load all cards. - * - * @return the Card or null if it was not found. - protected final Card loadCardsUntilYouFind(final String cardName) { - Card result = null; - - // Try to retrieve card loading progress monitor model. - // If no progress monitor present, output results to console. - BaseProgressMonitor monitor = null; - final FView view = Singletons.getView(); - if (view != null) { - monitor = view.getCardLoadingProgressMonitor(); - } - - if (monitor == null) { - monitor = new StderrProgressMonitor(1, 0L); - } - - // Iterate through txt files or zip archive. - // Report relevant numbers to progress monitor model. - if (zip == null) { - if (estimatedFilesRemaining == UNKNOWN_NUMBER_OF_FILES_REMAINING) { - final Generator findNonDirsGen = new FindNonDirectoriesSkipDotDirectoriesGenerator(cardsfolder); - estimatedFilesRemaining = GeneratorFunctions.estimateSize(findNonDirsGen); - findNonDirsIterable = YieldUtils.toIterable(findNonDirsGen); + public final void parseLine(final String line) { + if (line.startsWith("Name:")) { + cardName = getValueAfterKey(line, "Name:"); + if (cardName == null || cardName.isEmpty()) { + throw new RuntimeException("Card name is empty"); } - monitor.setTotalUnitsThisPhase(estimatedFilesRemaining); + } else if (line.startsWith("ManaCost:")) { + String sCost = getValueAfterKey(line, "ManaCost:"); + manaCost = "no cost".equals(sCost) ? CardManaCost.empty : new CardManaCost(new ParserCardnameTxtManaCost(sCost)); - for (File cardTxtFile : findNonDirsIterable) { - if (!cardTxtFile.getName().endsWith(CARD_FILE_DOT_EXTENSION)) { - monitor.incrementUnitsCompletedThisPhase(1L); - continue; - } + } else if (line.startsWith("Types:")) { + cardType = CardType.parse(getValueAfterKey(line, "Types:")); - result = loadCard(cardTxtFile); - monitor.incrementUnitsCompletedThisPhase(1L); + } else if (line.startsWith("Oracle:")) { + cardRules = getValueAfterKey(line, "Oracle:").split("\\n"); - if (cardName != null && cardName.equals(result.getName())) { - break; // no thread leak here if entire card DB is loaded, or if this object is finalized. - } + } else if (line.startsWith("PT:")) { + ptLine = getValueAfterKey(line, "PT:"); + } else if (line.startsWith("Loyalty:")) { + ptLine = getValueAfterKey(line, "Loyalty:"); - } //endfor + } else if (line.startsWith("SVar:RemAIDeck:")) { + removedFromAIDecks = "True".equalsIgnoreCase(getValueAfterKey(line, "SVar:RemAIDeck:")); - } else { - monitor.setTotalUnitsThisPhase(estimatedFilesRemaining); - ZipEntry entry; + } else if (line.startsWith("SVar:RemRandomDeck:")) { + removedFromRandomDecks = "True".equalsIgnoreCase(getValueAfterKey(line, "SVar:RemRandomDeck:")); - // zipEnum was initialized in the constructor. - while (zipEnum.hasMoreElements()) { - entry = (ZipEntry) zipEnum.nextElement(); - - if (entry.isDirectory() || !entry.getName().endsWith(CARD_FILE_DOT_EXTENSION)) { - monitor.incrementUnitsCompletedThisPhase(1L); - continue; - } - - result = loadCard(entry); - monitor.incrementUnitsCompletedThisPhase(1L); - - if (cardName != null && cardName.equals(result.getName())) { - break; - } - } - - } //endif - - return result; - } //loadCardsUntilYouFind(String) - */ - - - /** - *

Reads a line from the given reader and handles exceptions.

- * - * @return a {@link java.lang.String} object. - * @param reader a {@link java.io.BufferedReader} object. - */ - public static String readLine(final BufferedReader reader) { - //makes the checked exception, into an unchecked runtime exception - try { - String line = reader.readLine(); - if (line != null) { - line = line.trim(); - } - return line; - } catch (Exception ex) { - ErrorViewer.showError(ex); - throw new RuntimeException("CardReader : readLine(Card) error", ex); // NOPMD by Braids on 8/18/11 10:53 PM - } - } //readLine(BufferedReader) - - /** - *

Load a card from an InputStream.

- * - * @param txtFileLocator describes the location of the txt file we are - * parsing - * - * @param inputStream the stream from which to load the card's information - * - * @return the card loaded from the stream - * - * @throws CardParsingException if there is something wrong with the - * stream's contents - */ - protected final CardRules loadCard(final String txtFileLocator, final InputStream inputStream) - throws CardParsingException - { - int lineNum = 0; - - String cardName = null; - CardType cardType = null; - String manacost = null; - String ptLine = null; - String[] cardRules = null; - Map setsData = new TreeMap(); - boolean removedFromAIDecks = false; - boolean removedFromRandomDecks = false; - - InputStreamReader inputStreamReader = null; - BufferedReader reader = null; - try { - inputStreamReader = new InputStreamReader(inputStream, charset); - reader = new BufferedReader(inputStreamReader); - - while (true) { - String line = readLine(reader); - lineNum++; - - if (line.charAt(0) == '#') { - //no need to do anything, this indicates a comment line - continue; - - } else if (line.startsWith("Name:")) { - cardName = getValueAfterKey(line, "Name:"); - - if (cardName == null || cardName.isEmpty()) { - throw new CardParsingException(txtFileLocator, lineNum, "Card name is empty"); - } - - } else if (line.startsWith("ManaCost:")) { - final String value = getValueAfterKey(line, "ManaCost:"); - - if (!"no cost".equals(value)) { - manacost = value; - } - else { - assert manacost == null; - } - - } else if (line.startsWith("Types:")) { - final String value = getValueAfterKey(line, "Types:"); - - try { - cardType = CardType.parse(value); - } - catch (Throwable exn) { - throw new CardParsingException(txtFileLocator, lineNum, - "In Types: " + exn.getMessage(), exn); - } - - } else if (line.startsWith("Oracle:")) { - final String value = getValueAfterKey(line, "Oracle:"); - cardRules = value.split("\\n"); - - } else if (line.startsWith("PT:")) { - throwCPEIfPTIsNotNull(ptLine, txtFileLocator, lineNum); - ptLine = getValueAfterKey(line, "PT:"); - - } else if (line.startsWith("Loyalty:")) { - throwCPEIfPTIsNotNull(ptLine, txtFileLocator, lineNum); - ptLine = getValueAfterKey(line, "Loyalty:"); - - } else if (line.startsWith("SVar:RemAIDeck:")) { - final String value = getValueAfterKey(line, "SVar:RemAIDeck:"); - removedFromAIDecks = ("True".equalsIgnoreCase(value)); - - } else if (line.startsWith("SVar:RemRandomDeck:")) { - final String value = getValueAfterKey(line, "SVar:RemRandomDeck:"); - removedFromRandomDecks = ("True".equalsIgnoreCase(value)); - - } else if (line.startsWith("SetInfo:")) { - parseSetInfoLine(txtFileLocator, lineNum, line, setsData); - - } else if ("End".equals(line)) { - break; - } - - } // while true - - } finally { - try { - reader.close(); - } catch (IOException ignored) { // NOPMD by Braids on 8/18/11 11:08 PM - } - try { - inputStreamReader.close(); - } catch (IOException ignored) { // NOPMD by Braids on 8/18/11 11:08 PM - } - } - - try { - return new CardRules(cardName, cardType, manacost, ptLine, cardRules, setsData, removedFromRandomDecks, - removedFromAIDecks); - } - catch (Throwable exn) { - throw new CardParsingException(txtFileLocator, lineNum, - "Error constructing CardRules instance: " + exn.toString(), - exn); + } else if (line.startsWith("SetInfo:")) { + parseSetInfoLine(line, setsData); } } @@ -390,9 +100,7 @@ public class CardRulesReader * * @throws CardParsingException if there is a problem parsing the line */ - public static void parseSetInfoLine(final String txtFileLocator, final int lineNum, final String line, - final Map setsData) - throws CardParsingException + public static void parseSetInfoLine(final String line, final Map setsData) { final int setCodeIx = 0; final int rarityIx = 1; @@ -405,7 +113,7 @@ public class CardRulesReader final String[] pieces = value.split("\\|"); if (pieces.length <= rarityIx) { - throw new CardParsingException(txtFileLocator, lineNum, + throw new RuntimeException( "SetInfo line <<" + value + ">> has insufficient pieces"); } @@ -415,7 +123,7 @@ public class CardRulesReader int numIllustrations = 1; if (setsData.containsKey(setCode)) { - throw new CardParsingException(txtFileLocator, lineNum, + throw new RuntimeException( "Found multiple SetInfo lines for set code <<" + setCode + ">>"); } @@ -424,13 +132,13 @@ public class CardRulesReader numIllustrations = Integer.parseInt(pieces[numPicIx]); } catch (NumberFormatException nfe) { - throw new CardParsingException(txtFileLocator, lineNum, + throw new RuntimeException( "Fourth item of SetInfo is not an integer in <<" + value + ">>"); } if (numIllustrations < 1) { - throw new CardParsingException(txtFileLocator, lineNum, + throw new RuntimeException( "Fourth item of SetInfo is not a positive integer, but" + numIllustrations); } @@ -456,8 +164,7 @@ public class CardRulesReader rarity = CardRarity.Special; } else { - throw new CardParsingException(txtFileLocator, lineNum, - "Unrecognized rarity string <<" + txtRarity + ">>"); + throw new RuntimeException("Unrecognized rarity string <<" + txtRarity + ">>"); } CardInSet cardInSet = new CardInSet(rarity, numIllustrations); @@ -465,37 +172,6 @@ public class CardRulesReader setsData.put(setCode, cardInSet); } - /** - * Test if ptLine is null; if it is not, throw a CardParsingException. - * - * @param ptLine the previously seen power/toughness or loyalty value, if any - * @param txtFileLocator describes location of the card's txt file - * @param lineNum the line number just read - * - * @throws CardParsingException iff ptLine is not null - */ - public static void throwCPEIfPTIsNotNull(final String ptLine, final String txtFileLocator, final int lineNum) - throws CardParsingException - { - if (ptLine != null) { - throw new CardParsingException(txtFileLocator, lineNum, - "more than one PT or Loyalty is present"); - } - } - - /** - * Parse the value from a card.txt line. - * - * Throws {@link IndexOutOfBoundsException} if fieldNameWithColon is not in line. - * - * @param line the raw line; may have newline at end - * - * @param fieldNameWithColon the field name with its colon, used to - * identify the key - * - * @return the value after the colon, with its leading and trailing - * whitespace removed - */ public static String getValueAfterKey(final String line, final String fieldNameWithColon) { final int startIx = fieldNameWithColon.length(); final String lineAfterColon = line.substring(startIx); @@ -503,137 +179,62 @@ public class CardRulesReader } - /** - * Set the character encoding to use when loading cards. - * - * @param charsetName the name of the charset, for example, "UTF-8" - */ - public final void setEncoding(final String charsetName) { - this.charset = Charset.forName(charsetName); - } + + public static class ParserCardnameTxtManaCost implements ManaParser { + private final String[] cost; + private int nextToken; + private int colorlessCost; - /** - * Load a card from a txt file. - * - * @param pathToTxtFile the full or relative path to the file to load - * - * @return a new Card instance - protected final Card loadCard(final File pathToTxtFile) { - FileInputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(pathToTxtFile); - return loadCard(fileInputStream); - } catch (FileNotFoundException ex) { - ErrorViewer.showError(ex, "File \"%s\" exception", pathToTxtFile.getAbsolutePath()); - throw new RuntimeException(// NOPMD by Braids on 8/18/11 10:53 PM - "CardReader : run error -- file exception -- filename is " - + pathToTxtFile.getPath(), ex); - } finally { - try { - fileInputStream.close(); - } catch (IOException ignored) { // NOPMD by Braids on 8/18/11 11:08 PM - } + public ParserCardnameTxtManaCost(final String cost) { + this.cost = cost.split(" "); + // System.out.println(cost); + nextToken = 0; + colorlessCost = 0; } - } - */ - /** - * Load a card from an entry in a zip file. - * - * @param entry to load from - * - * @return a new Card instance - protected final Card loadCard(final ZipEntry entry) { - InputStream zipInputStream = null; - try { - zipInputStream = zip.getInputStream(entry); - return loadCard(zipInputStream); + public int getTotalColorlessCost() { + if ( hasNext() ) { + throw new RuntimeException("Colorless cost should be obtained after iteration is complete"); + } + return colorlessCost; + } - } catch (IOException exn) { - throw new RuntimeException(exn); // NOPMD by Braids on 8/18/11 10:53 PM - } finally { - try { - if (zipInputStream != null) { - zipInputStream.close(); + @Override + public boolean hasNext() { return nextToken < cost.length; } + + @Override + public CardManaCostShard next() { + + String unparsed = cost[nextToken++];; + + // System.out.println(unparsed); + if (StringUtils.isNumeric(unparsed)) { + colorlessCost += Integer.parseInt(unparsed); + return null; + } + + int atoms = 0; + for (int iChar = 0; iChar < unparsed.length(); iChar++) { + switch (unparsed.charAt(iChar)) { + case 'W': atoms |= CardManaCostShard.Atom.WHITE; break; + case 'U': atoms |= CardManaCostShard.Atom.BLUE; break; + case 'B': atoms |= CardManaCostShard.Atom.BLACK; break; + case 'R': atoms |= CardManaCostShard.Atom.RED; break; + case 'G': atoms |= CardManaCostShard.Atom.GREEN; break; + case '2': atoms |= CardManaCostShard.Atom.OR_2_COLORLESS; break; + case 'P': atoms |= CardManaCostShard.Atom.OR_2_LIFE; break; + case 'X': atoms |= CardManaCostShard.Atom.IS_X; break; + default: break; } - } catch (IOException ignored) { // NOPMD by Braids on 8/18/11 11:08 PM - } - } - } - */ - - - /** - * Attempt to guess what the path to a given card's txt file would be. - * - * @param asciiCardName the card name in canonicalized ASCII form - * - * @return the likeliest path of the card's txt file, excluding - * cardsFolder but including the subdirectory of that and the ".txt" - * suffix. For example, "e/elvish_warrior.txt" - * - * @see CardUtil#canonicalizeCardName - */ - public final String toMostLikelyPath(final String asciiCardName) { - String baseFileName = asciiCardName; - - /* - * friarsol wrote: "hyphens and spaces are converted to underscores, - * commas and apostrophes are removed (I'm not sure if there are any - * other punctuation used)." - * - * @see http://www.slightlymagic.net/forum/viewtopic.php?f=52&t=4887#p63189 - */ - - baseFileName = HYPHEN_OR_SPACE.matcher(baseFileName).replaceAll("_"); - baseFileName = MULTIPLE_UNDERSCORES.matcher(baseFileName).replaceAll("_"); - baseFileName = PUNCTUATION_TO_ZAP.matcher(baseFileName).replaceAll(""); - - // Place the file within a single-letter subdirectory. - final StringBuffer buf = new StringBuffer(1 + 1 + baseFileName.length() + CARD_FILE_DOT_EXTENSION.length()); - buf.append(Character.toLowerCase(baseFileName.charAt(0))); - - // Zip file is always created with unix-style path names. - buf.append('/'); - - buf.append(baseFileName.toLowerCase(Locale.ENGLISH)); - buf.append(CARD_FILE_DOT_EXTENSION); - - return buf.toString(); - } - - /** - * Attempt to load a card by its canonical ASCII name. - * - * @param canonicalASCIIName the canonical ASCII name of the card - * - * @return a new Card instance having that name, or null if not found - public final Card findCard(final String canonicalASCIIName) { // NOPMD by Braids on 8/18/11 11:08 PM - UtilFunctions.checkNotNull("canonicalASCIIName", canonicalASCIIName); - - final String cardFilePath = toMostLikelyPath(canonicalASCIIName); - - Card result = null; - - if (zip != null) { - final ZipEntry entry = zip.getEntry(cardFilePath); - - if (entry != null) { - result = loadCard(entry); } + return CardManaCostShard.valueOf(atoms); } - if (result == null) { - result = loadCard(new File(cardsfolder, cardFilePath)); - } + @Override + public void remove() { } // unsuported + } - if (result == null || !(result.getName().equals(canonicalASCIIName))) { - //System.err.println(":Could not find \"" + cardFilePath + "\"."); - result = loadCardsUntilYouFind(canonicalASCIIName); - } - return result; - } - */ } + diff --git a/src/main/java/forge/card/CardSet.java b/src/main/java/forge/card/CardSet.java index bf99f678b5d..fed03350a8d 100644 --- a/src/main/java/forge/card/CardSet.java +++ b/src/main/java/forge/card/CardSet.java @@ -1,5 +1,7 @@ package forge.card; +import net.slightlymagic.braids.util.lambda.Lambda1; + /** *

CardSet class.

* @@ -24,6 +26,12 @@ public final class CardSet implements Comparable { // immutable public String getCode2() { return code2; } public int getIndex() { return index; } + + public static final Lambda1 fnGetName = new Lambda1() { + @Override public String apply(final CardSet arg1) { return arg1.name; } }; + public static final Lambda1 fn1 = new Lambda1() { + @Override public CardSet apply(final CardSet arg1) { return arg1; } }; + @Override public int compareTo(final CardSet o) { if (o == null) { return 1; } diff --git a/src/main/java/forge/card/MtgDataParser.java b/src/main/java/forge/card/MtgDataParser.java index a9b5d172438..04472817d1d 100644 --- a/src/main/java/forge/card/MtgDataParser.java +++ b/src/main/java/forge/card/MtgDataParser.java @@ -9,6 +9,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; import forge.FileUtil; +import forge.card.CardManaCost.ManaParser; import forge.properties.ForgeProps; import forge.properties.NewConstants; @@ -84,9 +85,12 @@ public final class MtgDataParser implements Iterator { String name = it.next(); if (!it.hasNext()) { weHaveNext = false; return null; } + String manaCost = it.next(); + CardManaCost cost = CardManaCost.empty; CardType type = null; if (manaCost.startsWith("{")) { + cost = new CardManaCost(new ManaParserMtgData(manaCost)); if (!it.hasNext()) { weHaveNext = false; return null; } type = CardType.parse(it.next()); } else { // Land? @@ -114,7 +118,7 @@ public final class MtgDataParser implements Iterator { if (sets.isEmpty()) { return null; } // that was a bad card - it won't be added by invoker - return new CardRules(name, type, manaCost, ptOrLoyalty, strs.toArray(emptyArray), sets, + return new CardRules(name, type, cost, ptOrLoyalty, strs.toArray(emptyArray), sets, // TODO: fix last two parameters false, false); } @@ -158,5 +162,64 @@ public final class MtgDataParser implements Iterator { @Override public void remove() { } + + + public static class ManaParserMtgData implements ManaParser { + private final String cost; + + private int nextBracket; + private int colorlessCost; + + + public ManaParserMtgData(final String cost) { + this.cost = cost; + // System.out.println(cost); + nextBracket = cost.indexOf('{'); + colorlessCost = 0; + } + + public int getTotalColorlessCost() { + if ( hasNext() ) { + throw new RuntimeException("Colorless cost should be obtained after iteration is complete"); + } + return colorlessCost; + } + + @Override + public boolean hasNext() { return nextBracket != -1; } + + @Override + public CardManaCostShard next() { + int closeBracket = cost.indexOf('}', nextBracket); + String unparsed = cost.substring(nextBracket + 1, closeBracket); + nextBracket = cost.indexOf('{', closeBracket + 1); + + // System.out.println(unparsed); + if (StringUtils.isNumeric(unparsed)) { + colorlessCost += Integer.parseInt(unparsed); + return null; + } + + int atoms = 0; + for (int iChar = 0; iChar < unparsed.length(); iChar++) { + switch (unparsed.charAt(iChar)) { + case 'W': atoms |= CardManaCostShard.Atom.WHITE; break; + case 'U': atoms |= CardManaCostShard.Atom.BLUE; break; + case 'B': atoms |= CardManaCostShard.Atom.BLACK; break; + case 'R': atoms |= CardManaCostShard.Atom.RED; break; + case 'G': atoms |= CardManaCostShard.Atom.GREEN; break; + case '2': atoms |= CardManaCostShard.Atom.OR_2_COLORLESS; break; + case 'P': atoms |= CardManaCostShard.Atom.OR_2_LIFE; break; + case 'X': atoms |= CardManaCostShard.Atom.IS_X; break; + default: break; + } + } + return CardManaCostShard.valueOf(atoms); + } + + @Override + public void remove() { } // unsuported + } + } diff --git a/src/main/java/forge/card/cardFactory/PreloadingCardFactory.java b/src/main/java/forge/card/cardFactory/PreloadingCardFactory.java index c6f00870940..3ca5589320e 100644 --- a/src/main/java/forge/card/cardFactory/PreloadingCardFactory.java +++ b/src/main/java/forge/card/cardFactory/PreloadingCardFactory.java @@ -2,11 +2,15 @@ package forge.card.cardFactory; import java.io.File; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import forge.AllZone; import forge.Card; import forge.CardReader; +import forge.card.CardDb; +import forge.card.CardRules; import forge.error.ErrorViewer; import forge.properties.ForgeProps; @@ -69,10 +73,13 @@ public class PreloadingCardFactory extends AbstractCardFactory { protected void readCards(File file) { getMap().clear(); - CardReader read = new CardReader(ForgeProps.getFile(CARDSFOLDER), getMap()); + List listCardRules = new ArrayList(); + CardReader read = new CardReader(ForgeProps.getFile(CARDSFOLDER), getMap(), listCardRules); + // this fills in our map of card names to Card instances. read.run(); + CardDb.setup(listCardRules.iterator()); }// readCard() }