package regexgolf2.services.persistence.mappers; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import regexgolf2.model.Challenge; import regexgolf2.model.Requirement; import regexgolf2.services.persistence.PersistenceException; import regexgolf2.services.persistence.database.Database; import com.google.java.contract.Ensures; import com.google.java.contract.Requires; public class ChallengeMapper { private final Database _db; private final RequirementMapper _requirements; public ChallengeMapper(Database db, RequirementMapper requirements) { _db = db; _requirements = requirements; } /** * Returns a list of all the Challenges stored in the Database. * * @throws SQLException * If initializing failed */ @Ensures("result != null") public List<Challenge> getAll() throws PersistenceException { List<Challenge> challenges = new ArrayList<>(); String challengeSQL = "SELECT id, regex, name FROM challenges;"; try { PreparedStatement challengePS = _db.getConnection().prepareStatement(challengeSQL); ResultSet challengeRS = challengePS.executeQuery(); while (challengeRS.next()) { Challenge challenge = new Challenge(); challenge.setId(challengeRS.getInt(1)); challenge.getSampleSolution().trySetSolution(challengeRS.getString(2)); challenge.setName(challengeRS.getString(3)); challenges.add(challenge); } challengePS.close(); for (Challenge challenge : challenges) { // XXX Database requests inside a loop ... is viable with // SQLite? List<Requirement> cRequirements = _requirements.getAllFor(challenge.getId()); for (Requirement r : cRequirements) challenge.addRequirement(r); } return challenges; } catch (SQLException ex) { throw new PersistenceException(ex); } } /** * Inserts a Challenge into the database. The Challenge gets an ID that is * written into the given object. * * @throws SQLException * If initializing failed */ @Requires("challenge != null") public void insert(Challenge challenge) throws PersistenceException { challenge.setId(getNextChallengeId()); String challengeSQL = "INSERT INTO challenges (id, regex, name) VALUES (?, ?, ?);"; try { PreparedStatement challengePS = _db.getConnection().prepareStatement(challengeSQL); challengePS.setInt(1, challenge.getId()); challengePS.setString(2, challenge.getSampleSolution().getSolution()); challengePS.setString(3, challenge.getName()); challengePS.execute(); challengePS.close(); _requirements.insert(challenge.getRequirements(), challenge.getId()); } catch (SQLException ex) { throw new PersistenceException(ex); } } /** * Simply selects the max id and adds 1. This is not safe for concurrency! * Currently, this Application is designed without support for concurrent * database access. */ private int getNextChallengeId() throws PersistenceException { String sql = "SELECT CASE WHEN count(*) = 0 THEN 1 ELSE max(id) + 1 END FROM challenges"; try { PreparedStatement nextIdPS = _db.getConnection().prepareStatement(sql); ResultSet rs = nextIdPS.executeQuery(); int nextId = rs.getInt(1); rs.close(); return nextId; } catch (SQLException ex) { throw new PersistenceException(ex); } } @Requires("challenge != null") public void update(Challenge challenge) throws PersistenceException { String challengeSQL = "UPDATE challenges SET regex = ?, name = ? WHERE id = ?"; try { PreparedStatement ps = _db.getConnection().prepareStatement(challengeSQL); ps.setString(1, challenge.getSampleSolution().getSolution()); ps.setString(2, challenge.getName()); ps.setInt(3, challenge.getId()); ps.execute(); ps.close(); _requirements.delete(challenge.getId()); _requirements.insert(challenge.getRequirements(), challenge.getId()); } catch (SQLException ex) { throw new PersistenceException(ex); } } public void delete(Challenge challenge) throws PersistenceException { String challengeSQL = "DELETE FROM challenges WHERE id = ?"; try { PreparedStatement ps = _db.getConnection().prepareStatement(challengeSQL); ps.setInt(1, challenge.getId()); ps.execute(); ps.close(); // requirements are deleted via cascade by the database itself // _requirements.delete(challengeId); } catch (SQLException ex) { throw new PersistenceException(ex); } } }