/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012-2017 gnosygnu@gmail.com
XOWA is licensed under the terms of the General Public License (GPL) Version 3,
or alternatively under the terms of the Apache License Version 2.0.
You may use XOWA according to either of these licenses as is most appropriate
for your project on a case-by-case basis.
The terms of each license can be found in the source code repository:
GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.addons.wikis.searchs.searchers; import gplx.*; import gplx.xowa.*; import gplx.xowa.addons.*; import gplx.xowa.addons.wikis.*; import gplx.xowa.addons.wikis.searchs.*;
import gplx.xowa.addons.wikis.searchs.dbs.*;
import gplx.xowa.addons.wikis.searchs.searchers.rslts.*; import gplx.xowa.addons.wikis.searchs.searchers.wkrs.*; import gplx.xowa.addons.wikis.searchs.parsers.*; import gplx.xowa.addons.wikis.searchs.searchers.crts.*;
import gplx.xowa.addons.wikis.searchs.searchers.crts.visitors.*;
import gplx.core.net.*; import gplx.core.net.qargs.*;
public class Srch_search_mgr implements Gfo_invk {
private final Srch_search_addon addon;
private final Xow_wiki wiki;
private final Srch_rslt_list cache__page = new Srch_rslt_list();
private final Hash_adp_bry cache__word_counts = Hash_adp_bry.cs();
private final Srch_rslt_cache cache__rslts = new Srch_rslt_cache();
private final Srch_page_tbl_wkr page_tbl_searcher = new Srch_page_tbl_wkr();
private final Srch_crt_parser crt_parser;
private final Srch_search_cmd[] cur_cmds;
private final Object mutex = new Object();
private int search_count;
private boolean upgrade_prompted;
public Srch_search_mgr(Srch_search_addon addon, Xow_wiki wiki, Srch_text_parser parser) {
this.addon = addon; this.wiki = wiki;
crt_parser = new Srch_crt_parser(Srch_crt_scanner_syms.Dflt); // NOTE: hard-coded to dflt; should change to use qry.Phrase.Syms, but requires more work
// init cur_cmds with Noop cmd to make cancel logic below easier
int len = Srch_search_qry.Tid_len;
this.cur_cmds = new Srch_search_cmd[Srch_search_qry.Tid_len];
for (int i = 0; i < len; ++i)
cur_cmds[i] = Srch_search_cmd.Noop();
wiki.App().Cfg().Bind_many_wiki(this, wiki, Cfg__args_default);
}
public void Clear_rslts_cache() {cache__rslts.Clear();}
public void Search_cancel() {
cur_cmds[Srch_search_qry.Tid__suggest_box].Cancel();
}
public void Search(Srch_search_qry qry, Srch_rslt_cbk cbk) { // NOTE: main entry point for search
if (qry.Phrase.Orig.length == 0) return;
// handle obsolete search dbs;
if (addon.Db_mgr().Cfg().Version_id__needs_upgrade()
&& !upgrade_prompted) {
upgrade_prompted = true;
Srch_db_upgrade upgrade_mgr = new Srch_db_upgrade(wiki, addon.Db_mgr());
upgrade_mgr.Upgrade();
return;
}
// cancel existing cmd
Srch_search_cmd cur_cmd = cur_cmds[qry.Tid];
cur_cmd.Cancel();
// create new one; run it;
Srch_crt_mgr crt_mgr = crt_parser.Parse_or_invalid(qry.Phrase.Compiled);
if (crt_mgr == Srch_crt_mgr.Invalid) return; // handle "\\" which is invalid or other fatal errors
Srch_rslt_list rslts_list = cache__rslts.Get_or_new(crt_mgr.Key);
cur_cmd = new Srch_search_cmd(this, qry, crt_mgr, cbk, rslts_list);
cur_cmds[qry.Tid] = cur_cmd;
if (wiki.App().Mode().Tid_is_http()) // FUTURE: use api async flag instead; WHEN: long polling support
cur_cmd.Search();
else
gplx.core.threads.Thread_adp_.Start_by_key(gplx.xowa.apps.Xoa_thread_.Key_special_suggest, cur_cmd, Srch_search_cmd.Invk__search);
}
public void Search_async(Cancelable cxl, Srch_search_qry qry, Srch_crt_mgr crt_mgr, Srch_rslt_cbk rslt_cbk, Srch_rslt_list rslts_list) {
synchronized (mutex) { // force only one search at a time; do not (a) place around Thread_sleep; (b) reuse for any other locks
if (++search_count > 64) this.Clear(); // lazy way of clearing memory
Srch_search_ctx ctx = new Srch_search_ctx(cxl, wiki, addon, cache__page, cache__word_counts, qry, qry.Phrase.Syms, crt_mgr, rslts_list);
ctx.Score_rng.Select_init(ctx.Rslts_needed, rslts_list.Score_bgn, rslts_list.Score_len, Srch_link_wkr.Percentile_rng__calc_adj(crt_mgr.Words_nth__len()));
page_tbl_searcher.Search(ctx, rslt_cbk);
if (cxl.Canceled()) return;
Srch_link_wkr link_wkr = new Srch_link_wkr();
link_wkr.Search(rslts_list, rslt_cbk, ctx);
}
}
private void Clear() {
Gfo_usr_dlg_.Instance.Log_many("", "", "search.clear");
search_count = 0;
cache__page.Clear();
cache__word_counts.Clear();
cache__rslts.Clear();
}
public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
if (ctx.Match(k, Cfg__args_default)) this.Clear_rslts_cache(); // NOTE: must clear cache after args_dflt changed
else return Gfo_invk_.Rv_unhandled;
return this;
}
public static final String Cfg__args_default = "xowa.addon.search.special.args_default";
}