solution for Issue 643: Integrate top decks json feed from Firemind.

master
Lodici 2014-10-10 11:17:18 +01:00
parent 0ba16d4894
commit 21b57a7267
9 changed files with 251 additions and 14 deletions

View File

@ -31,6 +31,8 @@ release/Magarena/logs
notes/
release/Magarena/players
release/Magarena/decks/*.dec
release/Magarena/decks/firemind
release/Magarena/firemind
syntax: regexp
release/Magarena-debug.sh
release/Magarena-debug-jar.bat

View File

@ -16,10 +16,11 @@ public enum DeckType {
// TODO: Recent("Recently Played"), // last 20 most recently played decks
Random("Random"),
Preconstructed("Prebuilt"),
Custom("Player")
Custom("Player"),
Firemind("Firemind Top Decks")
;
public static final Set<DeckType> PREDEFINED_DECKS = EnumSet.range(Preconstructed, Custom);
public static final Set<DeckType> PREDEFINED_DECKS = EnumSet.range(Preconstructed, Firemind);
private final String deckTypeCaption;

View File

@ -97,15 +97,15 @@ public class DeckUtils {
}
final String description = deck.getDescription();
if (description != null) {
writer.write(">" + description);
writer.write(">" + description.replaceAll("(\\r|\\n|\\r\\n)", "\\\\n"));
}
} catch (final IOException ex) {
isSuccessful = false;
System.err.println("ERROR! Unable to save deck");
System.err.println(ex.getMessage());
ex.printStackTrace();
System.err.println("Invalid deck : " + deck.getFilename() + " - " + ex.getMessage());
} finally {
magic.data.FileIO.close(writer);
if (writer != null) {
magic.data.FileIO.close(writer);
}
}
return isSuccessful;

View File

@ -0,0 +1,46 @@
package magic.firemind;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import magic.data.DownloadableFile;
public class FiremindJsonFile extends DownloadableFile {
private static final String JSON_URL = "https://www.firemind.ch/decks/top.json";
private final URL url;
private final File file;
public FiremindJsonFile(final File jsonFile) throws MalformedURLException {
url = new URL(JSON_URL);
file = jsonFile;
}
@Override
public void download(Proxy proxy) throws IOException {
final File tempFile = new File(file.getParent(), "~" + file.getName());
DownloadableFile.downloadToFile(proxy, url, tempFile);
Files.move(tempFile.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
@Override
public String getFilename() {
return file.getName();
}
@Override
public File getFile() {
return file;
}
@Override
public URL getDownloadUrl() {
return url;
}
}

View File

@ -0,0 +1,89 @@
package magic.firemind;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Calendar;
import java.util.List;
import magic.data.DeckUtils;
import magic.data.GeneralConfig;
import magic.model.MagicDeck;
import magic.utility.MagicFileSystem;
import magic.utility.MagicFileSystem.DataPath;
import org.apache.commons.io.FileUtils;
public final class FiremindJsonReader {
private FiremindJsonReader() {}
private static boolean isJsonFileUpToDate(final File jsonFile) {
final Calendar calToday = Calendar.getInstance();
final Calendar calFile = Calendar.getInstance();
calFile.setTimeInMillis(jsonFile.lastModified());
return calToday.get(Calendar.YEAR) == calFile.get(Calendar.YEAR)
&& calToday.get(Calendar.MONTH) == calFile.get(Calendar.MONTH)
&& calToday.get(Calendar.DAY_OF_MONTH) == calFile.get(Calendar.DAY_OF_MONTH);
}
private static void downloadLatestJsonFile(final File jsonFile) {
try {
final FiremindJsonFile downloadFile = new FiremindJsonFile(jsonFile);
downloadFile.download(GeneralConfig.getInstance().getProxy());
} catch (IOException ex) {
System.err.println("Download of json file failed : " + ex.getMessage());
}
if (jsonFile.exists()) {
if (!isJsonFileUpToDate(jsonFile)) {
// only attempt download once per day even if download fails.
final Calendar cal = Calendar.getInstance();
jsonFile.setLastModified(cal.getTimeInMillis());
}
} else {
try {
// a problem occurred so create empty json file so that for
// the rest of the day does not keep trying to download.
jsonFile.createNewFile();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
private static void clearFiremindDecksDirectory(final Path firemindDecksPath) throws IOException {
FileUtils.deleteDirectory(firemindDecksPath.toFile());
MagicFileSystem.verifyDirectoryPath(firemindDecksPath);
}
public static void refreshTopDecks() throws IOException {
final File jsonFile = MagicFileSystem.getDataPath(DataPath.FIREMIND).resolve("topdecks.json").toFile();
if (jsonFile.exists() && isJsonFileUpToDate(jsonFile)) {
// JSON file has already been downloaded/updated today.
return;
}
// Ensure Firemind decks directory exists.
final Path firemindDecksPath = MagicFileSystem.getDataPath(DataPath.DECKS).resolve("firemind");
MagicFileSystem.verifyDirectoryPath(firemindDecksPath);
downloadLatestJsonFile(jsonFile);
if (jsonFile.length() == 0) {
// a problem occurred, use existing decks.
return;
}
final List<MagicDeck> decks = JsonOrgParser.parse(jsonFile);
clearFiremindDecksDirectory(firemindDecksPath);
for (MagicDeck deck : decks) {
try {
final String filename = firemindDecksPath.resolve(deck.getFilename() + ".dec").toString();
DeckUtils.saveDeck(filename, deck);
} catch (Exception ex) {
System.err.println("Invalid deck : " + deck.getFilename() + " - " + ex.getMessage());
}
}
}
}

View File

@ -0,0 +1,87 @@
package magic.firemind;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import magic.data.DeckUtils;
import magic.model.MagicCardDefinition;
import magic.model.MagicDeck;
import org.json.JSONArray;
import org.json.JSONObject;
public final class JsonOrgParser {
private JsonOrgParser() {}
public static List<MagicDeck> parse(final File jsonFile) throws IOException {
final List<MagicDeck> decks = new ArrayList<>();
final JSONObject jsonRoot = new JSONObject(getJsonString(jsonFile));
final List<String> formats = new ArrayList<>(Arrays.asList(JSONObject.getNames(jsonRoot)));
for (String format : formats) {
final JSONObject jsonFormat = jsonRoot.getJSONObject(format);
final List<String> deckNames = new ArrayList<>(Arrays.asList(JSONObject.getNames(jsonFormat)));
for (String deckName : deckNames) {
final JSONObject jsonDeck = jsonFormat.getJSONObject(deckName);
final MagicDeck deck = new MagicDeck();
decks.add(deck);
deck.setFilename(format + "." + deckName);
deck.setDescription(getDeckDescription(jsonDeck));
addCardsToDeck(deck, jsonDeck.getJSONArray("cards"));
}
}
return decks;
}
private static void addCardsToDeck(final MagicDeck deck, final JSONArray jsonCards) {
for (int i = 0; i < jsonCards.length(); i++) {
final JSONObject jsonCard = jsonCards.getJSONObject(i);
final String cardName = jsonCard.getString("name");
final int cardQuantity = jsonCard.getInt("quantity");
final MagicCardDefinition cardDef = DeckUtils.getCard(cardName);
for (int j = 0; j < cardQuantity; j++) {
deck.add(cardDef);
}
}
}
private static String getDeckDescription(final JSONObject jsonDeck) {
final StringBuffer sb = new StringBuffer();
sb.append("Author: ").append(jsonDeck.getString("author"));
sb.append("\nRating: ").append(jsonDeck.getString("rating"));
sb.append("\nReleased: ").append(getFormattedReleaseDate(jsonDeck.getString("releaseDate")));
if (!jsonDeck.getString("description").trim().isEmpty()) {
sb.append("\n\n").append(jsonDeck.getString("description"));
}
return sb.toString();
}
private static String getFormattedReleaseDate(final String jsonValue) {
if (jsonValue.length() != 8) {
return jsonValue;
} else {
return jsonValue.substring(0, 4) + "-"
+ jsonValue.substring(4, 6) + "-"
+ jsonValue.substring(6, 8);
}
}
private static String getJsonString(final File jsonFile) throws IOException {
try (final BufferedReader br = new BufferedReader(new FileReader(jsonFile))) {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
return sb.toString();
}
}
}

View File

@ -90,14 +90,14 @@ public class DeckDescriptionViewer extends JPanel implements FocusListener {
}
public void setDeckDescription(String text) {
textArea.setText(text == null || text.isEmpty() ? "" : text);
textArea.setText(text == null || text.isEmpty() ? "" : text.replaceAll("\\\\n", "\n"));
textArea.setCaretPosition(0);
}
public void setDeckDescription(final MagicDeck deck) {
if (deck != null) {
final String text = deck.getDescription();
textArea.setText(text == null || text.isEmpty() ? "" : text);
textArea.setText(text == null || text.isEmpty() ? "" : text.replaceAll("\\\\n", "\n"));
textArea.setCaretPosition(0);
textArea.setForeground(deck.isValid() ? Color.BLACK : Color.RED.darker());
} else {

View File

@ -30,6 +30,7 @@ import javax.swing.event.ListSelectionListener;
import magic.MagicMain;
import magic.data.DeckType;
import magic.data.DeckUtils;
import magic.firemind.FiremindJsonReader;
import magic.model.MagicDeck;
import magic.ui.dialog.DecksFilterDialog;
import magic.ui.screen.interfaces.IDeckConsumer;
@ -140,14 +141,17 @@ public class DeckPicker extends JPanel {
final DeckType deckTypes[] = DeckType.PREDEFINED_DECKS.toArray(new DeckType[DeckType.PREDEFINED_DECKS.size()]);
deckTypeJCombo.setModel(new DefaultComboBoxModel<>(deckTypes));
deckTypeJCombo.setSelectedIndex(0);
// deck names list
refreshDecksList();
}
private void refreshDecksList() {
// ignore list selection events while setting list data.
decksJList.removeListSelectionListener(listSelectionListener);
decksJList.setListData(getDecksListData());
try {
decksJList.setListData(getDecksListData());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
decksJList.addListSelectionListener(listSelectionListener);
if (decksJList.getModel().getSize() > 0) {
decksJList.setSelectedIndex(0);
@ -160,15 +164,18 @@ public class DeckPicker extends JPanel {
}
}
private MagicDeck[] getDecksListData() {
private MagicDeck[] getDecksListData() throws IOException {
switch (selectedDeckType) {
case Preconstructed:
return getFilteredDecksListData(DeckUtils.getPrebuiltDecksFolder());
case Custom:
return getFilteredDecksListData(Paths.get(DeckUtils.getDeckFolder()));
case Firemind:
FiremindJsonReader.refreshTopDecks();
return getFilteredDecksListData(Paths.get(DeckUtils.getDeckFolder()).resolve("firemind"));
default:
return new MagicDeck[0];
}
}
}
private MagicDeck[] getFilteredDecksListData(final Path decksPath) {
@ -241,6 +248,8 @@ public class DeckPicker extends JPanel {
for (IDeckConsumer listener : listeners) {
if (selectedDeckType == DeckType.Random) {
listener.setDeck(deck.getName(), selectedDeckType);
} else if (selectedDeckType == DeckType.Firemind) {
listener.setDeck(deck, getDeckPath(deck.getName(), selectedDeckType));
} else {
listener.setDeck(deck, getDeckPath(deck.getName(), selectedDeckType));
}
@ -256,6 +265,8 @@ public class DeckPicker extends JPanel {
return DeckUtils.getPrebuiltDecksFolder().resolve(deckName + ".dec");
case Custom:
return Paths.get(DeckUtils.getDeckFolder()).resolve(deckName + ".dec");
case Firemind:
return Paths.get(DeckUtils.getDeckFolder()).resolve("firemind").resolve(deckName + ".dec");
default:
throw new RuntimeException("getDeckPath() not implemented for decktype: " + deckType);
}

View File

@ -74,7 +74,8 @@ public final class MagicFileSystem {
LOGS("logs"),
DUELS("duels"),
PLAYERS("players"),
AVATARS("avatars");
AVATARS("avatars"),
FIREMIND("firemind");
private final Path directoryPath;