refactor: improved deck import, added docs and miss tests for dek-files;

This commit is contained in:
Oleg Agafonov 2024-04-10 21:55:43 +04:00
parent 124d60e2b7
commit 889c1125e8
32 changed files with 238 additions and 124 deletions

View file

@ -38,13 +38,13 @@ public class MtgArenaDeckExporter extends DeckExporter {
for (DeckCardInfo card : sourceCards) {
String setCode = card.getSetCode().toUpperCase(Locale.ENGLISH);
setCode = SET_CODE_REPLACEMENTS.getOrDefault(setCode, setCode);
String name = card.getCardName() + " (" + setCode + ") " + card.getCardNum();
String name = card.getCardName() + " (" + setCode + ") " + card.getCardNumber();
String code = prefix + name;
int curAmount = amount.getOrDefault(code, 0);
if (curAmount == 0) {
res.add(name);
}
amount.put(code, curAmount + card.getQuantity());
amount.put(code, curAmount + card.getAmount());
}
return res;
}

View file

@ -38,7 +38,7 @@ public class MtgOnlineDeckExporter extends DeckExporter {
if (curAmount == 0) {
res.add(card.getCardName());
}
amount.put(code, curAmount + card.getQuantity());
amount.put(code, curAmount + card.getAmount());
}
return res;
}

View file

