Merge pull request 'tagger' (#36) from tagger into master
Some checks failed
/ build_release (push) Has been cancelled

Reviewed-on: #36
This commit is contained in:
Failure 2025-08-15 02:37:58 -07:00
commit eaac06f1cb
12 changed files with 670 additions and 10 deletions

View file

@ -7,6 +7,7 @@ import mage.cards.decks.Deck;
import mage.cards.repository.CardRepository;
import mage.cards.repository.CardScanner;
import mage.cards.repository.RepositoryUtil;
import mage.cards.repository.TagRepository;
import mage.client.cards.BigCard;
import mage.client.chat.ChatPanelBasic;
import mage.client.components.*;
@ -58,11 +59,14 @@ import org.apache.log4j.Logger;
import org.junit.Assert;
import org.mage.card.arcane.ManaSymbols;
import org.mage.card.arcane.SvgUtils;
import org.mage.plugins.card.dl.sources.TagSource;
import org.mage.plugins.card.images.DownloadPicturesService;
import org.mage.plugins.card.info.CardInfoPaneImpl;
import org.mage.plugins.card.utils.CardImageUtils;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
import com.google.gson.Gson;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
@ -985,6 +989,8 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
popupDownload = new javax.swing.JPopupMenu();
menuDownloadSymbols = new javax.swing.JMenuItem();
menuDownloadImages = new javax.swing.JMenuItem();
menuDownloadTags = new javax.swing.JMenuItem();
desktopPane = new MageJDesktop();
mageToolbar = new javax.swing.JToolBar();
btnPreferences = new javax.swing.JButton();
@ -1046,6 +1052,14 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
});
popupDownload.add(menuDownloadImages);
menuDownloadTags.setText("Download Scryfall Tagger tags (this will freeze for a bit)");
menuDownloadTags.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
TagSource.instance.syncTagRepositiory();
}
});
popupDownload.add(menuDownloadTags);
setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
setMinimumSize(new java.awt.Dimension(1000, 500));
@ -1186,7 +1200,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
pack();
}// </editor-fold>//GEN-END:initComponents
private void btnDeckEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed
private void btnDeckEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed
showDeckEditor(DeckEditorMode.FREE_BUILDING, null, null, null, 0);
}//GEN-LAST:event_btnDeckEditorActionPerformed
@ -1635,6 +1651,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
private javax.swing.JMenuItem menuDebugTestModalDialog;
private javax.swing.JMenuItem menuDownloadImages;
private javax.swing.JMenuItem menuDownloadSymbols;
private javax.swing.JMenuItem menuDownloadTags;
private javax.swing.JPopupMenu popupDebug;
private javax.swing.JPopupMenu popupDownload;
private javax.swing.JToolBar.Separator separatorDebug;
@ -1947,6 +1964,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
menuDownloadSymbols.setFont(font);
menuDownloadImages.setFont(font);
menuDownloadTags.setFont(font);
menuDebugTestModalDialog.setFont(font);
menuDebugTestCardRenderModesDialog.setFont(font);
menuDebugTestCustomCode.setFont(font);

View file

