diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 182be367ebd..ad61e89d849 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -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"; } } diff --git a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java index a16a7bbccd0..5d4e19e7642 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java @@ -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) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index c47059ed798..e4ad14fbaef 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -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) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index fef9bdab407..bba0ef95006 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -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; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index 42541329f91..57ced4260ea 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -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() { diff --git a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java index d18df6ef694..99d0dae1098 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablePlayerPanel.java @@ -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; } diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 7bbd6816add..7652a5040b5 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -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); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 4c18ad006f2..68a3ea860d7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -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); diff --git a/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java b/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java new file mode 100644 index 00000000000..a151caf01a8 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java @@ -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 lookupCardInfo(String name) { + return Optional.ofNullable(CardRepository.instance.findPreferedCoreExpansionCard(name, true)); + } + +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java new file mode 100644 index 00000000000..dedffadcf58 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java @@ -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 mainCards = getNodes(doc, "/cockatrice_deck/zone[@name='main']/card"); + decklist.setCards(mainCards.stream() + .flatMap(toDeckCardInfo(getCardLookup(), errorMessages)) + .collect(Collectors.toList())); + + List 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> toDeckCardInfo(CardLookup lookup, StringBuilder errors) { + return node -> { + String name = node.getAttributes().getNamedItem("name").getNodeValue().trim(); + Optional 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 getNodes(Document doc, String xpathExpression) throws XPathExpressionException { + NodeList nodes = (NodeList) xpathFactory.newXPath().evaluate(xpathExpression, doc, NODESET); + ArrayList 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); + } + } + +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java index fee59b3b4d6..787e3cfe5ac 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DckDeckImporter.java @@ -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*$"); diff --git a/Mage/src/main/java/mage/cards/decks/importer/DecDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DecDeckImporter.java index cf2c83a0c55..5ab07af1de4 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DecDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DecDeckImporter.java @@ -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 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())); diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java index 1957cd7058e..81868e427ff 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java @@ -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); } diff --git a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java deleted file mode 100644 index 0384a17f44c..00000000000 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporterUtil.java +++ /dev/null @@ -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); - } -} diff --git a/Mage/src/main/java/mage/cards/decks/importer/DekDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DekDeckImporter.java index 7e6efc577a6..2672ff9d3ef 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DekDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DekDeckImporter.java @@ -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) { diff --git a/Mage/src/main/java/mage/cards/decks/importer/MWSDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/MWSDeckImporter.java index bb5c0662186..fd541aa1a51 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/MWSDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/MWSDeckImporter.java @@ -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) { diff --git a/Mage/src/main/java/mage/cards/decks/importer/PlainTextDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/PlainTextDeckImporter.java new file mode 100644 index 00000000000..c9d99d3aaee --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/PlainTextDeckImporter.java @@ -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); +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java index 4d593610ff5..a2b5706eb8b 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/TxtDeckImporter.java @@ -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"}; diff --git a/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java new file mode 100644 index 00000000000..77256b69cf8 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java @@ -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()); + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java new file mode 100644 index 00000000000..613a8f6c14d --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java @@ -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 lookupCardInfo(String name) { + System.out.println(name); + return super.lookupCardInfo(name); + } + }; + return lookup; + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java b/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java new file mode 100644 index 00000000000..4db37db6d75 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java @@ -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 lookup = new HashMap<>(); + + public FakeCardLookup addCard(String cardName) { + lookup.put(cardName, new CardInfo() {{ + name = cardName; + }}); + return this; + } + + @Override + public Optional lookupCardInfo(String name) { + CardInfo card = lookup.get(name); + if (card == null) { + System.out.println("Couldn't find: " + name); + } + return Optional.ofNullable(card); + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/TestDeckChecker.java b/Mage/src/test/java/mage/cards/decks/importer/TestDeckChecker.java new file mode 100644 index 00000000000..e4f25432391 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/TestDeckChecker.java @@ -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 main = new ArrayList<>(); + private final List 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(); + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/testdeck.cod b/Mage/src/test/java/mage/cards/decks/importer/testdeck.cod new file mode 100644 index 00000000000..c90d63f6767 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/testdeck.cod @@ -0,0 +1,14 @@ + + + Deck Name + Some comments in here. + + + + + + + + + + \ No newline at end of file diff --git a/Mage/src/test/java/mage/cards/decks/importer/testdeck.dec b/Mage/src/test/java/mage/cards/decks/importer/testdeck.dec new file mode 100644 index 00000000000..80abb81381d --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/testdeck.dec @@ -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 \ No newline at end of file