@ -40,7 +40,7 @@ public class XmageDeckExporter extends DeckExporter {
if (curAmount == 0) {
deckMain.add(card);
}
amount.put(code, curAmount + card.getQuantity());
amount.put(code, curAmount + card.getAmount());
}
// sideboard
for (DeckCardInfo card : deck.getSideboard()) {
@ -49,15 +49,15 @@ public class XmageDeckExporter extends DeckExporter {
if (curAmount == 0) {
deckSideboard.add(card);
}
amount.put(code, curAmount + card.getQuantity());
amount.put(code, curAmount + card.getAmount());
}
// cards print
for (DeckCardInfo card : deckMain) {
out.printf("%d [%s:%s] %s%n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName());
out.printf("%d [%s:%s] %s%n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNumber(), card.getCardName());
}
for (DeckCardInfo card : deckSideboard) {
out.printf("SB: %d [%s:%s] %s%n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName());
out.printf("SB: %d [%s:%s] %s%n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNumber(), card.getCardName());
}
// layout print
@ -86,7 +86,7 @@ public class XmageDeckExporter extends DeckExporter {
out.print("(");
for (int i = 0; i < stack.size(); ++i) {
DeckCardInfo info = stack.get(i);
out.printf("[%s:%s]", info.getSetCode(), info.getCardNum());
out.printf("[%s:%s]", info.getSetCode(), info.getCardNumber());
if (i != stack.size() - 1) {
out.print(",");
}

View file

@ -43,7 +43,7 @@ public class XmageInfoDeckExporter extends DeckExporter {
if (curAmount == 0) {
deckMain.add(card);
}
amount.put(code, curAmount + card.getQuantity());
amount.put(code, curAmount + card.getAmount());
}
// Sideboard
@ -53,16 +53,16 @@ public class XmageInfoDeckExporter extends DeckExporter {
if (curAmount == 0) {
deckSideboard.add(card);
}
amount.put(code, curAmount + card.getQuantity());
amount.put(code, curAmount + card.getAmount());
}
// Cards print
for (DeckCardInfo card : deckMain) {
CardInfo cardInfo = CardRepository.instance.findCard(card.getCardName());
if (cardInfo == null) {
out.printf("%d [%s:%s] %s%n\n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName());
out.printf("%d [%s:%s] %s%n\n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNumber(), card.getCardName());
} else {
out.printf("%d [%s:%s] %s ;; %s ;; %s ;; %d %n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName(),
out.printf("%d [%s:%s] %s ;; %s ;; %s ;; %d %n", amount.get("M@" + card.getCardKey()), card.getSetCode(), card.getCardNumber(), card.getCardName(),
cardInfo.getColor().getDescription(), cardInfo.getTypes().toString(), cardInfo.getManaValue());
}
}
@ -70,9 +70,9 @@ public class XmageInfoDeckExporter extends DeckExporter {
for (DeckCardInfo card : deckSideboard) {
CardInfo cardInfo = CardRepository.instance.findCard(card.getCardName());
if (cardInfo == null) {
out.printf("SB: %d [%s:%s] %s%n\n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName());
out.printf("SB: %d [%s:%s] %s%n\n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNumber(), card.getCardName());
} else {
out.printf("SB: %d [%s:%s] %s ;; %s ;; %s ;; %d %n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNum(), card.getCardName(),
out.printf("SB: %d [%s:%s] %s ;; %s ;; %s ;; %d %n", amount.get("S@" + card.getCardKey()), card.getSetCode(), card.getCardNumber(), card.getCardName(),
cardInfo.getColor().getDescription(), cardInfo.getTypes().toString(), cardInfo.getManaValue());
}
}
@ -103,7 +103,7 @@ public class XmageInfoDeckExporter extends DeckExporter {
out.print("(");
for (int i = 0; i < stack.size(); ++i) {
DeckCardInfo info = stack.get(i);
out.printf("[%s:%s]", info.getSetCode(), info.getCardNum());
out.printf("[%s:%s]", info.getSetCode(), info.getCardNumber());
if (i != stack.size() - 1) {
out.print(",");
}

View file

@ -7,6 +7,9 @@ import mage.cards.repository.CardRepository;
import java.util.List;
import java.util.Optional;
/**
* Deck import: helper class to mock cards repository
*/
public class CardLookup {
public static final CardLookup instance = new CardLookup();

View file

@ -13,12 +13,15 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Deck import: Cockatrice app
*/
public class CodDeckImporter extends XmlDeckImporter {
/**
* @param filename
* @param fileName
* @param errorMessages
* @param saveAutoFixedFile do not supported for current format
* @param saveAutoFixedFile do not support for current format
* @return
*/
@Override
@ -43,7 +46,7 @@ public class CodDeckImporter extends XmlDeckImporter {
return decklist;
} catch (Exception e) {
logger.error("Error loading deck", e);
errorMessages.append("There was an error loading the deck.");
errorMessages.append("There was an error loading the deck: " + e.getMessage());
return new DeckCardLists();
}
}
@ -66,9 +69,12 @@ public class CodDeckImporter extends XmlDeckImporter {
Optional<CardInfo> cardInfo = lookup.lookupCardInfo(name);
if (cardInfo.isPresent()) {
CardInfo info = cardInfo.get();
int amount = getQuantityFromNode(node);
DeckCardInfo.makeSureCardAmountFine(amount, info.getName());
return Collections.nCopies(
getQuantityFromNode(node),
new DeckCardInfo(info.getName(), info.getCardNumber(), info.getSetCode())).stream();
amount,
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

@ -15,7 +15,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Original xmage's deck format (uses by deck editor)
* Deck import: native xmage format (uses by deck editor)
*
* @author North
*/
@ -39,7 +39,7 @@ public class DckDeckImporter extends PlainTextDeckImporter {
if (line.isEmpty() || line.startsWith("#")) {
return;
}
line = CardNameUtil.normalizeCardName(line);
// AUTO-FIX apply (if card number was fixed before then it can be replaced in layout or other lines too)
@ -61,6 +61,8 @@ public class DckDeckImporter extends PlainTextDeckImporter {
String cardNum = m.group(4);
String cardName = m.group(5);
DeckCardInfo.makeSureCardAmountFine(count, cardName);
cardNum = cardNum == null ? "" : cardNum.trim();
setCode = setCode == null ? "" : setCode.trim();
cardName = cardName == null ? "" : cardName.trim();
@ -128,9 +130,9 @@ public class DckDeckImporter extends PlainTextDeckImporter {
if (deckCardInfo != null) {
for (int i = 0; i < count; i++) {
if (!sideboard) {
deckList.getCards().add(deckCardInfo);
deckList.getCards().add(deckCardInfo.copy());
} else {
deckList.getSideboard().add(deckCardInfo);
deckList.getSideboard().add(deckCardInfo.copy());
}
}
}

View file

@ -7,6 +7,10 @@ import mage.cards.repository.CardInfo;
import java.util.Optional;
/**
* Deck import: Decked Builder, Apprentice and old Magic Online
* <p>
* Outdated, see actual format in TxtDeckImporter
*
* @author BetaSteward_at_googlemail.com
*/
public class DecDeckImporter extends PlainTextDeckImporter {
@ -34,11 +38,13 @@ public class DecDeckImporter extends PlainTextDeckImporter {
sbMessage.append("Could not find card: '").append(lineName).append("' at line ").append(lineCount).append('\n');
} else {
CardInfo cardInfo = cardLookup.get();
DeckCardInfo.makeSureCardAmountFine(num, cardInfo.getName());
DeckCardInfo deckCardInfo = new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode());
for (int i = 0; i < num; i++) {
if (!sideboard) {
deckList.getCards().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode()));
if (sideboard) {
deckList.getSideboard().add(deckCardInfo.copy());
} else {
deckList.getSideboard().add(new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode()));
deckList.getCards().add(deckCardInfo.copy());
}
}
}

View file

@ -7,6 +7,9 @@ import java.io.File;
import java.util.Locale;
import java.util.Scanner;
/**
* Deck import: base class for all importers
*/
public abstract class DeckImporter {
public static class FixedInfo {

View file

@ -3,34 +3,45 @@ package mage.cards.decks.importer;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
/**
* Created by royk on 11-Sep-16.
* Deck import: MTGO xml format
* <p>
* Outdated, see actual format in TxtDeckImporter
*
* @author royk
*/
public class DekDeckImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
if (line.isEmpty() || line.startsWith("#") || !line.contains("<Cards CatID")) {
if (line.isEmpty() || line.startsWith("#") || !line.contains("<Cards ")) {
return;
}
try {
// e.g. <Cards CatID="61202" Quantity="1" Sideboard="false" Name="Vildin-Pack Outcast" />
Integer cardCount = Integer.parseInt(extractAttribute(line, "Quantity"));
String cardName = extractAttribute(line, "Name");
DeckCardInfo.makeSureCardAmountFine(cardCount, cardName);
// fix double faces name to be compatible with xmage
// Refuse/Cooperate -> Refuse // Cooperate
if (!cardName.contains("//") && cardName.contains("/")) {
cardName = cardName.replace("/", " // ");
}
boolean isSideboard = "true".equals(extractAttribute(line, "Sideboard"));
CardInfo cardInfo = CardRepository.instance.findPreferredCoreExpansionCard(cardName);
CardInfo cardInfo = getCardLookup().lookupCardInfo(cardName).orElse(null);
if (cardInfo == null) {
sbMessage.append("Could not find card: '").append(cardName).append("' at line ").append(lineCount).append('\n');
} else {
DeckCardInfo deckCardInfo = new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode());
for (int i = 0; i < cardCount; i++) {
DeckCardInfo deckCardInfo = new DeckCardInfo(cardInfo.getName(), cardInfo.getCardNumber(), cardInfo.getSetCode());
if (isSideboard) {
deckList.getSideboard().add(deckCardInfo);
deckList.getSideboard().add(deckCardInfo.copy());
} else {
deckList.getCards().add(deckCardInfo);
deckList.getCards().add(deckCardInfo.copy());
}
}
}

View file

@ -7,6 +7,9 @@ import mage.cards.repository.CardInfo;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Deck import: xmage draft logs
*/
public class DraftLogImporter extends PlainTextDeckImporter {
private static final Pattern SET_PATTERN = Pattern.compile("------ (\\p{Alnum}+) ------$");

View file

@ -1,20 +1,25 @@
package mage.cards.decks.importer;
import com.google.gson.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import mage.cards.decks.DeckCardLists;
import java.io.File;
import java.io.FileReader;
/**
* @author github: timhae
* Deck import: helper class for all json base formats
* TODO: improve files structure
*
* @author timhae
*/
public abstract class JsonDeckImporter extends DeckImporter {
protected StringBuilder sbMessage = new StringBuilder();
/**
* @param fileName file to import
* @param fileName 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

View file

@ -5,6 +5,8 @@ import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
/**
* Deck import: Magic Workstation app
*
* @author BetaSteward_at_googlemail.com
*/
public class MWSDeckImporter extends PlainTextDeckImporter {

View file

@ -9,12 +9,14 @@ import mage.cards.repository.CardInfo;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static mage.cards.decks.CardNameUtil.CARD_NAME_PATTERN;
/**
* Deck import: MTGA official app
*/
public class MtgaImporter extends PlainTextDeckImporter {
private static final Map<String, String> SET_REMAPPING = ImmutableMap.of("DAR", "DOM");
@ -32,9 +34,9 @@ public class MtgaImporter extends PlainTextDeckImporter {
@Override
protected void readLine(String line, DeckCardLists deckList, FixedInfo fixedInfo) {
line = line.trim();
if (line.equals("Deck")) {
return;
}
@ -45,30 +47,33 @@ public class MtgaImporter extends PlainTextDeckImporter {
}
Matcher pattern = MTGA_PATTERN.matcher(CardNameUtil.normalizeCardName(line));
if (!pattern.matches()) {
sbMessage.append("Error reading '").append(line).append("'\n");
return;
}
Optional<CardInfo> found;
CardInfo found;
int count = Integer.parseInt(pattern.group(1));
String name = pattern.group(2);
String name = pattern.group(2);
if (pattern.group(3) != null && pattern.group(4) != null) {
String set = SET_REMAPPING.getOrDefault(pattern.group(3), pattern.group(3));
String cardNumber = pattern.group(4);
found = lookup.lookupCardInfo(name, set, cardNumber);
found = lookup.lookupCardInfo(name, set, cardNumber).orElse(null);
} else {
found = lookup.lookupCardInfo(name);
found = lookup.lookupCardInfo(name).orElse(null);
}
if (!found.isPresent()) {
sbMessage.append("Cound not find card for '").append(line).append("'\n");
} else {
final List<DeckCardInfo> zone = sideboard ? deckList.getSideboard() : deckList.getCards();
found.ifPresent(card -> zone.addAll(Collections.nCopies(count,
new DeckCardInfo(card.getName(), card.getCardNumber(), card.getSetCode()))));
if (found == null) {
sbMessage.append("Could not find card for '").append(line).append("'\n");
return;
}
DeckCardInfo.makeSureCardAmountFine(count, found.getName());
List<DeckCardInfo> zone = sideboard ? deckList.getSideboard() : deckList.getCards();
DeckCardInfo deckCardInfo = new DeckCardInfo(found.getName(), found.getCardNumber(), found.getSetCode());
zone.addAll(Collections.nCopies(count, deckCardInfo.copy()));
}
}

View file

@ -12,7 +12,9 @@ import java.util.Optional;
/**
* @author github: timhae
* Deck import: mtgjson service
*
* @author timhae
*/
public class MtgjsonDeckImporter extends JsonDeckImporter {

View file

@ -13,6 +13,9 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Deck import: OCTGN app
*/
public class O8dDeckImporter extends XmlDeckImporter {
/**
@ -72,5 +75,4 @@ public class O8dDeckImporter extends XmlDeckImporter {
}
};
}
}

View file

@ -10,6 +10,8 @@ import java.util.List;
import java.util.Scanner;
/**
* Deck import: helper class for all text base formats
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class PlainTextDeckImporter extends DeckImporter {

View file

@ -12,7 +12,9 @@ import java.util.Locale;
import java.util.Set;
/**
* @author BetaSteward_at_googlemail.com
* Deck import: text deck, compatible with MTGO and many other apps/services
*
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public class TxtDeckImporter extends PlainTextDeckImporter {

View file

@ -18,6 +18,9 @@ import java.util.List;
import static javax.xml.xpath.XPathConstants.NODESET;
/**
* Deck import: helper class for all xml base formats
*/
public abstract class XmlDeckImporter extends DeckImporter {
private final XPathFactory xpathFactory = XPathFactory.newInstance();