@ -13,6 +13,7 @@ import mage.client.constants.Constants;
import mage.client.constants.Constants.SortBy;
import mage.client.dialog.PreferencesDialog;
import mage.client.deckeditor.table.TableModel;
import mage.client.deckeditor.table.TaggerModel;
import mage.client.dialog.CheckBoxList;
import mage.client.util.GUISizeHelper;
import mage.client.util.gui.FastSearchUtil;
@ -35,7 +36,11 @@ import org.mage.card.arcane.ManaSymbolsCellRenderer;
import javafx.util.Pair;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.*;
import java.io.ByteArrayOutputStream;
@ -97,6 +102,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardSelectorScrollPane.setOpaque(false);
cardSelectorScrollPane.getViewport().setOpaque(false);
taggerScrollPane.setOpaque(false);
taggerScrollPane.getViewport().setOpaque(false);
cbSortBy.setModel(new DefaultComboBoxModel<>(SortBy.values()));
cbSortBy.setSelectedItem(sortSetting.getSortBy());
jTextFieldSearch.addActionListener(searchAction);
@ -106,19 +115,36 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
tbColor.setOpaque(true); // false = transparent
tbTypes.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
tbTypes.setOpaque(true); // false = transparent
taggerScrollPane.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
taggerScrollPane.setOpaque(true);
cardSelectorBottomPanel.setBackground(PreferencesDialog.getCurrentTheme().getDeckEditorToolbarBackgroundColor());
cardSelectorBottomPanel.setOpaque(true); // false = transparent
}
private void initListViewComponents() {
taggerTable = new JTable();
mainTable = new JTable();
mainModel = new TableModel();
tagsModel = new TaggerModel(() -> filterCards());
tagsModel.addListeners(taggerTable);
taggerTable.setModel(tagsModel);
TableColumnModel taggerTableModel = taggerTable.getColumnModel();
taggerTableModel.getColumn(0).setMaxWidth(30);
taggerTableModel.getColumn(0).setPreferredWidth(30);
taggerTableModel.getColumn(1).setMaxWidth(30);
taggerTableModel.getColumn(1).setPreferredWidth(30);
taggerTableModel.getColumn(2).setMaxWidth(160);
taggerTableModel.getColumn(2).setPreferredWidth(160);
taggerTableModel.getColumn(4).setMaxWidth(60);
DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class);
mainModel.addListeners(mainTable);
mainTable.setModel(mainModel);
mainTable.setForeground(Color.white);
DefaultTableCellRenderer myRenderer = (DefaultTableCellRenderer) mainTable.getDefaultRenderer(String.class);
myRenderer.setBackground(new Color(0, 0, 0, 100));
mainTable.getColumnModel().getColumn(0).setMaxWidth(0);
mainTable.getColumnModel().getColumn(0).setPreferredWidth(10);
@ -136,6 +162,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
// mainTable.setToolTipText(cardSelectorScrollPane.getToolTipText());
cardSelectorScrollPane.setViewportView(mainTable);
taggerScrollPane.setViewportView(taggerTable);
taggerTable.setOpaque(false);
mainTable.setOpaque(false);
cbSortBy.setEnabled(false);
chkPiles.setEnabled(false);
@ -188,6 +219,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
mainTable.setFont(GUISizeHelper.tableFont);
mainTable.setRowHeight(GUISizeHelper.tableRowHeight);
taggerTable.getTableHeader().setFont(GUISizeHelper.tableFont);
taggerTable.setFont(GUISizeHelper.tableFont);
taggerTable.setRowHeight(GUISizeHelper.tableRowHeight);
}
public void switchToGrid() {
@ -486,7 +520,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
if (limited) {
for (Card card : cards) {
if (filter.match(card, null)) {
if (filter.match(card, null) && tagsModel.testCard(card)) {
filteredCards.add(card);
}
}
@ -505,6 +539,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
if (!filter.match(card, null)) {
continue;
}
if (!tagsModel.testCard(card)) {
continue;
}
// found
filteredCards.add(card);
}
@ -631,6 +669,71 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardCountLabel = new javax.swing.JLabel();
cardCount = new javax.swing.JLabel();
// Brings me back to the Tk days
taggerScrollPane = new javax.swing.JScrollPane();
taggerContainer = new javax.swing.JPanel();
taggerContainer.setPreferredSize(new Dimension(200, 400));
taggerContainer.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.gridx = 0;
gbc.gridy = 0;
taggerControlBar = new javax.swing.JPanel();
taggerControlBar.setLayout(new GridBagLayout());
taggerReset = new javax.swing.JButton("Reset");
taggerReset.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
tagsModel.clear();
}
});
taggerContainer.add(taggerScrollPane, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weighty = 0;
gbc.weightx = 1;
gbc.gridx = 0;
gbc.gridy = 1;
taggerContainer.add(taggerControlBar, gbc);
gbc.fill = GridBagConstraints.NONE;
gbc.weighty = 0;
gbc.weightx = 0;
gbc.gridx = 0;
gbc.gridy = 0;
taggerControlBar.add(taggerReset, gbc);
taggerSearch = new javax.swing.JTextField();
taggerSearch.getDocument().addDocumentListener(new DocumentListener() {
public void removeUpdate(DocumentEvent e) {
changedUpdate(e);
}
public void insertUpdate(DocumentEvent e) {
changedUpdate(e);
}
public void changedUpdate(DocumentEvent e) {
tagsModel.search(taggerSearch.getText());
}
});
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weighty = 1;
gbc.weightx = 1;
gbc.gridx = 1;
gbc.gridy = 0;
taggerControlBar.add(taggerSearch, gbc);
// End hell
tablePanel = new javax.swing.JSplitPane(JSplitPane.HORIZONTAL_SPLIT, cardSelectorScrollPane, taggerContainer);
tablePanel.setOneTouchExpandable(true);
tablePanel.setResizeWeight(1.0);
tablePanel.setDividerLocation(1.0);
tbColor.setFloatable(false);
tbColor.setRollover(true);
tbColor.setToolTipText("Hold the ALT-key while clicking to deselect all other colors or hold the CTRL-key to select only all other colors.");
@ -1105,6 +1208,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardSelectorBottomPanel.setOpaque(false);
cardSelectorBottomPanel.setPreferredSize(new java.awt.Dimension(897, 40));
tablePanel.setOpaque(false);
tablePanel.setPreferredSize(new java.awt.Dimension(600, 40));
jButtonAddToMain.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/deck_in.png"))); // NOI18N
jButtonAddToMain.setToolTipText("<html>Add selected cards to deck.<br/>\nAlternative: <strong>Double click</strong> the card in card selector to move a card to the deck.");
@ -1250,6 +1355,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
cardCount.setText("0");
javax.swing.GroupLayout cardSelectorBottomPanelLayout = new javax.swing.GroupLayout(cardSelectorBottomPanel);
cardSelectorBottomPanel.setLayout(cardSelectorBottomPanelLayout);
cardSelectorBottomPanelLayout.setHorizontalGroup(
cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -1319,9 +1425,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(tbColor, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(tbTypes, javax.swing.GroupLayout.DEFAULT_SIZE, 1057, Short.MAX_VALUE)
.addComponent(cardSelectorScrollPane)
.addComponent(tablePanel)
.addComponent(cardSelectorBottomPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1057, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
@ -1329,10 +1436,13 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
.addGap(0, 0, 0)
.addComponent(tbTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(cardSelectorScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE)
.addComponent(tablePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 237, Short.MAX_VALUE)
.addGap(0, 0, 0)
.addComponent(cardSelectorBottomPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 31, javax.swing.GroupLayout.PREFERRED_SIZE))
);
}// </editor-fold>//GEN-END:initComponents
private void cbExpansionSetActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbExpansionSetActionPerformed
@ -1689,7 +1799,9 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
}
private TableModel mainModel;
private TaggerModel tagsModel;
private JTable mainTable;
private JTable taggerTable;
private ICardGrid currentView;
private final CheckBoxList listCodeSelected;
@ -1751,6 +1863,17 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
private javax.swing.JToolBar tbTypes;
private javax.swing.JToggleButton tbUncommon;
private javax.swing.JToggleButton tbWhite;
private javax.swing.JScrollPane taggerScrollPane;
private javax.swing.JPanel taggerControlBar;
private javax.swing.JTextField taggerSearch;
private javax.swing.JButton taggerReset;
private javax.swing.JPanel taggerContainer;
private javax.swing.JSplitPane tablePanel;
// End of variables declaration//GEN-END:variables
private final mage.client.cards.CardGrid cardGrid; // grid for piles view mode (example: selected cards in drafting)

View file

@ -0,0 +1,230 @@
package mage.client.deckeditor.table;
import mage.client.cards.BigCard;
import mage.cards.Card;
import mage.cards.repository.CardInfo;
import mage.cards.repository.Tag;
import mage.cards.repository.TagRepository;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.log4j.Logger;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import java.awt.event.*;
import java.util.List;
import java.util.*;
public class TaggerModel extends AbstractTableModel {
private static final long serialVersionUID = -528008802935423048L;
private static final Logger log = Logger.getLogger(TableModel.class);
protected BigCard bigCard;
protected UUID gameId;
private List<Tag> view = new ArrayList<Tag>();
public Map<String, Boolean> filtered = new HashMap<String, Boolean>();
public Set<String> whitelist = new HashSet<String>();
public Set<String> blacklist = new HashSet<String>();
private final String[] column = {"Inc", "Exc", "Name", "Description", "# Cards"};
private boolean descending = false;
private int columnSortedBy = 0;
private int recentSortedColumn;
private boolean recentAscending;
private Runnable doSort;
public TaggerModel(Runnable sortFn) {
doSort = sortFn;
view = TagRepository.instance.getAllTags();
if (view.size() == 0) {
Tag placeholder = new Tag();
placeholder.id = "";
placeholder.description = "Please download Scryfall tags from the 'download' tab & reopen";
placeholder.label = "Data missing!";
view.add(placeholder);
}
}
public void clear() {
view = TagRepository.instance.getAllTags();
this.whitelist.clear();
this.blacklist.clear();
doSort.run();
}
public void search(String query) {
view = TagRepository.instance.searchTags(query);
fireTableDataChanged();
}
@Override
public int getRowCount() {
return view.size();
}
@Override
public int getColumnCount() {
return column.length;
}
@Override
public String getColumnName(int n) {
return column[n];
}
@Override
public Object getValueAt(int row, int column) {
Tag tag = view.get(row);
switch (column) {
case 0:
return filtered.getOrDefault(tag.id, false) == true ? "X" : "";
case 1:
return filtered.getOrDefault(tag.id, true) == false ? "X" : "";
case 2:
return view.get(row).label;
case 3:
return view.get(row).description;
case 4:
return Long.toString(TagRepository.instance.getTagCardCount(view.get(row)));
}
return "";
}
public void doubleClick(int index) {
Tag tag = view.get(index);
if (!filtered.containsKey(tag.id)) {
filtered.put(tag.id, true);
} else if (filtered.get(tag.id)) {
filtered.put(tag.id, false);
} else {
filtered.remove(tag.id);
}
fireTableCellUpdated(index, 0);
fireTableCellUpdated(index, 1);
buildBlacklist();
buildWhitelist();
doSort.run();
}
public void addListeners(final JTable table) {
// sorts
MouseListener mouse = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e)) {
return;
}
TableColumnModel columnModel = table.getColumnModel();
int viewColumn = columnModel.getColumnIndexAtX(e.getX());
int column = table.convertColumnIndexToModel(viewColumn);
if (column != -1) {
descending = !descending;
columnSortedBy = column;
sort();
fireTableDataChanged();
}
}
};
table.getTableHeader().addMouseListener(mouse);
// updates card detail, listens to any mouse clicks
table.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (!SwingUtilities.isLeftMouseButton(e)) {
return;
}
if (e.getClickCount() % 2 == 0) {
doubleClick(table.getSelectedRow());
}
}
});
}
public boolean sort() {
switch (columnSortedBy) {
// I do not care.
case 0:
view.sort((t1, t2) -> (filtered.getOrDefault(t1.id, false) == true ? "X" : "").compareTo((filtered.getOrDefault(t2.id, false) == true ? "X" : "")));
break;
case 1:
view.sort((t1, t2) -> (filtered.getOrDefault(t1.id, true) == false ? "X" : "").compareTo((filtered.getOrDefault(t2.id, true) == false ? "X" : "")));
break;
case 2:
view.sort((t1, t2) -> ObjectUtils.compare(t1.label, t2.label));
break;
case 3:
view.sort((t1, t2) -> ObjectUtils.compare(t1.description, t2.description));
break;
case 4:
view.sort((t1, t2) -> TagRepository.instance.getTagCardCount(t2) - TagRepository.instance.getTagCardCount(t1));
break;
}
if (descending) {
Collections.reverse(view);
}
fireTableDataChanged();
return true;
}
public Set<String> buildWhitelist() {
whitelist.clear();
for (String key : filtered.keySet()) {
if (filtered.get(key)) {
for (CardInfo card : TagRepository.instance.getCardsByTagId(key)) {
whitelist.add(card.getName());
}
}
}
return whitelist;
}
public Set<String> buildBlacklist() {
blacklist.clear();
for (String key : filtered.keySet()) {
if (!filtered.get(key)) {
for (CardInfo card : TagRepository.instance.getCardsByTagId(key)) {
blacklist.remove(card.getName());
}
}
}
return blacklist;
}
public boolean testCard(Card card) {
if (!hasAnySelected()) {
return true;
}
return !blacklist.contains(card.getName()) && (whitelist.size() == 0 || whitelist.contains(card.getName()));
}
public boolean hasAnySelected() {
return filtered.size() > 0;
}
public int getRecentSortedColumn() {
return recentSortedColumn;
}
public boolean isRecentAscending() {
return recentAscending;
}
}

