Merge pull request #6190 from luziferius/refactor_promo_sets

[RFC] Refactor promo sets, add missing sets as listed on Scryfall
This commit is contained in:
Oleg Agafonov 2020-08-10 16:57:00 +02:00 committed by GitHub
commit b16d30b79b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
438 changed files with 14861 additions and 3880 deletions

View file

@ -1,8 +1,5 @@
package mage.cards;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.keyword.PartnerWithAbility;
@ -15,6 +12,10 @@ import mage.util.CardUtil;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -135,6 +136,12 @@ public abstract class ExpansionSet implements Serializable {
return releaseDate;
}
public int getReleaseYear() {
Calendar cal = Calendar.getInstance();
cal.setTime(this.getReleaseDate());
return cal.get(Calendar.YEAR);
}
public ExpansionSet getParentSet() {
return parentSet;
}

View file

@ -1,5 +1,11 @@
package mage.cards.decks.importer;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -7,68 +13,67 @@ 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 CodDeckImporter extends XmlDeckImporter {
@Override
public DeckCardLists importDeck(String filename, StringBuilder errorMessages) {
try {
Document doc = getXmlDocument(filename);
DeckCardLists decklist = new DeckCardLists();
/**
* @param filename
* @param errorMessages
* @param saveAutoFixedFile do not supported for current format
* @return
*/
@Override
public DeckCardLists importDeck(String filename, StringBuilder errorMessages, boolean saveAutoFixedFile) {
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> 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()));
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()));
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();
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;
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;
}
}
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 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();
}
};
}
}

View file

