mirror of https://github.com/kurisufriend/Clover
parent
98333e2588
commit
9db1044fbd
@ -0,0 +1,126 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.database; |
||||||
|
|
||||||
|
import com.j256.ormlite.stmt.QueryBuilder; |
||||||
|
import com.j256.ormlite.table.TableUtils; |
||||||
|
|
||||||
|
import org.floens.chan.core.model.History; |
||||||
|
import org.floens.chan.utils.Time; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
public class DatabaseHistoryManager { |
||||||
|
private static final String TAG = "DatabaseHistoryManager"; |
||||||
|
|
||||||
|
private static final long HISTORY_TRIM_TRIGGER = 100; |
||||||
|
private static final long HISTORY_TRIM_COUNT = 50; |
||||||
|
|
||||||
|
private DatabaseManager databaseManager; |
||||||
|
private DatabaseHelper helper; |
||||||
|
private DatabaseLoadableManager databaseLoadableManager; |
||||||
|
|
||||||
|
public DatabaseHistoryManager(DatabaseManager databaseManager, DatabaseHelper helper, DatabaseLoadableManager databaseLoadableManager) { |
||||||
|
this.databaseManager = databaseManager; |
||||||
|
this.helper = helper; |
||||||
|
this.databaseLoadableManager = databaseLoadableManager; |
||||||
|
} |
||||||
|
|
||||||
|
public void add(History history) { |
||||||
|
databaseManager.runTaskSync(addHistory(history)); |
||||||
|
} |
||||||
|
|
||||||
|
public Callable<Void> load() { |
||||||
|
return new Callable<Void>() { |
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
databaseManager.trimTable(helper.historyDao, "history", HISTORY_TRIM_TRIGGER, HISTORY_TRIM_COUNT); |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public Callable<List<History>> getHistory() { |
||||||
|
return new Callable<List<History>>() { |
||||||
|
@Override |
||||||
|
public List<History> call() throws Exception { |
||||||
|
QueryBuilder<History, Integer> historyQuery = helper.historyDao.queryBuilder(); |
||||||
|
List<History> date = historyQuery.orderBy("date", false).query(); |
||||||
|
for (int i = 0; i < date.size(); i++) { |
||||||
|
History history = date.get(i); |
||||||
|
history.loadable = databaseLoadableManager.refreshForeign(history.loadable); |
||||||
|
} |
||||||
|
return date; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public Callable<History> addHistory(final History history) { |
||||||
|
if (!history.loadable.isThreadMode()) { |
||||||
|
throw new IllegalArgumentException("History loadables must be in thread mode"); |
||||||
|
} |
||||||
|
|
||||||
|
if (history.loadable.id == 0) { |
||||||
|
throw new IllegalArgumentException("History loadable is not yet in the db"); |
||||||
|
} |
||||||
|
|
||||||
|
return new Callable<History>() { |
||||||
|
@Override |
||||||
|
public History call() throws Exception { |
||||||
|
QueryBuilder<History, Integer> builder = helper.historyDao.queryBuilder(); |
||||||
|
List<History> existingHistories = builder.where().eq("loadable_id", history.loadable.id).query(); |
||||||
|
History existingHistoryForLoadable = existingHistories.isEmpty() ? null : existingHistories.get(0); |
||||||
|
|
||||||
|
if (existingHistoryForLoadable != null) { |
||||||
|
existingHistoryForLoadable.date = Time.get(); |
||||||
|
helper.historyDao.update(existingHistoryForLoadable); |
||||||
|
} else { |
||||||
|
history.date = Time.get(); |
||||||
|
helper.historyDao.create(history); |
||||||
|
} |
||||||
|
|
||||||
|
return history; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public Callable<Void> removeHistory(final History history) { |
||||||
|
return new Callable<Void>() { |
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
helper.historyDao.delete(history); |
||||||
|
return null; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public Callable<Void> clearHistory() { |
||||||
|
return new Callable<Void>() { |
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
long start = Time.startTiming(); |
||||||
|
TableUtils.clearTable(helper.getConnectionSource(), History.class); |
||||||
|
Time.endTiming("Clear history table", start); |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,172 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.database; |
||||||
|
|
||||||
|
import android.util.Log; |
||||||
|
|
||||||
|
import com.j256.ormlite.stmt.QueryBuilder; |
||||||
|
|
||||||
|
import org.floens.chan.core.model.Loadable; |
||||||
|
import org.floens.chan.utils.Logger; |
||||||
|
import org.floens.chan.utils.Time; |
||||||
|
|
||||||
|
import java.sql.SQLException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.concurrent.Callable; |
||||||
|
|
||||||
|
public class DatabaseLoadableManager { |
||||||
|
private static final String TAG = "DatabaseLoadableManager"; |
||||||
|
|
||||||
|
private DatabaseManager databaseManager; |
||||||
|
private DatabaseHelper helper; |
||||||
|
|
||||||
|
private Map<Loadable, Loadable> cachedLoadables = new HashMap<>(); |
||||||
|
|
||||||
|
public DatabaseLoadableManager(DatabaseManager databaseManager, DatabaseHelper helper) { |
||||||
|
this.databaseManager = databaseManager; |
||||||
|
this.helper = helper; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Called when the application goes into the background, to do intensive update calls for loadables |
||||||
|
* whose list indexes or titles have changed. |
||||||
|
*/ |
||||||
|
public Callable<Void> flush() { |
||||||
|
return new Callable<Void>() { |
||||||
|
@Override |
||||||
|
public Void call() throws Exception { |
||||||
|
List<Loadable> toFlush = new ArrayList<>(); |
||||||
|
for (Loadable loadable : cachedLoadables.values()) { |
||||||
|
if (loadable.dirty) { |
||||||
|
loadable.dirty = false; |
||||||
|
toFlush.add(loadable); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!toFlush.isEmpty()) { |
||||||
|
Logger.d(TAG, "Flushing " + toFlush.size() + " loadable(s) list positions"); |
||||||
|
long start = Time.startTiming(); |
||||||
|
for (int i = 0; i < toFlush.size(); i++) { |
||||||
|
Loadable loadable = toFlush.get(i); |
||||||
|
helper.loadableDao.update(loadable); |
||||||
|
} |
||||||
|
Time.endTiming("Loadable flushing", start); |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* All loadables that are not gotten from a database (like from any of the Loadable.for...() factory methods) |
||||||
|
* need to go through this method to correctly get a loadable if it already existed in the db. |
||||||
|
* <p>It will search the database for existing loadables of the mode is THREAD, and return one of those if there is |
||||||
|
* else it will create the loadable in the database and return the given loadable. |
||||||
|
* |
||||||
|
* @param loadable Loadable to search from that was not yet gotten from the db. |
||||||
|
* @return a loadable ready to use. |
||||||
|
*/ |
||||||
|
public Loadable get(final Loadable loadable) { |
||||||
|
if (loadable.id != 0) { |
||||||
|
throw new IllegalArgumentException("get() only works for transient loadables"); |
||||||
|
} |
||||||
|
|
||||||
|
// We only cache THREAD loadables in the db
|
||||||
|
if (loadable.isThreadMode()) { |
||||||
|
long start = Time.startTiming(); |
||||||
|
Loadable result = databaseManager.runTaskSync(getLoadable(loadable)); |
||||||
|
Time.endTiming("get loadable from db " + loadable.board, start); |
||||||
|
return result; |
||||||
|
} else { |
||||||
|
return loadable; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Call this when you use a thread loadable as a foreign object on your table |
||||||
|
* <p>It will correctly update the loadable cache |
||||||
|
* |
||||||
|
* @param loadable Loadable that only has its id loaded |
||||||
|
* @return a loadable ready to use. |
||||||
|
* @throws SQLException |
||||||
|
*/ |
||||||
|
public Loadable refreshForeign(final Loadable loadable) throws SQLException { |
||||||
|
if (loadable.id == 0) { |
||||||
|
throw new IllegalArgumentException("This only works loadables that have their id loaded"); |
||||||
|
} |
||||||
|
|
||||||
|
// If the loadable was already loaded in the cache, return that entry
|
||||||
|
for (Loadable key : cachedLoadables.keySet()) { |
||||||
|
if (key.id == loadable.id) { |
||||||
|
return key; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Add it to the cache, refresh contents
|
||||||
|
helper.loadableDao.refresh(loadable); |
||||||
|
cachedLoadables.put(loadable, loadable); |
||||||
|
return loadable; |
||||||
|
} |
||||||
|
|
||||||
|
private Callable<Loadable> getLoadable(final Loadable loadable) { |
||||||
|
if (!loadable.isThreadMode()) { |
||||||
|
throw new IllegalArgumentException("getLoadable can only be used for thread loadables"); |
||||||
|
} |
||||||
|
|
||||||
|
return new Callable<Loadable>() { |
||||||
|
@Override |
||||||
|
public Loadable call() throws Exception { |
||||||
|
Loadable cachedLoadable = cachedLoadables.get(loadable); |
||||||
|
if (cachedLoadable != null) { |
||||||
|
Logger.v(TAG, "Cached loadable found"); |
||||||
|
return cachedLoadable; |
||||||
|
} else { |
||||||
|
QueryBuilder<Loadable, Integer> builder = helper.loadableDao.queryBuilder(); |
||||||
|
List<Loadable> results = builder.where() |
||||||
|
.eq("mode", loadable.mode) |
||||||
|
.and().eq("board", loadable.board) |
||||||
|
.and().eq("no", loadable.no) |
||||||
|
.query(); |
||||||
|
|
||||||
|
if (results.size() > 1) { |
||||||
|
Log.w(TAG, "Multiple loadables found for where Loadable.equals() would return true"); |
||||||
|
for (Loadable result : results) { |
||||||
|
Log.w(TAG, result.toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Loadable result = results.isEmpty() ? null : results.get(0); |
||||||
|
if (result == null) { |
||||||
|
Log.d(TAG, "Creating loadable"); |
||||||
|
helper.loadableDao.create(loadable); |
||||||
|
result = loadable; |
||||||
|
} else { |
||||||
|
Log.d(TAG, "Loadable found in db"); |
||||||
|
} |
||||||
|
|
||||||
|
cachedLoadables.put(result, result); |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -1,50 +0,0 @@ |
|||||||
/* |
|
||||||
* Clover - 4chan browser https://github.com/Floens/Clover/
|
|
||||||
* Copyright (C) 2014 Floens |
|
||||||
* |
|
||||||
* This program is free software: you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU General Public License as published by |
|
||||||
* the Free Software Foundation, either version 3 of the License, or |
|
||||||
* (at your option) any later version. |
|
||||||
* |
|
||||||
* This program is distributed in the hope that it will be useful, |
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
* GNU General Public License for more details. |
|
||||||
* |
|
||||||
* You should have received a copy of the GNU General Public License |
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/ |
|
||||||
package org.floens.chan.core.pool; |
|
||||||
|
|
||||||
import org.floens.chan.core.model.Loadable; |
|
||||||
|
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
public class LoadablePool { |
|
||||||
private static final LoadablePool instance = new LoadablePool(); |
|
||||||
|
|
||||||
private Map<Loadable, Loadable> pool = new HashMap<>(); |
|
||||||
|
|
||||||
private LoadablePool() { |
|
||||||
} |
|
||||||
|
|
||||||
public static LoadablePool getInstance() { |
|
||||||
return instance; |
|
||||||
} |
|
||||||
|
|
||||||
public Loadable obtain(Loadable searchLoadable) { |
|
||||||
if (searchLoadable.isThreadMode()) { |
|
||||||
Loadable poolLoadable = pool.get(searchLoadable); |
|
||||||
if (poolLoadable == null) { |
|
||||||
poolLoadable = searchLoadable; |
|
||||||
pool.put(poolLoadable, poolLoadable); |
|
||||||
} |
|
||||||
|
|
||||||
return poolLoadable; |
|
||||||
} else { |
|
||||||
return searchLoadable; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue