package me.desht.chesscraft.results; import me.desht.chesscraft.chess.ai.ChessAI; import me.desht.chesscraft.exceptions.ChessException; import me.desht.dhutils.Debugger; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.*; import java.util.Map.Entry; /** * Abstract base class to represent a view on the raw results data. Subclass * this and implement the addResult() and getInitialScore() methods. */ public abstract class ResultViewBase { private final Results handler; private final String viewType; private final Map<String, Integer> scoreMap; private boolean updateDatabase = true; ResultViewBase(Results handler, String viewType) { this.viewType = viewType; this.handler = handler; this.scoreMap = new HashMap<String, Integer>(); } public abstract void addResult(ResultEntry re); protected abstract int getInitialScore(); /** * @return the viewType */ public String getViewType() { return viewType; } /** * Rebuild this view's data from the raw result records. This is done right after the database data * has been reloaded. */ void rebuild() { // don't push out database updates here; we've only just read the data in updateDatabase = false; for (ResultEntry re : handler.getEntries()) { addResult(re); } updateDatabase = true; } /** * Get a list of all player scores, highest first. * * @return A list of score records (player, score) */ public void getScores() { getScores(0, false); } /** * Get the top N scores on the server, highest first. * * @param count The number of players to return * @param excludeAI true if AI scores should be excluded * @throws ChessException if called before data has finished being restored from DB */ public List<ScoreRecord> getScores(final int count, final boolean excludeAI) { if (!handler.isDatabaseLoaded()) { throw new ChessException("No results data is available yet"); } List<ScoreRecord> res = new ArrayList<ScoreRecord>(); List<Entry<String, Integer>> list = new ArrayList<Entry<String,Integer>>(); for (Entry<String,Integer> entry : scoreMap.entrySet()) { if (excludeAI && ChessAI.isAIPlayer(entry.getKey())) { continue; } list.add(entry); } Collections.sort(list, new Comparator<Entry<String, Integer>>() { public int compare(Entry<String, Integer> m1, Entry<String, Integer> m2) { return (m2.getValue()).compareTo(m1.getValue()); } }); int n = 0; for (Entry<String,Integer> entry : list) { if (count > 0 && n++ > count) { break; } res.add(new ScoreRecord(entry.getKey(), entry.getValue())); } return res; } protected void awardPoints(String player, int score) { int current = getScore(player); setScore(player, current + score); } /** * Set the score for the given player. * * @param player * @param score */ public void setScore(String player, int score) { scoreMap.put(player, score); if (updateDatabase) { handler.queueDatabaseUpdate(new ViewScoreUpdate(player, score, viewType)); } } /** * Get the score for the given player. If the player is not yet in the database, * return the initial score (and add the player with that score). * * @param player The player to check for * @return The player's score */ public int getScore(String player) { if (!scoreMap.containsKey(player)) { scoreMap.put(player, getInitialScore()); } return scoreMap.get(player); } private class ViewScoreUpdate implements DatabaseSavable { private final String player; private final int score; private final String tableName; private ViewScoreUpdate(String player, int score, String tableName) { this.player = player; this.score = score; this.tableName = tableName; } @Override public void saveToDatabase(Connection conn) throws SQLException { String fullName = Results.getResultsHandler().getTableName(tableName); PreparedStatement getPlayer = conn.prepareStatement("SELECT player, score FROM " + fullName + " WHERE player = ?"); getPlayer.setString(1, player); ResultSet rs = getPlayer.executeQuery(); PreparedStatement update; if (!rs.next()) { // new insertion update = conn.prepareStatement("INSERT INTO " + fullName + " VALUES (?,?)"); update.setString(1, player); update.setInt(2, score); } else if (score != rs.getInt(2)) { // update existing update = conn.prepareStatement("UPDATE " + fullName + " SET score = ? WHERE player = ?"); update.setString(2, player); update.setInt(1, score); } else { return; } Debugger.getInstance().debug("execute SQL: " + update); update.executeUpdate(); } } }