@ -7,11 +7,15 @@ import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Original xmage's deck format (uses by deck editor)
*
* @author North
*/
public class DckDeckImporter extends PlainTextDeckImporter {
@ -24,13 +28,25 @@ public class DckDeckImporter extends PlainTextDeckImporter {
private static final Pattern layoutStackEntryPattern = Pattern.compile("\\[(\\w+[^:]*\\w*):(\\w+\\w*)]"); // test cases: [JR:64ab],[JR:64],[MPSAK1321:43],[MPSAKH:9],[MPS123-AKH:32],[MPS-13AKH:30],[MPS-AKH:49],[MPS-AKH:11], [PUMA:U16]
// possible fixes for card numbers: [code:123] -> [code:456]
// possible fixes for card numbers with name: [code:123] card1 -> [code:456] card2
private final Map<String, String> possibleFixes = new LinkedHashMap<>();
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
if (line.isEmpty() || line.startsWith("#")) {
return;
}
// AUTO-FIX apply (if card number was fixed before then it can be replaced in layout or other lines too)
for (Map.Entry<String, String> fix : this.possibleFixes.entrySet()) {
if (line.contains(fix.getKey())) {
line = line.replace(fix.getKey(), fix.getValue());
}
}
fixedInfo.setFixedLine(line);
Matcher m = pattern.matcher(line);
if (m.matches()) {
boolean sideboard = false;
@ -46,13 +62,22 @@ public class DckDeckImporter extends PlainTextDeckImporter {
setCode = setCode == null ? "" : setCode.trim();
cardName = cardName == null ? "" : cardName.trim();
// search priority: set/code -> name
// text for auto-fix (don't work on extra spaces between numbers and name -- it's ok)
String originalNumbers = "";
String originalNumbersWithName = "";
if (!setCode.isEmpty() && !cardNum.isEmpty()) {
// [ISD:144] card_name
originalNumbers = "[" + setCode + ":" + cardNum + "]";
originalNumbersWithName = originalNumbers + " " + cardName;
}
// search priority: set/number -> name
// with bulletproof on card number or name changes
DeckCardInfo deckCardInfo = null;
// search by number
CardInfo foundedCard = CardRepository.instance.findCard(setCode, cardNum);
// search by set/number
CardInfo foundedCard = CardRepository.instance.findCard(setCode, cardNum, true);
boolean wasOutdated = false;
if ((foundedCard != null) && !foundedCard.getName().equals(cardName)) {
sbMessage.append("Line ").append(lineCount).append(": ").append("found outdated card number or name, will try to replace: ").append(line).append('\n');
@ -63,7 +88,7 @@ public class DckDeckImporter extends PlainTextDeckImporter {
// search by name
if (foundedCard == null) {
if (!wasOutdated) {
sbMessage.append("Line ").append(lineCount).append(": ").append("can't find card by number, will try ro replace: ").append(line).append('\n');
sbMessage.append("Line ").append(lineCount).append(": ").append("can't find card by number, will try to replace: ").append(line).append('\n');
}
if (!cardName.equals("")) {
@ -71,9 +96,24 @@ public class DckDeckImporter extends PlainTextDeckImporter {
}
if (foundedCard != null) {
sbMessage.append("Line ").append(lineCount).append(": ")
.append("replaced to [").append(foundedCard.getSetCode()).append(":").append(foundedCard.getCardNumberAsInt()).append("] ")
.append(foundedCard.getName()).append('\n');
if (foundedCard.isNightCard()) {
sbMessage.append("Line ").append(lineCount).append(": ").append("ERROR, you can't use night card in deck [").append(foundedCard.getName()).append("]").append('\n');
} else {
sbMessage.append("Line ").append(lineCount).append(": ")
.append("replaced to [").append(foundedCard.getSetCode()).append(":").append(foundedCard.getCardNumberAsInt()).append("] ")
.append(foundedCard.getName()).append('\n');
// AUTO-FIX POSSIBLE (apply and save it for another lines like layout)
// [ISD:144] card
String fixNumbers = "[" + foundedCard.getSetCode() + ":" + foundedCard.getCardNumber() + "]";
String fixNumbersWithName = fixNumbers + " " + foundedCard.getName();
this.possibleFixes.put(originalNumbersWithName, fixNumbersWithName); // name fix must goes first
this.possibleFixes.put(originalNumbers, fixNumbers);
String fixedLine = fixedInfo.getOriginalLine()
.replace(originalNumbersWithName, fixNumbersWithName)
.replace(originalNumbers, fixNumbers);
fixedInfo.setFixedLine(fixedLine);
}
} else {
sbMessage.append("Line ").append(lineCount).append(": ").append("ERROR, can't find card [").append(cardName).append("]").append('\n');
}

View file

@ -1,21 +1,19 @@
package mage.cards.decks.importer;
import java.util.Optional;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import java.util.Optional;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class DecDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
if (line.isEmpty() || line.startsWith("//")) {
return;
}

View file

@ -1,95 +1,122 @@
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;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
public abstract class DeckImporter {
protected static final Logger logger = Logger.getLogger(DeckImporter.class);
public class FixedInfo {
private final String originalLine;
private String fixedLine;
private Boolean canFix = true; // set false if deck have critical error and can't be auto-fixed
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 if (file.toLowerCase(Locale.ENGLISH).endsWith("o8d")) {
return new O8dDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("json")) {
return new MtgjsonDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("draft")) {
return new DraftLogImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("mtga")) {
return new MtgaImporter();
} else {
return null;
}
}
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;
}
FixedInfo(String originalLine) {
this.originalLine = originalLine;
this.fixedLine = originalLine;
}
public String getOriginalLine() {
return originalLine;
}
public Boolean getCanFix() {
return canFix;
}
public void setCanFix(Boolean canFix) {
this.canFix = canFix;
}
public String getFixedLine() {
return fixedLine;
}
public void setFixedLine(String fixedLine) {
this.fixedLine = fixedLine;
}
}
} catch (Exception e) {
// ignore error, deckimporter will process it
}
// not found
return false;
}
protected static final Logger logger = Logger.getLogger(DeckImporter.class);
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 if (file.toLowerCase(Locale.ENGLISH).endsWith("o8d")) {
return new O8dDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("json")) {
return new MtgjsonDeckImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("draft")) {
return new DraftLogImporter();
} else if (file.toLowerCase(Locale.ENGLISH).endsWith("mtga")) {
return new MtgaImporter();
} else {
return null;
}
}
public static DeckCardLists importDeckFromFile(String file, boolean saveAutoFixedFile) {
return importDeckFromFile(file, new StringBuilder(), saveAutoFixedFile);
}
public static DeckCardLists importDeckFromFile(String file, StringBuilder errorMessages, boolean saveAutoFixedFile) {
DeckImporter deckImporter = getDeckImporter(file);
if (deckImporter != null) {
return deckImporter.importDeck(file, errorMessages, saveAutoFixedFile);
} else {
return new DeckCardLists();
}
}
public abstract DeckCardLists importDeck(String file, StringBuilder errorMessages, boolean saveAutoFixedFile);
public DeckCardLists importDeck(String file, boolean saveAutoFixedFile) {
return importDeck(file, new StringBuilder(), saveAutoFixedFile);
}
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;
}
}
}
} catch (Exception e) {
// ignore error, deckimporter will process it
}
// not found
return false;
}
}

