package com.zulip.android.models; import android.util.Log; import com.j256.ormlite.dao.RuntimeExceptionDao; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.misc.BaseDaoEnabled; import com.j256.ormlite.misc.TransactionManager; import com.j256.ormlite.stmt.Where; import com.j256.ormlite.table.DatabaseTable; import com.zulip.android.ZulipApp; import com.zulip.android.util.ZLog; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import java.sql.SQLException; import java.util.List; import java.util.concurrent.Callable; /** * Ranges of messages we have received. */ @DatabaseTable(tableName = "ranges") public class MessageRange extends BaseDaoEnabled<MessageRange, Integer> { @DatabaseField() public int low; @DatabaseField() public int high; @DatabaseField(generatedId = true) private int id; public MessageRange() { } public MessageRange(int low, int high) { this.low = low; this.high = high; } public static MessageRange getRangeContaining(int value, RuntimeExceptionDao<MessageRange, Integer> messageRangeDao) { List<MessageRange> ranges; try { ranges = messageRangeDao.queryBuilder().where().le("low", value) .and().ge("high", value).query(); if (ranges.size() == 1) { return ranges.get(0); } else if (!ranges.isEmpty()) { Log.wtf("rangecheck", "Expected one range, got " + ranges.size() + " when looking for ID " + value); } } catch (SQLException e) { // This is nonfatal. ZLog.logException(e); } return null; } // / Update or create the final range for new messages from server events public static void updateNewMessagesRange(ZulipApp app, int maxId) { synchronized (app.updateRangeLock) { RuntimeExceptionDao<MessageRange, Integer> rangeDao = app .getDao(MessageRange.class); MessageRange currentRange = MessageRange.getRangeContaining( app.getMaxMessageId(), rangeDao); if (currentRange == null) { currentRange = new MessageRange(app.getMaxMessageId(), app.getMaxMessageId()); } if (currentRange.high <= maxId) { currentRange.high = maxId; rangeDao.createOrUpdate(currentRange); } } app.setMaxMessageId(maxId); } // Create a range for fetched messages, merging with other ranges if // necessary. Messages between low and high (both inclusive) must exist in // the DB. public static void markRange(ZulipApp app, final int low, final int high) { final RuntimeExceptionDao<MessageRange, Integer> messageRangeDao = app .getDao(MessageRange.class); try { synchronized (app.updateRangeLock) { TransactionManager.callInTransaction(app.getDatabaseHelper() .getConnectionSource(), new Callable<Void>() { public Void call() throws Exception { Where<MessageRange, Integer> where = messageRangeDao .queryBuilder().orderBy("low", true).where(); @SuppressWarnings("unchecked") List<MessageRange> ranges = where.or( where.and(where.ge("high", low - 1), where.le("high", high + 1)), where.and(where.ge("low", low - 1), where.le("low", high + 1))).query(); MessageRange rng = new MessageRange(low, high); if (!ranges.isEmpty()) { Log.i("", "our low: " + rng.low + ", our high: " + rng.high); int dbLow = ranges.get(0).low; int dbHigh = ranges.get(ranges.size() - 1).high; Log.i("", "their low: " + dbLow + ", their high: " + dbHigh); if (dbLow < rng.low) { rng.low = dbLow; } if (dbHigh > rng.high) { rng.high = dbHigh; } messageRangeDao.delete(ranges); } messageRangeDao.createOrUpdate(rng); return null; } }); } } catch (SQLException e) { throw new RuntimeException(e); } } @Override public int hashCode() { return new HashCodeBuilder(17, 31).append(low).append(high) .toHashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj.getClass() != getClass()) { return false; } MessageRange rhs = (MessageRange) obj; return new EqualsBuilder().append(this.low, rhs.low) .append(this.high, rhs.high).isEquals(); } }