package org.wikipedia.readinglist.page.database;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.wikipedia.WikipediaApp;
import org.wikipedia.concurrency.CallbackTask;
import org.wikipedia.concurrency.CallbackTask.Task;
import org.wikipedia.database.BaseDao;
import org.wikipedia.database.async.AsyncConstant;
import org.wikipedia.database.contract.ReadingListPageContract;
import org.wikipedia.database.http.HttpRowDao;
import org.wikipedia.page.PageTitle;
import org.wikipedia.readinglist.page.ReadingListPage;
import org.wikipedia.readinglist.page.ReadingListPageRow;
import org.wikipedia.readinglist.page.database.disk.DiskRowDao;
import org.wikipedia.readinglist.page.database.disk.DiskStatus;
import org.wikipedia.readinglist.page.database.disk.ReadingListPageDiskRow;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
public final class ReadingListPageDao extends BaseDao<ReadingListPageRow> {
private static final ReadingListPageDao INSTANCE = new ReadingListPageDao();
@NonNull private final HttpRowDao<ReadingListPageRow, ReadingListPageHttpRow> httpDao;
@NonNull private final DiskRowDao<ReadingListPageRow, ReadingListPageDiskRow> diskDao;
public static ReadingListPageDao instance() {
return INSTANCE;
}
@NonNull public Cursor page(@NonNull String key) {
Uri uri = ReadingListPageContract.PageWithDisk.URI;
String selection = Sql.SELECT_ROWS_WITH_KEY;
String[] selectionArgs = new String[] {key};
String order = ReadingListPageContract.PageWithDisk.ORDER_ALPHABETICAL;
return client().select(uri, selection, selectionArgs, order);
}
@NonNull public Cursor pages(@NonNull String listKey) {
Uri uri = ReadingListPageContract.PageWithDisk.URI;
String selection = Sql.SELECT_ROWS_WITH_LIST_KEY;
String[] selectionArgs = new String[] {listKey};
String order = ReadingListPageContract.PageWithDisk.ORDER_MRU;
return client().select(uri, selection, selectionArgs, order);
}
public void randomPage(@NonNull CallbackTask.Callback<PageTitle> callback) {
CallbackTask.execute(new CallbackTask.Task<PageTitle>() {
@Override public PageTitle execute() {
Cursor c = client().select(ReadingListPageContract.PageWithDisk.URI, null, null, null);
PageTitle title = null;
try {
if (c.getCount() > 0) {
c.moveToPosition(new Random().nextInt(c.getCount()));
title = ReadingListDaoProxy.pageTitle(ReadingListPage.fromCursor(c));
}
} finally {
c.close();
}
return title;
}
}, callback);
}
@Nullable public ReadingListPage findPage(@NonNull String key) {
Cursor cursor = ReadingListPageDao.instance().page(key);
try {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
return ReadingListPage.fromCursor(cursor);
}
} finally {
cursor.close();
}
return null;
}
public void deletePageFromLists(@NonNull ReadingListPage page,
@NonNull Collection<String> listKeys) {
for (String key : listKeys) {
page.removeListKey(key);
}
upsert(page);
}
public void upsertAsync(@NonNull final ReadingListPage row) {
CallbackTask.execute(new Task<Void>() {
@Override public Void execute() {
upsert(row);
return null;
}
});
}
public synchronized void upsert(@NonNull ReadingListPage row) {
if (row.listKeys().isEmpty()) {
httpDao.markDeleted(new ReadingListPageHttpRow(row));
diskDao.markDeleted(new ReadingListPageDiskRow(row));
delete(row);
} else {
httpDao.markUpserted(new ReadingListPageHttpRow(row));
if (row.diskStatus() == DiskStatus.OUTDATED) {
diskDao.markOutdated(new ReadingListPageDiskRow(row));
} else if (row.diskStatus() == DiskStatus.ONLINE || row.diskStatus() == DiskStatus.UNSAVED) {
diskDao.markOnline(new ReadingListPageDiskRow(row));
}
super.upsert(row);
}
}
public synchronized void markOutdated(@NonNull ReadingListPage row) {
diskDao.markOutdated(new ReadingListPageDiskRow(row));
}
@NonNull public synchronized Collection<ReadingListPageDiskRow> startDiskTransaction() {
Collection<ReadingListPageDiskRow> rows = queryPendingDiskTransactions();
diskDao.startTransaction(rows);
return rows;
}
public synchronized void completeDiskTransaction(@NonNull ReadingListPageDiskRow row) {
diskDao.completeTransaction(row);
if (row.dat() != null) {
super.upsert(row.dat());
}
}
public synchronized void failDiskTransaction(@NonNull Collection<ReadingListPageDiskRow> rows) {
diskDao.failTransaction(rows);
}
public void clearAsync() {
CallbackTask.execute(new Task<Void>() {
@Override public Void execute() {
clear();
return null;
}
});
}
@Override public synchronized void clear() {
httpDao.clear();
diskDao.clear();
super.clear();
}
@NonNull private Collection<ReadingListPageDiskRow> queryPendingDiskTransactions() {
Uri uri = ReadingListPageContract.DiskWithPage.URI;
String selection = Sql.SELECT_ROWS_PENDING_DISK_TRANSACTION;
final String[] selectionArgs = null;
final String order = null;
Cursor cursor = client().select(uri, selection,
selectionArgs, order);
Collection<ReadingListPageDiskRow> rows = new ArrayList<>();
try {
while (cursor.moveToNext()) {
rows.add(ReadingListPageDiskRow.fromCursor(cursor));
}
} finally {
cursor.close();
}
return rows;
}
// TODO: expose HTTP DAO methods.
private ReadingListPageDao() {
super(WikipediaApp.getInstance().getDatabaseClient(ReadingListPageRow.class));
httpDao = new HttpRowDao<>(WikipediaApp.getInstance().getDatabaseClient(ReadingListPageHttpRow.class));
diskDao = new DiskRowDao<>(WikipediaApp.getInstance().getDatabaseClient(ReadingListPageDiskRow.class));
}
private static class Sql {
private static final String SELECT_ROWS_WITH_KEY = ":keyCol == ?"
.replaceAll(":keyCol", ReadingListPageContract.Page.KEY.qualifiedName());
private static final String SELECT_ROWS_WITH_LIST_KEY = "',' || :listKeyCol || ',' like '%,' || ? || ',%'"
.replaceAll(":listKeyCol", ReadingListPageContract.Page.LIST_KEYS.qualifiedName());
private static String SELECT_ROWS_PENDING_DISK_TRANSACTION = ":transactionIdCol == :noTransactionId"
.replaceAll(":transactionIdCol", ReadingListPageContract.DiskWithPage.DISK_TRANSACTION_ID.qualifiedName())
.replaceAll(":noTransactionId", String.valueOf(AsyncConstant.NO_TRANSACTION_ID));
}
}