Merge pull request #5501 from hitch17/add-cockatrice-deck-format-5493

UI: add cockatrice deck format support for import (*.cod)
This commit is contained in:
Oleg Agafonov 2019-01-11 07:17:48 +04:00 committed by GitHub
commit 11f93cf762
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 584 additions and 162 deletions

View file

@ -14,7 +14,6 @@ import mage.cards.Sets;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.importer.DeckImporter;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
@ -798,7 +797,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
newDeck = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath(), errorMessages), true, true);
newDeck = Deck.load(DeckImporter.importDeckFromFile(dialog.getTmpPath(), errorMessages), true, true);
processAndShowImportErrors(errorMessages);
if (newDeck != null) {
@ -831,7 +830,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
deckToAppend = Deck.load(DeckImporterUtil.importDeck(dialog.getTmpPath(), errorMessages), true, true);
deckToAppend = Deck.load(DeckImporter.importDeckFromFile(dialog.getTmpPath(), errorMessages), true, true);
processAndShowImportErrors(errorMessages);
if (deckToAppend != null) {
@ -878,7 +877,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
Deck newDeck = null;
StringBuilder errorMessages = new StringBuilder();
newDeck = Deck.load(DeckImporterUtil.importDeck(file.getPath(), errorMessages), true, true);
newDeck = Deck.load(DeckImporter.importDeckFromFile(file.getPath(), errorMessages), true, true);
processAndShowImportErrors(errorMessages);
if (newDeck != null) {
@ -977,7 +976,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
File file = fcImportDeck.getSelectedFile();
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
DeckImporter importer = DeckImporterUtil.getDeckImporter(file.getPath());
DeckImporter importer = DeckImporter.getDeckImporter(file.getPath());
if (importer != null) {
StringBuilder errorMessages = new StringBuilder();
@ -1048,7 +1047,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
try {
MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
String path = DeckGenerator.generateDeck();
deck = Deck.load(DeckImporterUtil.importDeck(path), true, true);
deck = Deck.load(DeckImporter.importDeckFromFile(path), true, true);
} catch (GameException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading generated deck", JOptionPane.ERROR_MESSAGE);
} catch (DeckGeneratorException ex) {
@ -1120,7 +1119,11 @@ class ImportFilter extends FileFilter {
ext = s.substring(i + 1).toLowerCase(Locale.ENGLISH);
}
if (ext != null) {
if (ext.toLowerCase(Locale.ENGLISH).equals("dec") || ext.toLowerCase(Locale.ENGLISH).equals("mwdeck") || ext.toLowerCase(Locale.ENGLISH).equals("txt") || ext.toLowerCase(Locale.ENGLISH).equals("dek")) {
if (ext.toLowerCase(Locale.ENGLISH).equals("dec")
|| ext.toLowerCase(Locale.ENGLISH).equals("mwdeck")
|| ext.toLowerCase(Locale.ENGLISH).equals("txt")
|| ext.toLowerCase(Locale.ENGLISH).equals("dek")
|| ext.toLowerCase(Locale.ENGLISH).equals("cod")) {
return true;
}
}
@ -1129,7 +1132,7 @@ class ImportFilter extends FileFilter {
@Override
public String getDescription() {
return "*.dec | *.mwDeck | *.txt | *.dek";
return "*.dec | *.mwDeck | *.txt | *.dek | *.cod";
}
}

View file

@ -1,7 +1,7 @@
package mage.client.dialog;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.players.PlayerType;
@ -119,9 +119,9 @@ public class JoinTableDialog extends MageDialog {
try {
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, txtPassword.getText());
if (isTournament) {
joined = session.joinTournamentTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), PlayerType.HUMAN, 1, DeckImporterUtil.importDeck(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText());
joined = session.joinTournamentTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), PlayerType.HUMAN, 1, DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText());
} else {
joined = session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), PlayerType.HUMAN, 1, DeckImporterUtil.importDeck(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText());
joined = session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), PlayerType.HUMAN, 1, DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText());
}
} catch (Exception ex) {

View file

@ -7,7 +7,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.swing.*;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.components.MageComponents;
@ -425,7 +426,7 @@ public class NewTableDialog extends MageDialog {
table.getTableId(),
this.player1Panel.getPlayerName(),
PlayerType.HUMAN, 1,
DeckImporterUtil.importDeck(this.player1Panel.getDeckFile()),
DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile()),
this.txtPassword.getText())) {
for (TablePlayerPanel player : players) {
if (player.getPlayerType() != PlayerType.HUMAN) {

View file

@ -18,7 +18,7 @@ import java.util.UUID;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import mage.cards.decks.Deck;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.cards.repository.ExpansionInfo;
import mage.cards.repository.ExpansionRepository;
import mage.client.MageFrame;
@ -557,7 +557,7 @@ public class NewTournamentDialog extends MageDialog {
if (!(cubeFromDeckFilename.isEmpty())) {
Deck cubeFromDeck = new Deck();
try {
cubeFromDeck = Deck.load(DeckImporterUtil.importDeck(cubeFromDeckFilename), true, true);
cubeFromDeck = Deck.load(DeckImporter.importDeckFromFile(cubeFromDeckFilename), true, true);
} catch (GameException e1) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), e1.getMessage(), "Error loading deck", JOptionPane.ERROR_MESSAGE);
}
@ -631,11 +631,11 @@ public class NewTournamentDialog extends MageDialog {
table.getTableId(),
this.player1Panel.getPlayerName(),
PlayerType.HUMAN, 1,
DeckImporterUtil.importDeck(this.player1Panel.getDeckFile()),
DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile()),
tOptions.getPassword())) {
for (TournamentPlayerPanel player : players) {
if (player.getPlayerType().getSelectedItem() != PlayerType.HUMAN) {
if (!player.joinTournamentTable(roomId, table.getTableId(), DeckImporterUtil.importDeck(this.player1Panel.getDeckFile()))) {
if (!player.joinTournamentTable(roomId, table.getTableId(), DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile()))) {
// error message must be send by sever
SessionHandler.removeTable(roomId, table.getTableId());
table = null;

View file

@ -20,7 +20,8 @@ import javax.swing.JPopupMenu;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.MenuSelectionManager;
import javax.swing.event.ChangeListener;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.cards.BigCard;
@ -569,7 +570,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
}
private void btnCheatActionPerformed(java.awt.event.ActionEvent evt) {
SessionHandler.cheat(gameId, playerId, DeckImporterUtil.importDeck("cheat.dck"));
SessionHandler.cheat(gameId, playerId, DeckImporter.importDeckFromFile("cheat.dck"));
}
public boolean isSmallMode() {

View file

@ -8,7 +8,7 @@
package mage.client.table;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.client.SessionHandler;
import mage.client.util.Config;
import mage.client.util.Event;
@ -53,7 +53,7 @@ public class TablePlayerPanel extends javax.swing.JPanel {
public boolean joinTable(UUID roomId, UUID tableId) throws IOException, ClassNotFoundException {
if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) {
return SessionHandler.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), (PlayerType) this.cbPlayerType.getSelectedItem(), this.newPlayerPanel.getLevel(), DeckImporterUtil.importDeck(this.newPlayerPanel.getDeckFile()), "");
return SessionHandler.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), (PlayerType) this.cbPlayerType.getSelectedItem(), this.newPlayerPanel.getLevel(), DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile()), "");
}
return true;
}

View file

@ -1,6 +1,6 @@
package mage.client.table;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.chat.ChatPanelBasic;
@ -1263,8 +1263,8 @@ public class TablesPanel extends javax.swing.JPanel {
options.setBannedUsers(IgnoreList.ignoreList(serverAddress));
table = SessionHandler.createTable(roomId, options);
SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, DeckImporterUtil.importDeck("test.dck"), "");
SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, DeckImporterUtil.importDeck("test.dck"), "");
SessionHandler.joinTable(roomId, table.getTableId(), "Human", PlayerType.HUMAN, 1, DeckImporter.importDeckFromFile("test.dck"), "");
SessionHandler.joinTable(roomId, table.getTableId(), "Computer", PlayerType.COMPUTER_MAD, 5, DeckImporter.importDeckFromFile("test.dck"), "");
SessionHandler.startMatch(roomId, table.getTableId());
} catch (HeadlessException ex) {
handleError(ex);

View file

@ -6,7 +6,7 @@ import mage.abilities.Ability;
import mage.cards.Card;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.importer.DeckImporterUtil;
import mage.cards.decks.importer.DeckImporter;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.cards.repository.CardScanner;
@ -189,7 +189,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
if (loadedDeckCardLists.containsKey(deckName)) {
list = loadedDeckCardLists.get(deckName);
} else {
list = DeckImporterUtil.importDeck(deckName);
list = DeckImporter.importDeckFromFile(deckName);
loadedDeckCardLists.put(deckName, list);
}
Deck deck = Deck.load(list, false, false);

View file

@ -0,0 +1,16 @@
package mage.cards.decks.importer;
import java.util.Optional;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
public class CardLookup {
public static final CardLookup instance = new CardLookup();
public Optional<CardInfo> lookupCardInfo(String name) {
return Optional.ofNullable(CardRepository.instance.findPreferedCoreExpansionCard(name, true));
}
}

View file

@ -0,0 +1,117 @@
package mage.cards.decks.importer;
import static javax.xml.xpath.XPathConstants.NODESET;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
public class CodDeckImporter extends DeckImporter {
private XPathFactory xpathFactory = XPathFactory.newInstance();
private DocumentBuilder builder = getDocumentBuilder();
@Override
public DeckCardLists importDeck(String filename, StringBuilder errorMessages) {
try {
Document doc = getXmlDocument(filename);
DeckCardLists decklist = new DeckCardLists();
List<Node> mainCards = getNodes(doc, "/cockatrice_deck/zone[@name='main']/card");
decklist.setCards(mainCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
List<Node> sideboardCards = getNodes(doc, "/cockatrice_deck/zone[@name='side']/card");
decklist.setSideboard(sideboardCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
getNodes(doc, "/cockatrice_deck/deckname")
.forEach(n -> decklist.setName(n.getTextContent().trim()));
return decklist;
} catch (Exception e) {
logger.error("Error loading deck", e);
errorMessages.append("There was an error loading the deck.");
return new DeckCardLists();
}
}
private static int getQuantityFromNode(Node node) {
Node numberNode = node.getAttributes().getNamedItem("number");
if (numberNode == null) {
return 1;
}
try {
return Math.min(100, Math.max(1, Integer.parseInt(numberNode.getNodeValue())));
} catch (NumberFormatException e) {
return 1;
}
}
private static Function<Node, Stream<DeckCardInfo>> toDeckCardInfo(CardLookup lookup, StringBuilder errors) {
return node -> {
String name = node.getAttributes().getNamedItem("name").getNodeValue().trim();
Optional<CardInfo> cardInfo = lookup.lookupCardInfo(name);
if (cardInfo.isPresent()) {
CardInfo info = cardInfo.get();
return Collections.nCopies(
getQuantityFromNode(node),
new DeckCardInfo(info.getName(), info.getCardNumber(), info.getSetCode())).stream();
} else {
errors.append("Could not find card: '").append(name).append("'\n");
return Stream.empty();
}
};
}
private List<Node> getNodes(Document doc, String xpathExpression) throws XPathExpressionException {
NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET);
ArrayList<Node> list = new ArrayList<>();
for (int i = 0; i < nodes.getLength(); i++) {
list.add(nodes.item(i));
}
return list;
}
private DocumentBuilder getDocumentBuilder() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
return factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new RuntimeException();
}
}
protected Document getXmlDocument(String filename) throws IOException {
try {
return builder.parse(new File(filename));
} catch (SAXException e) {
throw new IOException(e);
}
}
}

