* GUI: added auto-fix for deck files on usage (if deck contains wrong card numbers then it will be fixed and saved automaticity);

This commit is contained in:
Oleg Agafonov 2020-08-08 21:27:20 +04:00
parent 9652d83aec
commit c5d7a3e9f9
31 changed files with 644 additions and 567 deletions

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.HashMap;
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,23 @@ 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]
private final Map<String, String> possibleFixes = new HashMap<>(); // possible fixes for card codes and numbers [code:123] -> [code:456]
@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,12 +60,20 @@ public class DckDeckImporter extends PlainTextDeckImporter {
setCode = setCode == null ? "" : setCode.trim();
cardName = cardName == null ? "" : cardName.trim();
// search priority: set/code -> name
// text for auto-fix
String originalText = "";
if (!setCode.isEmpty() && !cardNum.isEmpty()) {
// [ISD:144]
originalText = "[" + setCode + ":" + cardNum + "]";
}
String fixedText = originalText;
// search priority: set/number -> name
// with bulletproof on card number or name changes
DeckCardInfo deckCardInfo = null;
// search by number
// search by set/number
CardInfo foundedCard = CardRepository.instance.findCard(setCode, cardNum);
boolean wasOutdated = false;
if ((foundedCard != null) && !foundedCard.getName().equals(cardName)) {
@ -63,7 +85,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("")) {
@ -74,6 +96,12 @@ public class DckDeckImporter extends PlainTextDeckImporter {
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]
fixedText = "[" + foundedCard.getSetCode() + ":" + foundedCard.getCardNumber() + "]";
fixedInfo.setFixedLine(fixedInfo.getOriginalLine().replace(originalText, fixedText));
this.possibleFixes.put(originalText, fixedText);
} 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,29 @@ 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 (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 +76,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();