View file

@ -11,7 +11,7 @@ import mage.cards.repository.CardRepository;
public class DekDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
if (line.isEmpty() || line.startsWith("#") || !line.contains("<Cards CatID")) {
return;
@ -27,24 +27,24 @@ public class DekDeckImporter extends PlainTextDeckImporter {
} else {
for (int i = 0; i < cardCount; i++) {
DeckCardInfo deckCardInfo = new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode());
if(isSideboard) {
if (isSideboard) {
deckList.getSideboard().add(deckCardInfo);
} else {
deckList.getCards().add(deckCardInfo);
}
}
}
}catch (NumberFormatException nfe) {
} catch (NumberFormatException nfe) {
sbMessage.append("Invalid number: ").append(extractAttribute(line, "Quantity")).append(" at line ").append(lineCount).append('\n');
}
}
private String extractAttribute(String line, String name) {
String searchString = name+"=\"";
int startDelim = line.indexOf(searchString)+searchString.length();
String searchString = name + "=\"";
int startDelim = line.indexOf(searchString) + searchString.length();
int endDelim = line.substring(startDelim).indexOf('\"');
return line.substring(startDelim, startDelim+endDelim);
return line.substring(startDelim, startDelim + endDelim);
}
}

View file

@ -11,13 +11,14 @@ import java.util.regex.Pattern;
public class DraftLogImporter extends PlainTextDeckImporter {
private static Pattern SET_PATTERN = Pattern.compile("------ (\\p{Alnum}+) ------$");
private static Pattern PICK_PATTERN = Pattern.compile("--> (.+)$");
private static final Pattern SET_PATTERN = Pattern.compile("------ (\\p{Alnum}+) ------$");
private static final Pattern PICK_PATTERN = Pattern.compile("--> (.+)$");
private String currentSet = null;
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
Matcher setMatcher = SET_PATTERN.matcher(line);
if (setMatcher.matches()) {
currentSet = setMatcher.group(1);

View file

@ -1,14 +1,12 @@
package mage.cards.decks.importer;
import java.io.File;
import java.io.FileReader;
import org.json.simple.JSONArray;
import mage.cards.decks.DeckCardLists;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import mage.cards.decks.DeckCardLists;
import java.io.File;
import java.io.FileReader;
/**
* @author github: timhae
@ -18,12 +16,12 @@ public abstract class JsonDeckImporter extends DeckImporter {
protected StringBuilder sbMessage = new StringBuilder();
/**
*
* @param file file to import
* @param errorMessages you can setup output messages to showup to user
* @param file file to import
* @param errorMessages you can setup output messages to showup to user
* @param saveAutoFixedFile do not supported for that format
* @return decks list
*/
public DeckCardLists importDeck(String file, StringBuilder errorMessages) {
public DeckCardLists importDeck(String file, StringBuilder errorMessages, boolean saveAutoFixedFile) {
File f = new File(file);
DeckCardLists deckList = new DeckCardLists();
if (!f.exists()) {
@ -62,8 +60,8 @@ public abstract class JsonDeckImporter extends DeckImporter {
}
@Override
public DeckCardLists importDeck(String file) {
return importDeck(file, null);
public DeckCardLists importDeck(String file, boolean saveAutoFixedFile) {
return importDeck(file, null, saveAutoFixedFile);
}
protected abstract void readJson(JSONObject line, DeckCardLists decklist);

View file

@ -1,23 +1,21 @@
package mage.cards.decks.importer;
import java.util.List;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.util.RandomUtil;
import java.util.List;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class MWSDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
if (line.isEmpty() || line.startsWith("//")) {
return;
}
@ -47,7 +45,7 @@ public class MWSDeckImporter extends PlainTextDeckImporter {
if (!cards.isEmpty()) {
cardInfo = cards.get(RandomUtil.nextInt(cards.size()));
}
}
}
if (cardInfo == null) {
cardInfo = getCardLookup().lookupCardInfo(lineName).orElse(null);
}

View file

@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableMap;
import mage.cards.decks.CardNameUtil;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import java.util.Collections;
@ -21,18 +20,19 @@ public class MtgaImporter extends PlainTextDeckImporter {
private static final Map<String, String> SET_REMAPPING = ImmutableMap.of("DAR", "DOM");
private static final Pattern MTGA_PATTERN = Pattern.compile(
"(\\p{Digit}+)" +
"\\p{javaWhitespace}+" +
"(" + CARD_NAME_PATTERN.pattern() + ")" +
"\\p{javaWhitespace}+" +
"\\((\\p{Alnum}+)\\)" +
"\\p{javaWhitespace}+" +
"(\\p{Digit}+)");
"\\p{javaWhitespace}+" +
"(" + CARD_NAME_PATTERN.pattern() + ")" +
"\\p{javaWhitespace}+" +
"\\((\\p{Alnum}+)\\)" +
"\\p{javaWhitespace}+" +
"(\\p{Digit}+)");
private final CardLookup lookup = getCardLookup();
private boolean sideboard = false;
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
if (line.trim().equals("")) {
sideboard = true;
return;

View file

@ -1,5 +1,11 @@
package mage.cards.decks.importer;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -7,65 +13,64 @@ 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();
/**
* @param filename
* @param errorMessages
* @param saveAutoFixedFile do not supported for current format
* @return
*/
@Override
public DeckCardLists importDeck(String filename, StringBuilder errorMessages, boolean saveAutoFixedFile) {
try {
Document doc = getXmlDocument(filename);
DeckCardLists decklist = new DeckCardLists();
List<Node> mainCards = getNodes(doc, "/deck/section[@name='Main']/card");
decklist.setCards(mainCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
List<Node> mainCards = getNodes(doc, "/deck/section[@name='Main']/card");
decklist.setCards(mainCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
List<Node> sideboardCards = getNodes(doc, "/deck/section[@name='Sideboard']/card");
decklist.setSideboard(sideboardCards.stream()
.flatMap(toDeckCardInfo(getCardLookup(), errorMessages))
.collect(Collectors.toList()));
List<Node> 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();
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;
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;
}
}
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.getTextContent();
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 static Function<Node, Stream<DeckCardInfo>> toDeckCardInfo(CardLookup lookup, StringBuilder errors) {
return node -> {
String name = node.getTextContent();
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();
}
};
}
}

View file

@ -1,14 +1,15 @@
package mage.cards.decks.importer;
import java.io.File;
import java.util.Scanner;
import mage.cards.decks.DeckCardLists;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class PlainTextDeckImporter extends DeckImporter {
@ -18,13 +19,17 @@ public abstract class PlainTextDeckImporter extends DeckImporter {
/**
* Import deck from text file
*
* @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)
* @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)
* @param saveAutoFixedFile save fixed deck file (if any fixes applied)
* @return decks list
*/
public DeckCardLists importDeck(String file, StringBuilder errorMessages) {
public DeckCardLists importDeck(String file, StringBuilder errorMessages, boolean saveAutoFixedFile) {
File f = new File(file);
List<String> originalFile = new ArrayList<>();
List<String> fixedFile = new ArrayList<>();
DeckCardLists deckList = new DeckCardLists();
if (!f.exists()) {
logger.warn("Deckfile " + file + " not found.");
@ -35,17 +40,33 @@ public abstract class PlainTextDeckImporter extends DeckImporter {
sbMessage.setLength(0);
try {
try (Scanner scanner = new Scanner(f)) {
Boolean canFix = true;
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
FixedInfo fixedInfo = new FixedInfo(line);
lineCount++;
readLine(line, deckList);
readLine(line, deckList, fixedInfo);
originalFile.add(line);
fixedFile.add(fixedInfo.getFixedLine());
canFix = canFix && fixedInfo.getCanFix();
}
// auto-fix
if (saveAutoFixedFile && canFix && !originalFile.equals(fixedFile)) {
logger.warn("WARNING, deck file contains errors, try to apply auto-fix and save: " + f.getAbsolutePath());
saveFixedDeckFile(fixedFile, f);
}
if (deckList.getCards().isEmpty() && deckList.getSideboard().isEmpty()) {
sbMessage.append("ERROR, unknown deck format, can't find any cards").append("\n");
}
if (sbMessage.length() > 0) {
if(errorMessages != null) {
if (errorMessages != null) {
// normal output for user
errorMessages.append(sbMessage);
}else{
} else {
// fatal error
logger.fatal(sbMessage);
}
@ -59,11 +80,30 @@ public abstract class PlainTextDeckImporter extends DeckImporter {
return deckList;
}
@Override
public DeckCardLists importDeck(String file) {
return importDeck(file, null);
private void saveFixedDeckFile(List<String> fixedfile, File file) {
try (FileOutputStream stream = new FileOutputStream(file)) {
PrintWriter out = new PrintWriter(stream);
for (String line : fixedfile) {
out.println(line);
}
out.close();
} catch (Exception e) {
logger.error("Can't save fixed deck file: " + file.getAbsolutePath() + ", reason: " + e.getMessage());
}
}
protected abstract void readLine(String line, DeckCardLists deckList);
@Override
public DeckCardLists importDeck(String file, boolean saveAutoFixedFile) {
return importDeck(file, null, saveAutoFixedFile);
}
/**
* Read one text line from file and convert it to card
*
* @param line original text line
* @param deckList deck list for new card
* @param fixedInfo fixed info that contains fixed line (if converter can auto-fix deck file, e.g. replace one card to another)
*/
protected abstract void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo);
}

View file

@ -31,7 +31,7 @@ public class TxtDeckImporter extends PlainTextDeckImporter {
}
@Override
protected void readLine(String line, DeckCardLists deckList) {
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
line = line.trim();

View file

@ -39,7 +39,7 @@ public enum CardRepository {
private static final long CARD_CONTENT_VERSION = 231;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;
private RepositoryEventSource eventSource = new RepositoryEventSource();
private final RepositoryEventSource eventSource = new RepositoryEventSource();
CardRepository() {
File file = new File("db");
@ -180,6 +180,7 @@ public enum CardRepository {
public Boolean haveSnowLands(String setCode) {
return setCode.equals("CSP")
|| setCode.equals("MH1")
|| setCode.equals("SLD")
|| setCode.equals("ME2")
|| setCode.equals("ICE");
}
@ -334,11 +335,22 @@ public enum CardRepository {
}
public CardInfo findCard(String setCode, String cardNumber) {
return findCard(setCode, cardNumber, true);
}
public CardInfo findCard(String setCode, String cardNumber, boolean ignoreNightCards) {
try {
QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder();
queryBuilder.limit(1L).where().eq("setCode", new SelectArg(setCode))
.and().eq("cardNumber", new SelectArg(cardNumber))
.and().eq("nightCard", new SelectArg(false));
if (ignoreNightCards) {
queryBuilder.limit(1L).where()
.eq("setCode", new SelectArg(setCode))
.and().eq("cardNumber", new SelectArg(cardNumber))
.and().eq("nightCard", new SelectArg(false));
} else {
queryBuilder.limit(1L).where()
.eq("setCode", new SelectArg(setCode))
.and().eq("cardNumber", new SelectArg(cardNumber));
}
List<CardInfo> result = cardDao.query(queryBuilder.prepare());
if (!result.isEmpty()) {
return result.get(0);

View file

@ -35,7 +35,7 @@ public final class BeastToken extends TokenImpl {
if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("CMD")) {
this.setTokenType(2);
}
if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("DD3GVL")) {
if (getOriginalExpansionSetCode() != null && getOriginalExpansionSetCode().equals("GVL")) {
this.setTokenType(2);
}

View file

@ -16,7 +16,7 @@ public final class BeastToken2 extends TokenImpl {
static final private List<String> tokenImageSets = new ArrayList<>();
static {
tokenImageSets.addAll(Arrays.asList("ZEN", "C14", "DDD", "C15", "DD3GVL", "MM3", "CMA", "E01", "C19", "C20"));
tokenImageSets.addAll(Arrays.asList("ZEN", "C14", "DDD", "C15", "GVL", "MM3", "CMA", "E01", "C19", "C20"));
}
public BeastToken2() {
@ -50,7 +50,7 @@ public final class BeastToken2 extends TokenImpl {
@Override
public void setExpansionSetCodeForImage(String code) {
super.setExpansionSetCodeForImage(code);
if (getOriginalExpansionSetCode().equals("C14") || getOriginalExpansionSetCode().equals("DDD") || getOriginalExpansionSetCode().equals("DD3GVL") || getOriginalExpansionSetCode().equals("MM3")) {
if (getOriginalExpansionSetCode().equals("C14") || getOriginalExpansionSetCode().equals("DDD") || getOriginalExpansionSetCode().equals("GVL") || getOriginalExpansionSetCode().equals("MM3")) {
this.setTokenType(2);
}
}

View file

@ -19,7 +19,7 @@ public final class CatToken extends TokenImpl {
power = new MageInt(2);
toughness = new MageInt(2);
availableImageSetCodes = Arrays.asList("MBP", "C14", "C15", "C17", "C18", "M13", "M14", "MBS", "SOM");
availableImageSetCodes = Arrays.asList("PMEI", "C14", "C15", "C17", "C18", "M13", "M14", "MBS", "SOM");
}
public CatToken(final CatToken token) {

View file

@ -18,7 +18,7 @@ public final class ElementalShamanToken extends TokenImpl {
static final private List<String> tokenImageSets = new ArrayList<>();
static {
tokenImageSets.addAll(Arrays.asList("C15", "DD3JVC", "DD2", "LRW"));
tokenImageSets.addAll(Arrays.asList("C15", "JVC", "DD2", "LRW"));
}
public ElementalShamanToken(boolean withHaste) {

View file

@ -21,7 +21,7 @@ public final class ElementalTokenWithHaste extends TokenImpl {
toughness = new MageInt(1);
this.addAbility(HasteAbility.getInstance());
availableImageSetCodes = Arrays.asList("C20", "MBP", "OGW", "SOK", "MRD");
availableImageSetCodes = Arrays.asList("C20", "PMEI", "OGW", "SOK", "MRD");
}
public ElementalTokenWithHaste(final ElementalTokenWithHaste token) {

View file

@ -16,7 +16,7 @@ public final class ElephantToken extends TokenImpl {
static final private List<String> tokenImageSets = new ArrayList<>();
static {
tokenImageSets.addAll(Arrays.asList("C14", "CNS", "DDD", "MM2", "WWK", "OGW", "C15", "DD3GVL", "MM3", "CMA", "MH1"));
tokenImageSets.addAll(Arrays.asList("C14", "CNS", "DDD", "MM2", "WWK", "OGW", "C15", "GVL", "MM3", "CMA", "MH1"));
}
public ElephantToken() {

View file

@ -18,7 +18,7 @@ public final class GoblinToken extends TokenImpl {
static {
tokenImageSets.addAll(Arrays.asList("10E", "ALA", "SOM", "M10", "NPH", "M13", "RTR",
"MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "DD3EVG", "MM2",
"MMA", "M15", "C14", "KTK", "EVG", "DTK", "ORI", "DDG", "DDN", "EVG", "MM2",
"MM3", "EMA", "C16", "DOM", "ANA", "RNA", "WAR", "MH1"));
}