View file

@ -15,7 +15,7 @@ import mage.cards.repository.CardRepository;
*
* @author North
*/
public class DckDeckImporter extends DeckImporter {
public class DckDeckImporter extends PlainTextDeckImporter {
private static final Pattern pattern = Pattern.compile("(SB:)?\\s*(\\d*)\\s*\\[([^]:]+):([^]:]+)\\]\\s*(.*)\\s*$");

View file

@ -1,6 +1,8 @@
package mage.cards.decks.importer;
import java.util.Optional;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
@ -10,7 +12,7 @@ import mage.cards.repository.CardRepository;
*
* @author BetaSteward_at_googlemail.com
*/
public class DecDeckImporter extends DeckImporter {
public class DecDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList) {
@ -29,10 +31,11 @@ public class DecDeckImporter extends DeckImporter {
String lineName = line.substring(delim).trim();
try {
int num = Integer.parseInt(lineNum);
CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(lineName, true);
if (cardInfo == null) {
Optional<CardInfo> cardLookup = getCardLookup().lookupCardInfo(lineName);
if (!cardLookup.isPresent()) {
sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append('\n');
} else {
CardInfo cardInfo = cardLookup.get();
for (int i = 0; i < num; i++) {
if (!sideboard) {
deckList.getCards().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode()));

View file

@ -1,74 +1,87 @@
package mage.cards.decks.importer;
import mage.cards.decks.DeckCardLists;
import org.apache.log4j.Logger;
import java.io.File;
import java.util.Locale;
import java.util.Optional;
import java.util.Scanner;
/**
*
* @author BetaSteward_at_googlemail.com
*/
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
public abstract class DeckImporter {
private static final Logger logger = Logger.getLogger(DeckImporter.class);
protected static final Logger logger = Logger.getLogger(DeckImporter.class);
protected StringBuilder sbMessage = new StringBuilder(); //TODO we should stop using this not garbage collectable StringBuilder. It just bloats
protected int lineCount;
private static final String[] SIDEBOARD_MARKS = new String[]{"//sideboard", "sb: "};
public static DeckImporter getDeckImporter(String file) {
if (file == null) {
return null;
} if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) {
return new DecDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("mwdeck")) {
return new MWSDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("txt")) {
return new TxtDeckImporter(haveSideboardSection(file));
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("dck")) {
return new DckDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("dek")) {
return new DekDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("cod")) {
return new CodDeckImporter();
} else {
return null;
}
}
/**
*
* @param file file to import
* @param errorMessages you can setup output messages to showup to user (set null for fatal exception on messages.count > 0)
* @return decks list
*/
public DeckCardLists importDeck(String file, StringBuilder errorMessages) {
File f = new File(file);
DeckCardLists deckList = new DeckCardLists();
if (!f.exists()) {
logger.warn("Deckfile " + file + " not found.");
return deckList;
public static DeckCardLists importDeckFromFile(String file) {
return importDeckFromFile(file, new StringBuilder());
}
public static DeckCardLists importDeckFromFile(String file, StringBuilder errorMessages) {
DeckImporter deckImporter = getDeckImporter(file);
if (deckImporter != null) {
return deckImporter.importDeck(file, errorMessages);
} else {
return new DeckCardLists();
}
}
public abstract DeckCardLists importDeck(String file, StringBuilder errorMessages);
public DeckCardLists importDeck(String file) {
return importDeck(file, new StringBuilder());
}
public CardLookup getCardLookup() {
return CardLookup.instance;
}
private static boolean haveSideboardSection(String file) {
// search for sideboard section:
// or //sideboard
// or SB: 1 card name -- special deckstats.net
File f = new File(file);
try (Scanner scanner = new Scanner(f)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim().toLowerCase(Locale.ENGLISH);
for (String mark : SIDEBOARD_MARKS) {
if (line.startsWith(mark)) {
return true;
}
}
lineCount = 0;
sbMessage.setLength(0);
try {
try (Scanner scanner = new Scanner(f)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
lineCount++;
readLine(line, deckList);
}
if (sbMessage.length() > 0) {
if(errorMessages != null) {
// normal output for user
errorMessages.append(sbMessage);
}else{
// fatal error
logger.fatal(sbMessage);
}
}
} catch (Exception ex) {
logger.fatal(null, ex);
}
} catch (Exception ex) {
logger.fatal(null, ex);
}
return deckList;
}
} catch (Exception e) {
// ignore error, deckimporter will process it
}
public DeckCardLists importDeck(String file) {
return importDeck(file, null);
}
// not found
return false;
}
public String getErrors(){
return sbMessage.toString();
}
protected abstract void readLine(String line, DeckCardLists deckList);
}

View file

@ -1,71 +0,0 @@
package mage.cards.decks.importer;
import java.io.File;
import java.util.Locale;
import java.util.Scanner;
import mage.cards.decks.DeckCardLists;
/**
*
* @author North
*/
public final class DeckImporterUtil {
private static final String[] SIDEBOARD_MARKS = new String[]{"//sideboard", "sb: "};
public static boolean haveSideboardSection(String file) {
// search for sideboard section:
// or //sideboard
// or SB: 1 card name -- special deckstats.net
File f = new File(file);
try (Scanner scanner = new Scanner(f)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim().toLowerCase(Locale.ENGLISH);
for (String mark : SIDEBOARD_MARKS) {
if (line.startsWith(mark)) {
return true;
}
}
}
} catch (Exception e) {
// ignore error, deckimporter will process it
}
// not found
return false;
}
public static DeckImporter getDeckImporter(String file) {
if (file == null) {
return null;
} if (file.toLowerCase(Locale.ENGLISH).endsWith("dec")) {
return new DecDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("mwdeck")) {
return new MWSDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("txt")) {
return new TxtDeckImporter(haveSideboardSection(file));
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("dck")) {
return new DckDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("dek")) {
return new DekDeckImporter();
} else {
return null;
}
}
public static DeckCardLists importDeck(String file, StringBuilder errorMessages) {
DeckImporter deckImporter = getDeckImporter(file);
if (deckImporter != null) {
return deckImporter.importDeck(file, errorMessages);
} else {
return new DeckCardLists();
}
}
public static DeckCardLists importDeck(String file) {
return importDeck(file, null);
}
}

View file

@ -8,7 +8,7 @@ import mage.cards.repository.CardRepository;
/**
* Created by royk on 11-Sep-16.
*/
public class DekDeckImporter extends DeckImporter {
public class DekDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList) {

View file

@ -14,7 +14,7 @@ import mage.util.RandomUtil;
*
* @author BetaSteward_at_googlemail.com
*/
public class MWSDeckImporter extends DeckImporter {
public class MWSDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList) {

View file

@ -0,0 +1,67 @@
package mage.cards.decks.importer;
import java.io.File;
import java.util.Scanner;
import mage.cards.decks.DeckCardLists;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class PlainTextDeckImporter extends DeckImporter {
protected StringBuilder sbMessage = new StringBuilder(); //TODO we should stop using this not garbage collectable StringBuilder. It just bloats
protected int lineCount;
/**
*
* @param file file to import
* @param errorMessages you can setup output messages to showup to user (set null for fatal exception on messages.count > 0)
* @return decks list
*/
public DeckCardLists importDeck(String file, StringBuilder errorMessages) {
File f = new File(file);
DeckCardLists deckList = new DeckCardLists();
if (!f.exists()) {
logger.warn("Deckfile " + file + " not found.");
return deckList;
}
lineCount = 0;
sbMessage.setLength(0);
try {
try (Scanner scanner = new Scanner(f)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
lineCount++;
readLine(line, deckList);
}
if (sbMessage.length() > 0) {
if(errorMessages != null) {
// normal output for user
errorMessages.append(sbMessage);
}else{
// fatal error
logger.fatal(sbMessage);
}
}
} catch (Exception ex) {
logger.fatal(null, ex);
}
} catch (Exception ex) {
logger.fatal(null, ex);
}
return deckList;
}
public DeckCardLists importDeck(String file) {
return importDeck(file, null);
}
protected abstract void readLine(String line, DeckCardLists deckList);
}

View file

@ -14,7 +14,7 @@ import mage.cards.repository.CardRepository;
*
* @author BetaSteward_at_googlemail.com
*/
public class TxtDeckImporter extends DeckImporter {
public class TxtDeckImporter extends PlainTextDeckImporter {
private static final String[] SET_VALUES = new String[]{"lands", "creatures", "planeswalkers", "other spells", "sideboard cards",
"Instant", "Land", "Enchantment", "Artifact", "Sorcery", "Planeswalker", "Creature"};

View file

@ -0,0 +1,56 @@
package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
public class CodDeckImportTest {
private static final FakeCardLookup LOOKUP = new FakeCardLookup()
.addCard("Forest")
.addCard("Razorverge Thicket")
.addCard("Avacyn's Pilgrim")
.addCard("War Priest of Thune");
@Test
public void testImportCod() {
CodDeckImporter importer = new CodDeckImporter() {
@Override
public CardLookup getCardLookup() {
return LOOKUP;
}
};
StringBuilder errors = new StringBuilder();
DeckCardLists deck = importer.importDeck(
"src/test/java/mage/cards/decks/importer/testdeck.cod", errors);
assertEquals("Deck Name", deck.getName());
TestDeckChecker.checker()
.addMain("Forest", 12)
.addMain("Razorverge Thicket", 100)
.addMain("Avacyn's Pilgrim", 1)
.addSide("War Priest of Thune", 3)
.verify(deck, 113, 3);
assertEquals("Could not find card: '@#$NOT A REAL CARD NAME@#$'\n", errors.toString());
}
private static void assertCardSame(String name, DeckCardInfo card) {
assertEquals(name, card.getCardName());
assertEquals(1, card.getQuantity());
}
}

View file

@ -0,0 +1,91 @@
package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.junit.Test;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
public class DecDeckImportTest {
private static final FakeCardLookup LOOKUP = new FakeCardLookup()
.addCard("Masticore")
.addCard("Metalworker")
.addCard("Phyrexian Colossus")
.addCard("Crumbling Sanctuary")
.addCard("Grim Monolith")
.addCard("Mishra's Helix")
.addCard("Phyrexian Processor")
.addCard("Tangle Wire")
.addCard("Thran Dynamo")
.addCard("Voltaic Key")
.addCard("Tinker")
.addCard("Brainstorm")
.addCard("Crystal Vein")
.addCard("Island")
.addCard("Rishadan Port")
.addCard("Saprazzan Skerry")
.addCard("Annul")
.addCard("Chill")
.addCard("Miscalculation")
.addCard("Mishra's Helix")
.addCard("Rising Waters");
@Test
public void testImport() {
StringBuilder errors = new StringBuilder();
DecDeckImporter importer = new DecDeckImporter() {
@Override
public CardLookup getCardLookup() {
return LOOKUP;
}
};
DeckCardLists deck = importer.importDeck(
"src/test/java/mage/cards/decks/importer/testdeck.dec", errors);
TestDeckChecker.checker()
.addMain("Masticore", 4)
.addMain("Metalworker", 4)
.addMain("Phyrexian Colossus", 1)
.addMain("Crumbling Sanctuary", 1)
.addMain("Grim Monolith", 4)
.addMain("Mishra's Helix", 1)
.addMain("Phyrexian Processor", 4)
.addMain("Tangle Wire", 4)
.addMain("Thran Dynamo", 4)
.addMain("Voltaic Key", 4)
.addMain("Tinker", 4)
.addMain("Brainstorm", 4)
.addMain("Crystal Vein", 4)
.addMain("Island", 9)
.addMain("Rishadan Port", 4)
.addMain("Saprazzan Skerry", 4)
.addSide("Annul", 4)
.addSide("Chill", 4)
.addSide("Miscalculation", 4)
.addSide("Mishra's Helix", 1)
.addSide("Rising Waters", 2)
.verify(deck, 60, 15);
assertEquals("", errors.toString());
}
private static FakeCardLookup getFakeCardLookup() {
FakeCardLookup lookup = new FakeCardLookup() {
@Override
public Optional<CardInfo> lookupCardInfo(String name) {
System.out.println(name);
return super.lookupCardInfo(name);
}
};
return lookup;
}
}

View file

@ -0,0 +1,29 @@
package mage.cards.decks.importer;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import mage.cards.repository.CardInfo;
public class FakeCardLookup extends CardLookup {
private final Map<String, CardInfo> lookup = new HashMap<>();
public FakeCardLookup addCard(String cardName) {
lookup.put(cardName, new CardInfo() {{
name = cardName;
}});
return this;
}
@Override
public Optional<CardInfo> lookupCardInfo(String name) {
CardInfo card = lookup.get(name);
if (card == null) {
System.out.println("Couldn't find: " + name);
}
return Optional.ofNullable(card);
}
}

View file

@ -0,0 +1,54 @@
package mage.cards.decks.importer;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import mage.cards.decks.DeckCardLists;
public class TestDeckChecker {
private final List<String> main = new ArrayList<>();
private final List<String> side = new ArrayList<>();
public TestDeckChecker addMain(String name) {
return addMain(name, 1);
}
public TestDeckChecker addMain(String name, int quantity) {
main.addAll(Collections.nCopies(quantity, name));
return this;
}
public TestDeckChecker addSide(String name) {
return addSide(name, 1);
}
public TestDeckChecker addSide(String name, int quantity) {
side.addAll(Collections.nCopies(quantity, name));
return this;
}
public void verify(DeckCardLists deck, int nMain, int nSide) {
assertEquals(nMain, main.size());
assertEquals(nSide, side.size());
assertEquals(nMain, deck.getCards().size());
assertEquals(nSide, deck.getSideboard().size());
for (int i = 0; i < main.size(); i++) {
String expected = main.get(i);
String actual = deck.getCards().get(i).getCardName();
assertEquals(String.format("Expected: '%s' Actual: '%s' at index: %s",
expected, actual, i), expected, actual);
}
}
public static TestDeckChecker checker() {
return new TestDeckChecker();
}
}

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<cockatrice_deck version="1">
<deckname>Deck Name </deckname>
<comments>Some comments in here.</comments>
<zone name="main">
<card number="12" name="Forest"/>
<card number="101" price="0" name="Razorverge Thicket"/>
<card price="0" name="Avacyn's Pilgrim"/>
<card number="1" price="0" name="@#$NOT A REAL CARD NAME@#$"/>
</zone>
<zone name="side">
<card number="3" price="0" name="War Priest of Thune"/>
</zone>
</cockatrice_deck>

View file

@ -0,0 +1,28 @@
// Name: Mono-Blue Tinker from MTGSalvation.com via mtgtrice mtg-decks
// Creatures
4 Masticore
4 Metalworker
1 Phyrexian Colossus
// Artifacts
1 Crumbling Sanctuary
4 Grim Monolith
1 Mishra's Helix
4 Phyrexian Processor
4 Tangle Wire
4 Thran Dynamo
4 Voltaic Key
// Sorceries
4 Tinker
// Instants
4 Brainstorm
// Lands
4 Crystal Vein
9 Island
4 Rishadan Port
4 Saprazzan Skerry
// Sideboard
SB: 4 Annul
SB: 4 Chill
SB: 4 Miscalculation
SB: 1 Mishra's Helix
SB: 2 Rising Waters