From 5bf748e2b53ccddde67c1a61c5fd016b20e9cd42 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Thu, 10 Jan 2019 09:51:53 -0800 Subject: [PATCH 1/2] Move tests to the Mage directory and stub out looking up cards by names for tests. --- .../decks/importer/CodDeckImportTest.java | 44 ----------- .../decks/importer/DecDeckImportTest.java | 20 ----- .../mage/cards/decks/importer/CardLookup.java | 16 ++++ .../cards/decks/importer/CodDeckImporter.java | 19 ++--- .../cards/decks/importer/DecDeckImporter.java | 7 +- .../cards/decks/importer/DeckImporter.java | 7 ++ .../decks/importer/CodDeckImportTest.java | 40 ++++++++++ .../decks/importer/DecDeckImportTest.java | 73 +++++++++++++++++++ .../cards/decks/importer/FakeCardLookup.java | 29 ++++++++ .../cards/decks/importer/TestDeckChecker.java | 52 +++++++++++++ .../mage/cards/decks/importer/testdeck.cod | 0 .../mage/cards/decks/importer/testdeck.dec | 0 12 files changed, 232 insertions(+), 75 deletions(-) delete mode 100644 Mage.Client/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java delete mode 100644 Mage.Client/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java create mode 100644 Mage/src/main/java/mage/cards/decks/importer/CardLookup.java create mode 100644 Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java create mode 100644 Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java create mode 100644 Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java create mode 100644 Mage/src/test/java/mage/cards/decks/importer/TestDeckChecker.java rename {Mage.Client => Mage}/src/test/java/mage/cards/decks/importer/testdeck.cod (100%) rename {Mage.Client => Mage}/src/test/java/mage/cards/decks/importer/testdeck.dec (100%) diff --git a/Mage.Client/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java b/Mage.Client/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java deleted file mode 100644 index 6e2c9993174..00000000000 --- a/Mage.Client/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package mage.cards.decks.importer; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import mage.cards.decks.DeckCardInfo; -import mage.cards.decks.DeckCardLists; - -public class CodDeckImportTest { - - @Test - public void testImportCod() { - StringBuilder errors = new StringBuilder(); - DeckCardLists deck = new CodDeckImporter().importDeck( - "src/test/java/mage/cards/decks/importer/testdeck.cod", errors); - assertEquals("Deck Name", deck.getName()); - assertEquals(113, deck.getCards().size()); - assertEquals(3, deck.getSideboard().size()); - int index = 0; - for (int i = 0; i < 12; i++) { - assertCardSame("Forest", deck.getCards().get(index++)); - } - for (int i = 0; i < 100; i++) { - assertCardSame("Razorverge Thicket", deck.getCards().get(index++)); - } - assertCardSame("Avacyn's Pilgrim", deck.getCards().get(index++)); - assertEquals(index, deck.getCards().size()); - - index = 0; - for (int i = 0; i < 3; i++) { - assertCardSame("War Priest of Thune", deck.getSideboard().get(index++)); - } - assertEquals(index, deck.getSideboard().size()); - - 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.Client/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java b/Mage.Client/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java deleted file mode 100644 index 0cefeecc72d..00000000000 --- a/Mage.Client/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package mage.cards.decks.importer; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import mage.cards.decks.DeckCardLists; - -public class DecDeckImportTest { - - @Test - public void testImport() { - StringBuilder errors = new StringBuilder(); - DeckCardLists deck = new DecDeckImporter().importDeck( - "src/test/java/mage/cards/decks/importer/testdeck.dec", errors); - assertEquals(60, deck.getCards().size()); - assertEquals(15, deck.getSideboard().size()); - } - -} 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 index 8ef40a0619c..dedffadcf58 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java @@ -7,6 +7,7 @@ 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; @@ -40,12 +41,12 @@ public class CodDeckImporter extends DeckImporter { List mainCards = getNodes(doc, "/cockatrice_deck/zone[@name='main']/card"); decklist.setCards(mainCards.stream() - .flatMap(toDeckCardInfo(errorMessages)) + .flatMap(toDeckCardInfo(getCardLookup(), errorMessages)) .collect(Collectors.toList())); List sideboardCards = getNodes(doc, "/cockatrice_deck/zone[@name='side']/card"); decklist.setSideboard(sideboardCards.stream() - .flatMap(toDeckCardInfo(errorMessages)) + .flatMap(toDeckCardInfo(getCardLookup(), errorMessages)) .collect(Collectors.toList())); getNodes(doc, "/cockatrice_deck/deckname") @@ -65,25 +66,25 @@ public class CodDeckImporter extends DeckImporter { return 1; } try { - return Math.min(100, Math.max(0, Integer.parseInt(numberNode.getNodeValue()))); + return Math.min(100, Math.max(1, Integer.parseInt(numberNode.getNodeValue()))); } catch (NumberFormatException e) { return 1; } } - private static Function> toDeckCardInfo(StringBuilder errors) { + private static Function> toDeckCardInfo(CardLookup lookup, StringBuilder errors) { return node -> { String name = node.getAttributes().getNamedItem("name").getNodeValue().trim(); - CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(name, true); - if (cardInfo != null) { + Optional cardInfo = lookup.lookupCardInfo(name); + if (cardInfo.isPresent()) { + CardInfo info = cardInfo.get(); return Collections.nCopies( getQuantityFromNode(node), - new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode())) - .stream(); + new DeckCardInfo(info.getName(), info.getCardNumber(), info.getSetCode())).stream(); } else { errors.append("Could not find card: '").append(name).append("'\n"); + return Stream.empty(); } - return Stream.empty(); }; } 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 94a879ad959..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; @@ -29,10 +31,11 @@ public class DecDeckImporter extends PlainTextDeckImporter { 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 02c94df04ac..81868e427ff 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java @@ -3,10 +3,13 @@ package mage.cards.decks.importer; import org.apache.log4j.Logger; import java.io.File; import java.util.Locale; +import java.util.Optional; import java.util.Scanner; import mage.cards.decks.DeckCardLists; +import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; public abstract class DeckImporter { @@ -53,6 +56,10 @@ public abstract class DeckImporter { return importDeck(file, new StringBuilder()); } + public CardLookup getCardLookup() { + return CardLookup.instance; + } + private static boolean haveSideboardSection(String file) { // search for sideboard section: // or //sideboard 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..993d100ea35 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java @@ -0,0 +1,40 @@ +package mage.cards.decks.importer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import mage.cards.decks.DeckCardLists; + +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()); + } + +} 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..c0480085539 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java @@ -0,0 +1,73 @@ +package mage.cards.decks.importer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import mage.cards.decks.DeckCardLists; + +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()); + } + +} 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..8f4e744fee1 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/TestDeckChecker.java @@ -0,0 +1,52 @@ +package mage.cards.decks.importer; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +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.Client/src/test/java/mage/cards/decks/importer/testdeck.cod b/Mage/src/test/java/mage/cards/decks/importer/testdeck.cod similarity index 100% rename from Mage.Client/src/test/java/mage/cards/decks/importer/testdeck.cod rename to Mage/src/test/java/mage/cards/decks/importer/testdeck.cod diff --git a/Mage.Client/src/test/java/mage/cards/decks/importer/testdeck.dec b/Mage/src/test/java/mage/cards/decks/importer/testdeck.dec similarity index 100% rename from Mage.Client/src/test/java/mage/cards/decks/importer/testdeck.dec rename to Mage/src/test/java/mage/cards/decks/importer/testdeck.dec From aac3e3fdc15ccd3a43a581f2cf3e3a39787bbeb3 Mon Sep 17 00:00:00 2001 From: John Hitchings Date: Thu, 10 Jan 2019 19:33:42 -0800 Subject: [PATCH 2/2] add importer for o8d and some more decks to test. --- .../client/deckeditor/DeckEditorPanel.java | 5 +- .../mage/cards/decks/importer/CardLookup.java | 6 ++ .../cards/decks/importer/CodDeckImporter.java | 45 +-------- .../cards/decks/importer/DeckImporter.java | 2 + .../cards/decks/importer/MWSDeckImporter.java | 4 +- .../cards/decks/importer/O8dDeckImporter.java | 71 ++++++++++++++ .../cards/decks/importer/XmlDeckImporter.java | 52 +++++++++++ .../mage/cards/repository/CardCriteria.java | 93 +++++++++++++++++++ .../decks/importer/CodDeckImportTest.java | 6 +- .../decks/importer/DecDeckImportTest.java | 25 +---- .../cards/decks/importer/FakeCardLookup.java | 36 ++++++- .../decks/importer/MwsDeckImportTest.java | 57 ++++++++++++ .../decks/importer/O8dDeckImportTest.java | 33 +++++++ .../decks/importer/{ => samples}/testdeck.cod | 0 .../decks/importer/{ => samples}/testdeck.dec | 0 .../decks/importer/samples/testdeck.mwDeck | 23 +++++ .../cards/decks/importer/samples/testdeck.o8d | 8 ++ 17 files changed, 387 insertions(+), 79 deletions(-) create mode 100644 Mage/src/main/java/mage/cards/decks/importer/O8dDeckImporter.java create mode 100644 Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java create mode 100644 Mage/src/test/java/mage/cards/decks/importer/MwsDeckImportTest.java create mode 100644 Mage/src/test/java/mage/cards/decks/importer/O8dDeckImportTest.java rename Mage/src/test/java/mage/cards/decks/importer/{ => samples}/testdeck.cod (100%) rename Mage/src/test/java/mage/cards/decks/importer/{ => samples}/testdeck.dec (100%) create mode 100644 Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mwDeck create mode 100644 Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.o8d 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 ad61e89d849..29ef276b9a7 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -1123,7 +1123,8 @@ class ImportFilter extends FileFilter { || ext.toLowerCase(Locale.ENGLISH).equals("mwdeck") || ext.toLowerCase(Locale.ENGLISH).equals("txt") || ext.toLowerCase(Locale.ENGLISH).equals("dek") - || ext.toLowerCase(Locale.ENGLISH).equals("cod")) { + || ext.toLowerCase(Locale.ENGLISH).equals("cod") + || ext.toLowerCase(Locale.ENGLISH).equals("o8d")) { return true; } } @@ -1132,7 +1133,7 @@ class ImportFilter extends FileFilter { @Override public String getDescription() { - return "*.dec | *.mwDeck | *.txt | *.dek | *.cod"; + return "*.dec | *.mwDeck | *.txt | *.dek | *.cod | *.o8d"; } } diff --git a/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java b/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java index a151caf01a8..2a7aa1ae3e1 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java +++ b/Mage/src/main/java/mage/cards/decks/importer/CardLookup.java @@ -1,7 +1,9 @@ package mage.cards.decks.importer; +import java.util.List; import java.util.Optional; +import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; @@ -13,4 +15,8 @@ public class CardLookup { return Optional.ofNullable(CardRepository.instance.findPreferedCoreExpansionCard(name, true)); } + public List lookupCardInfo(CardCriteria criteria) { + return CardRepository.instance.findCards(criteria); + } + } diff --git a/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java index dedffadcf58..a79a6beeb5b 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/CodDeckImporter.java @@ -1,10 +1,5 @@ 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; @@ -12,26 +7,14 @@ 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(); +public class CodDeckImporter extends XmlDeckImporter { @Override public DeckCardLists importDeck(String filename, StringBuilder errorMessages) { @@ -88,30 +71,4 @@ public class CodDeckImporter extends DeckImporter { }; } - 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/DeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java index 81868e427ff..2e20ded6412 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/DeckImporter.java @@ -32,6 +32,8 @@ public abstract class DeckImporter { return new DekDeckImporter(); } else if (file.toLowerCase(Locale.ENGLISH).endsWith("cod")) { return new CodDeckImporter(); + } else if (file.toLowerCase(Locale.ENGLISH).endsWith("o8d")) { + return new O8dDeckImporter(); } else { return null; } 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 fd541aa1a51..7c23d28c60b 100644 --- a/Mage/src/main/java/mage/cards/decks/importer/MWSDeckImporter.java +++ b/Mage/src/main/java/mage/cards/decks/importer/MWSDeckImporter.java @@ -43,13 +43,13 @@ public class MWSDeckImporter extends PlainTextDeckImporter { CardCriteria criteria = new CardCriteria(); criteria.name(lineName); criteria.setCodes(setCode); - List cards = CardRepository.instance.findCards(criteria); + List cards = getCardLookup().lookupCardInfo(criteria); if (!cards.isEmpty()) { cardInfo = cards.get(RandomUtil.nextInt(cards.size())); } } if (cardInfo == null) { - cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(lineName, true); + cardInfo = getCardLookup().lookupCardInfo(lineName).orElse(null); } if (cardInfo == null) { diff --git a/Mage/src/main/java/mage/cards/decks/importer/O8dDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/O8dDeckImporter.java new file mode 100644 index 00000000000..cefc64e9616 --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/O8dDeckImporter.java @@ -0,0 +1,71 @@ +package mage.cards.decks.importer; + +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 org.w3c.dom.Document; +import org.w3c.dom.Node; + +import mage.cards.decks.DeckCardInfo; +import mage.cards.decks.DeckCardLists; +import mage.cards.repository.CardInfo; + +public class O8dDeckImporter extends XmlDeckImporter { + + @Override + public DeckCardLists importDeck(String filename, StringBuilder errorMessages) { + try { + Document doc = getXmlDocument(filename); + DeckCardLists decklist = new DeckCardLists(); + + List mainCards = getNodes(doc, "/deck/section[@name='Main']/card"); + decklist.setCards(mainCards.stream() + .flatMap(toDeckCardInfo(getCardLookup(), errorMessages)) + .collect(Collectors.toList())); + + List sideboardCards = getNodes(doc, "/deck/section[@name='Sideboard']/card"); + decklist.setSideboard(sideboardCards.stream() + .flatMap(toDeckCardInfo(getCardLookup(), errorMessages)) + .collect(Collectors.toList())); + + 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("qty"); + 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.getTextContent(); + 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(); + } + }; + } + +} diff --git a/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java b/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java new file mode 100644 index 00000000000..8a88799656f --- /dev/null +++ b/Mage/src/main/java/mage/cards/decks/importer/XmlDeckImporter.java @@ -0,0 +1,52 @@ +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.List; + +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; + +public abstract class XmlDeckImporter extends DeckImporter { + + private XPathFactory xpathFactory = XPathFactory.newInstance(); + private DocumentBuilder builder = getDocumentBuilder(); + + protected 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/repository/CardCriteria.java b/Mage/src/main/java/mage/cards/repository/CardCriteria.java index 6e72fcde256..85882512b24 100644 --- a/Mage/src/main/java/mage/cards/repository/CardCriteria.java +++ b/Mage/src/main/java/mage/cards/repository/CardCriteria.java @@ -309,4 +309,97 @@ public class CardCriteria { qb.orderBy(sortBy, true); } } + + public String getName() { + return name; + } + + public String getNameExact() { + return nameExact; + } + + public String getRules() { + return rules; + } + + public List getSetCodes() { + return setCodes; + } + + public List getTypes() { + return types; + } + + public List getNotTypes() { + return notTypes; + } + + public List getSupertypes() { + return supertypes; + } + + public List getNotSupertypes() { + return notSupertypes; + } + + public List getSubtypes() { + return subtypes; + } + + public List getRarities() { + return rarities; + } + + public Boolean getDoubleFaced() { + return doubleFaced; + } + + public boolean isBlack() { + return black; + } + + public boolean isBlue() { + return blue; + } + + public boolean isGreen() { + return green; + } + + public boolean isRed() { + return red; + } + + public boolean isWhite() { + return white; + } + + public boolean isColorless() { + return colorless; + } + + public Integer getConvertedManaCost() { + return convertedManaCost; + } + + public String getSortBy() { + return sortBy; + } + + public Long getStart() { + return start; + } + + public Long getCount() { + return count; + } + + public int getMinCardNumber() { + return minCardNumber; + } + + public int getMaxCardNumber() { + return maxCardNumber; + } + } diff --git a/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java index 993d100ea35..cc1a85076ad 100644 --- a/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java +++ b/Mage/src/test/java/mage/cards/decks/importer/CodDeckImportTest.java @@ -8,14 +8,14 @@ import mage.cards.decks.DeckCardLists; public class CodDeckImportTest { - private static final FakeCardLookup LOOKUP = new FakeCardLookup() + private static final FakeCardLookup LOOKUP = new FakeCardLookup(false) .addCard("Forest") .addCard("Razorverge Thicket") .addCard("Avacyn's Pilgrim") .addCard("War Priest of Thune"); @Test - public void testImportCod() { + public void testImport() { CodDeckImporter importer = new CodDeckImporter() { @Override public CardLookup getCardLookup() { @@ -24,7 +24,7 @@ public class CodDeckImportTest { }; StringBuilder errors = new StringBuilder(); DeckCardLists deck = importer.importDeck( - "src/test/java/mage/cards/decks/importer/testdeck.cod", errors); + "src/test/java/mage/cards/decks/importer/samples/testdeck.cod", errors); assertEquals("Deck Name", deck.getName()); TestDeckChecker.checker() diff --git a/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java index c0480085539..65803c884b4 100644 --- a/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java +++ b/Mage/src/test/java/mage/cards/decks/importer/DecDeckImportTest.java @@ -8,28 +8,7 @@ import mage.cards.decks.DeckCardLists; 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"); + private static final FakeCardLookup LOOKUP = new FakeCardLookup(); @Test public void testImport() { @@ -41,7 +20,7 @@ public class DecDeckImportTest { } }; DeckCardLists deck = importer.importDeck( - "src/test/java/mage/cards/decks/importer/testdeck.dec", errors); + "src/test/java/mage/cards/decks/importer/samples/testdeck.dec", errors); TestDeckChecker.checker() .addMain("Masticore", 4) diff --git a/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java b/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java index 4db37db6d75..f4db4e46d3f 100644 --- a/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java +++ b/Mage/src/test/java/mage/cards/decks/importer/FakeCardLookup.java @@ -1,14 +1,26 @@ package mage.cards.decks.importer; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; public class FakeCardLookup extends CardLookup { private final Map lookup = new HashMap<>(); + private final boolean alwaysMatches; + + public FakeCardLookup() { + this(true); + } + + public FakeCardLookup(boolean alwaysMatches) { + this.alwaysMatches = alwaysMatches; + } public FakeCardLookup addCard(String cardName) { lookup.put(cardName, new CardInfo() {{ @@ -18,12 +30,26 @@ public class FakeCardLookup extends CardLookup { } @Override - public Optional lookupCardInfo(String name) { - CardInfo card = lookup.get(name); - if (card == null) { - System.out.println("Couldn't find: " + name); + public Optional lookupCardInfo(String cardName) { + CardInfo card = lookup.get(cardName); + if (card != null) { + return Optional.of(card); } - return Optional.ofNullable(card); + + if (alwaysMatches) { + return Optional.of(new CardInfo() {{ + name = cardName; + }}); + } + + return Optional.empty(); + } + + @Override + public List lookupCardInfo(CardCriteria criteria) { + return lookupCardInfo(criteria.getName()) + .map(Collections::singletonList) + .orElse(Collections.emptyList()); } } diff --git a/Mage/src/test/java/mage/cards/decks/importer/MwsDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/MwsDeckImportTest.java new file mode 100644 index 00000000000..cf364d81080 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/MwsDeckImportTest.java @@ -0,0 +1,57 @@ +package mage.cards.decks.importer; + +import static org.junit.Assert.assertEquals; + +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import mage.cards.decks.DeckCardLists; +import mage.cards.repository.CardCriteria; +import mage.cards.repository.CardInfo; + +public class MwsDeckImportTest { + + private static final FakeCardLookup LOOKUP = new FakeCardLookup(); + + @Test + public void testImport() { + MWSDeckImporter importer = new MWSDeckImporter() { + @Override + public CardLookup getCardLookup() { + return LOOKUP; + } + }; + StringBuilder errors = new StringBuilder(); + DeckCardLists deck = importer.importDeck( + "src/test/java/mage/cards/decks/importer/samples/testdeck.mwDeck", errors); + + TestDeckChecker.checker() + .addMain("Mutavault", 4) + .addMain("Plains", 18) + .addMain("Daring Skyjek", 2) + .addMain("Azorius Arrester", 4) + .addMain("Banisher Priest", 4) + .addMain("Boros Elite", 4) + .addMain("Dryad Militant", 4) + .addMain("Imposing Sovereign", 4) + .addMain("Precinct Captain", 4) + .addMain("Soldier of the Pantheon", 4) + .addMain("Spear of Heliod", 3) + .addMain("Rootborn Defenses", 1) + .addMain("Brave the Elements", 4) + + .addSide("Wear/Tear", 1) + .addSide("Glare of Heresy", 2) + .addSide("Fiendslayer Paladin", 3) + .addSide("Riot Control", 3) + .addSide("Ajani, Caller of the Pride", 3) + .addSide("Rootborn Defenses", 3) + + .verify(deck, 60, 15); + + assertEquals("", errors.toString()); + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/O8dDeckImportTest.java b/Mage/src/test/java/mage/cards/decks/importer/O8dDeckImportTest.java new file mode 100644 index 00000000000..4eade3abd73 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/O8dDeckImportTest.java @@ -0,0 +1,33 @@ +package mage.cards.decks.importer; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import mage.cards.decks.DeckCardLists; + +public class O8dDeckImportTest { + + private static final FakeCardLookup LOOKUP = new FakeCardLookup(); + + @Test + public void testImport() { + O8dDeckImporter importer = new O8dDeckImporter() { + @Override + public CardLookup getCardLookup() { + return LOOKUP; + } + }; + StringBuilder errors = new StringBuilder(); + DeckCardLists deck = importer.importDeck( + "src/test/java/mage/cards/decks/importer/samples/testdeck.o8d", errors); + + TestDeckChecker.checker() + .addMain("Forest", 1) + .addSide("Island", 2) + .verify(deck, 1, 2); + + assertEquals("", errors.toString()); + } + +} diff --git a/Mage/src/test/java/mage/cards/decks/importer/testdeck.cod b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.cod similarity index 100% rename from Mage/src/test/java/mage/cards/decks/importer/testdeck.cod rename to Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.cod diff --git a/Mage/src/test/java/mage/cards/decks/importer/testdeck.dec b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.dec similarity index 100% rename from Mage/src/test/java/mage/cards/decks/importer/testdeck.dec rename to Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.dec diff --git a/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mwDeck b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mwDeck new file mode 100644 index 00000000000..527f58f60d4 --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.mwDeck @@ -0,0 +1,23 @@ +// Deck file for Magic Workstation (http://www.magicworkstation.com) +// NAME : WW Human +// CREATOR : meltiin (magic-ville.com) +// FORMAT : Standard + 4 [M14] Mutavault + 18 [UNH] Plains + 2 [GTC] Daring Skyjek + 4 [RTR] Azorius Arrester + 4 [M14] Banisher Priest + 4 [GTC] Boros Elite + 4 [RTR] Dryad Militant + 4 [M14] Imposing Sovereign + 4 [RTR] Precinct Captain + 4 [THS] Soldier of the Pantheon + 3 [THS] Spear of Heliod + 1 [RTR] Rootborn Defenses + 4 [M14] Brave the Elements +SB: 1 [DGM] Wear/Tear +SB: 2 [THS] Glare of Heresy +SB: 3 [M14] Fiendslayer Paladin +SB: 3 [DGM] Riot Control +SB: 3 [M14] Ajani, Caller of the Pride +SB: 3 [RTR] Rootborn Defenses \ No newline at end of file diff --git a/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.o8d b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.o8d new file mode 100644 index 00000000000..556671d0f4a --- /dev/null +++ b/Mage/src/test/java/mage/cards/decks/importer/samples/testdeck.o8d @@ -0,0 +1,8 @@ + +
+ Forest +
+
+ Island +
+