View file

@ -18,5 +18,7 @@ public enum ClientEventType {
DRAFT_PICK_CARD,
DRAFT_MARK_CARD,
//
PLAYER_TYPE_CHANGED
PLAYER_TYPE_CHANGED,
//
TAG_DOUBLE_CLICK
}

View file

@ -32,7 +32,7 @@ public class ScryfallApiCard {
transient public String imageLarge = "";
// potentially interesting fields, can be used in other places
//public UUID oracle_id; // TODO: implement card hint with oracle/cr ruling texts (see Rulings bulk data)
public String oracle_id; // TODO: implement card hint with oracle/cr ruling texts (see Rulings bulk data)
//public Integer edhrec_rank; // TODO: use it to rating cards for AI and draft bots
//public Object legalities; // TODO: add verify check for bans list
//public Boolean full_art; // TODO: add verify check for full art usage in sets

View file

@ -6,6 +6,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import mage.MageException;
import mage.cards.repository.CardRepository;
import mage.client.remote.XmageURLConnection;
import mage.client.util.CardLanguage;
import mage.util.JsonUtil;
@ -485,6 +486,9 @@ public class ScryfallImageSource implements CardImageSource {
continue;
}
// I LOVE POTENTIAL SQL INJECTION!!!!!
CardRepository.instance.execSQL("UPDATE card SET oracleId = '"+ card.oracle_id +"' WHERE setCode = '"+ card.set +"' AND cardNumber = '"+ card.collector_number +"'");
// keep only usefully languages
// memory optimization: fewer items, from 470 MB to 96 MB

View file

@ -0,0 +1,49 @@
package org.mage.plugins.card.dl.sources;
import java.util.List;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import mage.cards.repository.Tag;
import mage.cards.repository.TagRepository;
import mage.client.remote.XmageURLConnection;
class RawTag {
public String object;
public String id;
public String label;
public String type;
public String description;
public List<String> oracle_ids;
}
class OracleResponse {
public String object;
public boolean has_more;
public List<RawTag> data;
}
public enum TagSource {
instance;
private static final Logger LOGGER = Logger.getLogger(TagSource.class);
public void syncTagRepositiory() {
String oracle = XmageURLConnection.downloadText("https://api.scryfall.com/private/tags/oracle");
OracleResponse response = new Gson().fromJson(oracle, OracleResponse.class);
String tagCount = Integer.toString(response.data.size());
int i = 0;
for (RawTag rawTag : response.data) {
LOGGER.debug("Syncing tag: " + rawTag.label + " (" + Integer.toString(i) + "/" + tagCount + ")");
Tag tag = new Tag();
tag.id = rawTag.id;
tag.description = rawTag.description;
tag.label = rawTag.label;
TagRepository.instance.syncTag(tag, rawTag.oracle_ids);
}
}
}

View file

@ -40,6 +40,8 @@ public class CardInfo {
protected String setCode;
@DatabaseField(indexName = "setCode_cardNumber_index")
protected String cardNumber;
@DatabaseField(indexName = "oracleId_index", canBeNull = true)
protected String oracleId;
/**
* Fast access to numerical card number (number without prefix/postfix: 123b -> 123)
*/

View file

@ -38,7 +38,7 @@ public enum CardRepository {
// TODO: delete db version from cards and expansions due un-used (cause dbs re-created on each update now)
private static final String VERSION_ENTITY_NAME = "card";
private static final long CARD_DB_VERSION = 54; // raise this if db structure was changed
private static final long CARD_DB_VERSION = 55; // raise this if db structure was changed
private static final long CARD_CONTENT_VERSION = 241; // raise this if new cards were added to the server
private Dao<CardInfo, Object> cardsDao;
@ -579,6 +579,16 @@ public enum CardRepository {
return Collections.emptyList();
}
public List<CardInfo> findCardsWithTagRelations(List<TagRelation> tagRelations) {
try {
List<CardInfo> result = cardsDao.queryBuilder().where().in("oracleId", tagRelations.stream().map(id -> id.oracle_id).toArray()).query();
return result;
} catch (SQLException e) {
e.printStackTrace();
return new ArrayList<CardInfo>();
}
}
public List<CardInfo> findCards(String name, long limitByMaxAmount) {
return findCards(name, limitByMaxAmount, false, true);
}

View file

@ -0,0 +1,20 @@
package mage.cards.repository;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
/**
* @author JayDi85
*/
@DatabaseTable(tableName = "tag")
public class Tag {
@DatabaseField(id = true)
public String id;
@DatabaseField()
public String label;
@DatabaseField(canBeNull = true)
public String description;
}

View file

@ -0,0 +1,18 @@
package mage.cards.repository;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
/**
* @author JayDi85
*/
@DatabaseTable(tableName = "tag_relation")
public class TagRelation {
@DatabaseField(indexName = "tags_oracle_id_index", uniqueCombo = true)
protected String oracle_id;
@DatabaseField(indexName = "tags_tag_id_index", foreign = true, columnName = "tag_id")
protected Tag tag;
}

View file

@ -0,0 +1,184 @@
package mage.cards.repository;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.jdbc.JdbcConnectionSource;
import com.j256.ormlite.stmt.DeleteBuilder;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.table.TableUtils;
import mage.constants.SetType;
import org.apache.log4j.Logger;
import java.io.File;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author North, JayDi85
*/
public enum TagRepository {
instance;
private static final Logger logger = Logger.getLogger(TagRepository.class);
// fixes limit for out of memory problems
private static final AtomicInteger databaseFixes = new AtomicInteger();
private static final int MAX_DATABASE_FIXES = 10;
private static final String VERSION_ENTITY_NAME = "tags";
private static final long TAG_VERSION = 2; // raise this if db structure was changed
private static final long TAG_RELATION_VERSION = 2; // raise this if new cards were added to the server
private Dao<Tag, Object> tagsDao;
private Dao<TagRelation, Object> tagRelationDao;
TagRepository() {
File file = new File("db");
if (!file.exists()) {
file.mkdirs();
}
try {
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
boolean isObsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, TAG_VERSION);
boolean isNewBuild = RepositoryUtil.isNewBuildRun(connectionSource, VERSION_ENTITY_NAME, TagRepository.class); // recreate db on new build
if (isObsolete || isNewBuild) {
//System.out.println("Local cards db is outdated, cleaning...");
TableUtils.dropTable(connectionSource, TagRelation.class, true);
TableUtils.dropTable(connectionSource, Tag.class, true);
}
TableUtils.createTableIfNotExists(connectionSource, Tag.class);
TableUtils.createTableIfNotExists(connectionSource, TagRelation.class);
tagsDao = DaoManager.createDao(connectionSource, Tag.class);
tagRelationDao = DaoManager.createDao(connectionSource, TagRelation.class);
} catch (SQLException e) {
Logger.getLogger(TagRepository.class).error("Error creating tags repository - " + e, e);
}
}
public List<Tag> getTagsFromCard(CardInfo card) {
try {
List<TagRelation> relations = tagRelationDao.queryForEq("oracle_id", card.oracleId);
return tagsDao.queryBuilder().where().in("id", relations.stream().map((TagRelation rel) -> rel.tag)).query();
} catch (SQLException e) {
return new ArrayList<Tag>();
}
}
public List<CardInfo> getCardsByTag(Tag tag) {
try {
List<TagRelation> relations = tagRelationDao.queryForEq("tag_id", tag.id);
return CardRepository.instance.findCardsWithTagRelations(relations);
} catch (SQLException e) {
e.printStackTrace();
return new ArrayList<CardInfo>();
}
}
public List<CardInfo> getCardsByTagId(String id) {
try {
return getCardsByTag(tagsDao.queryForId(id));
} catch (SQLException e) {
e.printStackTrace();
return new ArrayList<CardInfo>();
}
}
public void syncTag(final Tag tag, List<String> oracleIds ) {
if (tag == null) {
return;
}
try {
tagsDao.createOrUpdate(tag);
tagRelationDao.callBatchTasks(() -> {
// only add new cards (no updates)
logger.info("DB: refreshing tag " + tag.label);
// clear out old ones
DeleteBuilder<TagRelation, Object> cleanser = tagRelationDao.deleteBuilder();
cleanser.where().eq("tag_id", tag.id);
cleanser.delete();
try {
for (String oracleId : oracleIds) {
TagRelation relation = new TagRelation();
relation.oracle_id = oracleId;
relation.tag = tag;
tagRelationDao.create(relation);
}
} catch (SQLException e) {
Logger.getLogger(TagRepository.class).error("Error adding tags to DB - " + e, e);
}
return null;
});
} catch (Exception e) {
e.printStackTrace();
}
try {
} catch (Exception ex) {
//
}
}
public void closeDB(boolean writeCompact) {
try {
if (tagsDao != null && tagsDao.getConnectionSource() != null) {
DatabaseConnection conn = tagsDao.getConnectionSource().getReadWriteConnection(tagsDao.getTableName());
if (writeCompact) {
conn.executeStatement("SHUTDOWN COMPACT", DatabaseConnection.DEFAULT_RESULT_FLAGS); // compact data and rewrite whole db
} else {
conn.executeStatement("SHUTDOWN IMMEDIATELY", DatabaseConnection.DEFAULT_RESULT_FLAGS); // close without any writes
}
tagsDao.getConnectionSource().releaseConnection(conn);
}
} catch (SQLException ignore) {
}
}
public void openDB() {
try {
ConnectionSource connectionSource = new JdbcConnectionSource(DatabaseUtils.prepareH2Connection(DatabaseUtils.DB_NAME_CARDS, true));
tagsDao = DaoManager.createDao(connectionSource, Tag.class);
tagRelationDao = DaoManager.createDao(connectionSource, TagRelation.class);
} catch (SQLException e) {
Logger.getLogger(TagRepository.class).error("Error opening tag repository - " + e, e);
}
}
public List<Tag> getAllTags() {
try {
return tagsDao.queryForAll();
} catch (SQLException e) {
return new ArrayList<Tag>();
}
}
public List<Tag> searchTags(String query) {
try {
return tagsDao.queryBuilder().where().like("label", "%"+query.replace(' ', '-')+"%").query();
} catch (SQLException e) {
return new ArrayList<Tag>();
}
}
public int getTagCardCount(Tag tag) {
try {
return (int) tagRelationDao.queryBuilder().where().eq("tag_id", tag.id).countOf();
} catch (SQLException e) {
return 0;
}
}
}