/* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ package mage.cards.repository; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; import com.j256.ormlite.dao.GenericRawResults; import com.j256.ormlite.jdbc.JdbcConnectionSource; import com.j256.ormlite.stmt.QueryBuilder; import com.j256.ormlite.stmt.SelectArg; import com.j256.ormlite.stmt.Where; import com.j256.ormlite.support.ConnectionSource; import com.j256.ormlite.support.DatabaseConnection; import com.j256.ormlite.table.TableUtils; import java.io.File; import java.sql.SQLException; import java.util.*; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SetType; import mage.util.RandomUtil; import org.apache.log4j.Logger; /** * @author North */ public enum CardRepository { instance; private static final String JDBC_URL = "jdbc:h2:file:./db/cards.h2;AUTO_SERVER=TRUE"; private static final String VERSION_ENTITY_NAME = "card"; // raise this if db structure was changed private static final long CARD_DB_VERSION = 51; // raise this if new cards were added to the server private static final long CARD_CONTENT_VERSION = 79; private final TreeSet<String> landTypes = new TreeSet<>(); private Dao<CardInfo, Object> cardDao; private Set<String> classNames; CardRepository() { File file = new File("db"); if (!file.exists()) { file.mkdirs(); } try { ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL); boolean obsolete = RepositoryUtil.isDatabaseObsolete(connectionSource, VERSION_ENTITY_NAME, CARD_DB_VERSION); if (obsolete) { TableUtils.dropTable(connectionSource, CardInfo.class, true); } TableUtils.createTableIfNotExists(connectionSource, CardInfo.class); cardDao = DaoManager.createDao(connectionSource, CardInfo.class); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error creating card repository - ", ex); } } public void addCards(final List<CardInfo> cards) { try { cardDao.callBatchTasks(() -> { try { for (CardInfo card : cards) { cardDao.create(card); if (classNames != null) { classNames.add(card.getClassName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error adding cards to DB - ", ex); } return null; }); } catch (Exception ex) { } } public boolean cardExists(String className) { try { if (classNames == null) { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("className").where().isNotNull("className"); List<CardInfo> results = cardDao.query(qb.prepare()); classNames = new TreeSet<>(); for (CardInfo card : results) { classNames.add(card.getClassName()); } } return classNames.contains(className); } catch (SQLException ex) { } return false; } public boolean cardExists(CardSetInfo className) { try { if (classNames == null) { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("className").where().isNotNull("className"); List<CardInfo> results = cardDao.query(qb.prepare()); classNames = new TreeSet<>(); for (CardInfo card : results) { classNames.add(card.getClassName()); } } return classNames.contains(className.getName()); } catch (SQLException ex) { } return false; } public Set<String> getNames() { Set<String> names = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("name"); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { int result = card.getName().indexOf(" // "); if (result > 0) { names.add(card.getName().substring(0, result)); names.add(card.getName().substring(result + 4)); } else { names.add(card.getName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting names from DB : " + ex); } return names; } public Set<String> getNonLandNames() { Set<String> names = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("name"); qb.where().not().like("types", new SelectArg('%' + CardType.LAND.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { int result = card.getName().indexOf(" // "); if (result > 0) { names.add(card.getName().substring(0, result)); names.add(card.getName().substring(result + 4)); } else { names.add(card.getName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-land names from DB : " + ex); } return names; } public Set<String> getCreatureNames() { Set<String> names = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("name"); qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { int result = card.getName().indexOf(" // "); if (result > 0) { names.add(card.getName().substring(0, result)); names.add(card.getName().substring(result + 4)); } else { names.add(card.getName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting creature names from DB : " + ex); } return names; } public Set<String> getArtifactNames() { Set<String> names = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("name"); qb.where().like("types", new SelectArg('%' + CardType.ARTIFACT.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { int result = card.getName().indexOf(" // "); if (result > 0) { names.add(card.getName().substring(0, result)); names.add(card.getName().substring(result + 4)); } else { names.add(card.getName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting artifact names from DB : " + ex); } return names; } public Set<String> getNonLandAndNonCreatureNames() { Set<String> names = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("name"); Where where = qb.where(); where.and(where.not().like("types", '%' + CardType.CREATURE.name() + '%'), where.not().like("types", '%' + CardType.LAND.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { int result = card.getName().indexOf(" // "); if (result > 0) { names.add(card.getName().substring(0, result)); names.add(card.getName().substring(result + 4)); } else { names.add(card.getName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-land and non-creature names from DB : " + ex); } return names; } public Set<String> getNonArtifactAndNonLandNames() { Set<String> names = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("name"); Where where = qb.where(); where.and(where.not().like("types", '%' + CardType.ARTIFACT.name() + '%'), where.not().like("types", '%' + CardType.LAND.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { int result = card.getName().indexOf(" // "); if (result > 0) { names.add(card.getName().substring(0, result)); names.add(card.getName().substring(result + 4)); } else { names.add(card.getName()); } } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting non-artifact non-land names from DB : " + ex); } return names; } public Set<String> getCreatureTypes() { TreeSet<String> subtypes = new TreeSet<>(); try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("subtypes"); qb.where().like("types", new SelectArg('%' + CardType.CREATURE.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { subtypes.addAll(card.getSubTypes()); } // Removing Forest because of Dryad Arbor subtypes.remove("Forest"); // Some creature types are not directly included in card types and are added here manually subtypes.add("Blinkmoth"); subtypes.add("Camarid"); subtypes.add("Caribou"); subtypes.add("Citizen"); subtypes.add("Coward"); subtypes.add("Deserter"); subtypes.add("Germ"); subtypes.add("Graveborn"); subtypes.add("Orb"); subtypes.add("Pentavite"); subtypes.add("Pincher"); subtypes.add("Prism"); subtypes.add("Reflection"); subtypes.add("Sand"); subtypes.add("Saproling"); subtypes.add("Scion"); subtypes.add("Serf"); subtypes.add("Servo"); subtypes.add("Splinter"); subtypes.add("Survivor"); subtypes.add("Tetravite"); subtypes.add("Triskelavite"); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting creaturetypes from DB : " + ex); } return subtypes; } public Set<String> getLandTypes() { if (landTypes.isEmpty()) { try { QueryBuilder<CardInfo, Object> qb = cardDao.queryBuilder(); qb.distinct().selectColumns("subtypes"); qb.where().like("types", new SelectArg('%' + CardType.LAND.name() + '%')); List<CardInfo> results = cardDao.query(qb.prepare()); for (CardInfo card : results) { landTypes.addAll(card.getSubTypes()); } // Removing Dryad because of Dryad Arbor landTypes.remove("Dryad"); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting landtypes from DB : " + ex); } } return landTypes; } public CardInfo findCard(String setCode, String cardNumber) { try { QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder(); queryBuilder.limit(1L).where().eq("setCode", new SelectArg(setCode)).and().eq("cardNumber", cardNumber).and().eq("nightCard", false); List<CardInfo> result = cardDao.query(queryBuilder.prepare()); if (!result.isEmpty()) { return result.get(0); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error finding card from DB : " + ex); } return null; } public List<String> getClassNames() { List<String> names = new ArrayList<>(); try { List<CardInfo> results = cardDao.queryForAll(); for (CardInfo card : results) { names.add(card.getClassName()); } } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting classnames from DB : " + ex); } return names; } public List<CardInfo> getMissingCards(List<String> classNames) { try { QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder(); queryBuilder.where().not().in("className", classNames); return cardDao.query(queryBuilder.prepare()); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting missing cards from DB : " + ex); } return Collections.emptyList(); } /** * @param name * @return random card with the provided name or null if none is found */ public CardInfo findCard(String name) { List<CardInfo> cards = findCards(name); if (!cards.isEmpty()) { return cards.get(RandomUtil.nextInt(cards.size())); } return null; } public CardInfo findPreferedCoreExpansionCard(String name, boolean caseInsensitive) { List<CardInfo> cards; if (caseInsensitive) { cards = findCardsCaseInsensitive(name); } else { cards = findCards(name); } if (!cards.isEmpty()) { Date lastReleaseDate = null; Date lastExpansionDate = null; CardInfo cardToUse = null; for (CardInfo cardinfo : cards) { ExpansionInfo set = ExpansionRepository.instance.getSetByCode(cardinfo.getSetCode()); if (set != null) { if ((set.getType() == SetType.EXPANSION || set.getType() == SetType.CORE) && (lastExpansionDate == null || set.getReleaseDate().after(lastExpansionDate))) { cardToUse = cardinfo; lastExpansionDate = set.getReleaseDate(); } if (lastExpansionDate == null && (lastReleaseDate == null || set.getReleaseDate().after(lastReleaseDate))) { cardToUse = cardinfo; lastReleaseDate = set.getReleaseDate(); } } } return cardToUse; } return null; } public CardInfo findCardWPreferredSet(String name, String expansion, boolean caseInsensitive) { List<CardInfo> cards; if (caseInsensitive) { cards = findCardsCaseInsensitive(name); } else { cards = findCards(name); } if (!cards.isEmpty()) { CardInfo cardToUse = null; for (CardInfo cardinfo : cards) { if (cardinfo.getSetCode() != null && expansion != null && expansion.equalsIgnoreCase(cardinfo.getSetCode())) { return cardinfo; } } } return findPreferedCoreExpansionCard(name, true); } public List<CardInfo> findCards(String name) { try { QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder(); queryBuilder.where().eq("name", new SelectArg(name)); return cardDao.query(queryBuilder.prepare()); } catch (SQLException ex) { } return Collections.emptyList(); } public List<CardInfo> findCardsCaseInsensitive(String name) { try { String sqlName = name.toLowerCase().replaceAll("\'", "\'\'"); GenericRawResults<CardInfo> rawResults = cardDao.queryRaw( "select * from " + CardRepository.VERSION_ENTITY_NAME + " where lower(name) = '" + sqlName + '\'', cardDao.getRawRowMapper()); List<CardInfo> result = new ArrayList<>(); for (CardInfo cardinfo : rawResults) { result.add(cardinfo); } return result; } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error during execution of raw sql statement", ex); } return Collections.emptyList(); } public List<CardInfo> findCards(CardCriteria criteria) { try { QueryBuilder<CardInfo, Object> queryBuilder = cardDao.queryBuilder(); criteria.buildQuery(queryBuilder); return cardDao.query(queryBuilder.prepare()); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error during execution of card repository query statement", ex); } return Collections.emptyList(); } public long getContentVersionFromDB() { try { ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL); return RepositoryUtil.getDatabaseVersion(connectionSource, VERSION_ENTITY_NAME + "Content"); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting content version from DB - ", ex); } return 0; } public void setContentVersion(long version) { try { ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL); RepositoryUtil.updateVersion(connectionSource, VERSION_ENTITY_NAME + "Content", version); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error getting content version - ", ex); } } public long getContentVersionConstant() { return CARD_CONTENT_VERSION; } public void closeDB() { try { if (cardDao != null && cardDao.getConnectionSource() != null) { DatabaseConnection conn = cardDao.getConnectionSource().getReadWriteConnection(); conn.executeStatement("shutdown compact", 0); } } catch (SQLException ex) { } } public void openDB() { try { ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL); cardDao = DaoManager.createDao(connectionSource, CardInfo.class); } catch (SQLException ex) { Logger.getLogger(CardRepository.class).error("Error opening card repository - ", ex); } } }