1
0
mirror of https://github.com/gnosygnu/xowa.git synced 2026-03-02 03:49:30 +00:00
This commit is contained in:
gnosygnu
2015-07-12 21:10:02 -04:00
commit 794b5a232f
3099 changed files with 238212 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
public class Scrib_lib_html implements Scrib_lib {
public Scrib_lib_html(Scrib_core core) {}
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, String_.Ary_empty); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.html.lua"));
return mod;
}
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
throw Exc_.new_unhandled(key);
}
}

View File

@@ -0,0 +1,245 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.langs.*;
import gplx.xowa.xtns.pfuncs.times.*; import gplx.xowa.langs.numbers.*; import gplx.xowa.xtns.pfuncs.numbers.*; import gplx.xowa.langs.durations.*;
public class Scrib_lib_language implements Scrib_lib {
public Scrib_lib_language(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.language.lua"));
notify_lang_changed_fnc = mod.Fncs_get_by_key("notify_lang_changed");
return mod;
} private Scrib_lua_proc notify_lang_changed_fnc;
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_getContLangCode: return GetContLangCode(args, rslt);
case Proc_isSupportedLanguage: return IsSupportedLanguage(args, rslt);
case Proc_isKnownLanguageTag: return IsKnownLanguageTag(args, rslt);
case Proc_isValidCode: return IsValidCode(args, rslt);
case Proc_isValidBuiltInCode: return IsValidBuiltInCode(args, rslt);
case Proc_fetchLanguageName: return FetchLanguageName(args, rslt);
case Proc_fetchLanguageNames: return FetchLanguageNames(args, rslt);
case Proc_getFallbacksFor: return GetFallbacksFor(args, rslt);
case Proc_lcfirst: return Lcfirst(args, rslt);
case Proc_ucfirst: return Ucfirst(args, rslt);
case Proc_lc: return Lc(args, rslt);
case Proc_uc: return Uc(args, rslt);
case Proc_caseFold: return CaseFold(args, rslt);
case Proc_formatNum: return FormatNum(args, rslt);
case Proc_formatDate: return FormatDate(args, rslt);
case Proc_formatDuration: return FormatDuration(args, rslt);
case Proc_getDurationIntervals: return GetDurationIntervals(args, rslt);
case Proc_parseFormattedNumber: return ParseFormattedNumber(args, rslt);
case Proc_convertPlural: return ConvertPlural(args, rslt);
case Proc_convertGrammar: return ConvertGrammar(args, rslt);
case Proc_gender: return gender(args, rslt);
case Proc_isRTL: return IsRTL(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int
Proc_getContLangCode = 0, Proc_isSupportedLanguage = 1, Proc_isKnownLanguageTag = 2
, Proc_isValidCode = 3, Proc_isValidBuiltInCode = 4, Proc_fetchLanguageName = 5, Proc_fetchLanguageNames = 6, Proc_getFallbacksFor = 7
, Proc_lcfirst = 8, Proc_ucfirst = 9, Proc_lc = 10, Proc_uc = 11, Proc_caseFold = 12
, Proc_formatNum = 13, Proc_formatDate = 14, Proc_formatDuration = 15, Proc_getDurationIntervals = 16, Proc_parseFormattedNumber = 17
, Proc_convertPlural = 18, Proc_convertGrammar = 19, Proc_gender = 20, Proc_isRTL = 21
;
public static final String
Invk_getContLangCode = "getContLangCode", Invk_isSupportedLanguage = "isSupportedLanguage", Invk_isKnownLanguageTag = "isKnownLanguageTag"
, Invk_isValidCode = "isValidCode", Invk_isValidBuiltInCode = "isValidBuiltInCode"
, Invk_fetchLanguageName = "fetchLanguageName", Invk_fetchLanguageNames = "fetchLanguageNames", Invk_getFallbacksFor = "getFallbacksFor"
, Invk_lcfirst = "lcfirst", Invk_ucfirst = "ucfirst", Invk_lc = "lc", Invk_uc = "uc", Invk_caseFold = "caseFold"
, Invk_formatNum = "formatNum", Invk_formatDate = "formatDate", Invk_formatDuration = "formatDuration", Invk_getDurationIntervals = "getDurationIntervals", Invk_parseFormattedNumber = "parseFormattedNumber"
, Invk_convertPlural = "convertPlural", Invk_convertGrammar = "convertGrammar", Invk_gender = "gender", Invk_isRTL = "isRTL"
;
private static final String[] Proc_names = String_.Ary
( Invk_getContLangCode, Invk_isSupportedLanguage, Invk_isKnownLanguageTag
, Invk_isValidCode, Invk_isValidBuiltInCode, Invk_fetchLanguageName, Invk_fetchLanguageNames, Invk_getFallbacksFor
, Invk_lcfirst, Invk_ucfirst, Invk_lc, Invk_uc, Invk_caseFold
, Invk_formatNum, Invk_formatDate, Invk_formatDuration, Invk_getDurationIntervals, Invk_parseFormattedNumber
, Invk_convertPlural, Invk_convertGrammar, Invk_gender, Invk_isRTL
);
public void Notify_lang_changed() {if (notify_lang_changed_fnc != null) core.Interpreter().CallFunction(notify_lang_changed_fnc.Id(), KeyVal_.Ary_empty);}
public boolean GetContLangCode(Scrib_proc_args args, Scrib_proc_rslt rslt) {return rslt.Init_obj(core.Ctx().Lang().Key_str());}
public boolean IsSupportedLanguage(Scrib_proc_args args, Scrib_proc_rslt rslt) {return IsKnownLanguageTag(args, rslt);}// NOTE: checks if "MessagesXX.php" exists; note that xowa has all "MessagesXX.php"; for now, assume same functionality as IsKnownLanguageTag (worst case is that a small wiki depends on a lang not being there; will need to put in a "wiki.Langs()" then)
public boolean IsKnownLanguageTag(Scrib_proc_args args, Scrib_proc_rslt rslt) { // NOTE: checks if in languages/Names.php
String lang_code = args.Cast_str_or_null(0);
boolean exists = false;
if ( lang_code != null // null check; protecting against Module passing in nil from lua
&& String_.Eq(lang_code, String_.Lower(lang_code)) // must be lower-case; REF.MW: $code === strtolower( $code )
&& Xol_lang_itm_.Exists(Bry_.new_a7(lang_code))
)
exists = true;
return rslt.Init_obj(exists);
}
public boolean IsValidCode(Scrib_proc_args args, Scrib_proc_rslt rslt) { // REF.MW: Language.php!isValidCode
byte[] lang_code = args.Pull_bry(0);
boolean valid = Xoa_ttl.parse_(core.Wiki(), lang_code) != null; // NOTE: MW calls Title::getTitleInvalidRegex()
if (valid) {
int len = lang_code.length;
for (int i = 0; i < len; i++) {
byte b = lang_code[i];
switch (b) { // NOTE: snippet from MW follows; also \000 assumed to be Nil --> :/\\\000&<>'\"
case Byte_ascii.Colon: case Byte_ascii.Slash: case Byte_ascii.Backslash: case Byte_ascii.Nil: case Byte_ascii.Amp: case Byte_ascii.Lt: case Byte_ascii.Gt: case Byte_ascii.Apos: case Byte_ascii.Quote:
valid = false;
i = len;
break;
}
}
}
return rslt.Init_obj(valid);
}
public boolean IsValidBuiltInCode(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] lang_code = args.Pull_bry(0);
int len = lang_code.length;
boolean valid = true;
for (int i = 0; i < len; i++) { // REF.MW: '/^[a-z0-9-]+$/i'
byte b = lang_code[i];
if (b == Byte_ascii.Dash) {}
else {
byte tid = Xol_lang_.Char_tid(b);
switch (tid) {
case Xol_lang_.Char_tid_ltr_l: case Xol_lang_.Char_tid_ltr_u: case Xol_lang_.Char_tid_num:
break;
default:
valid = false;
i = len;
break;
}
}
}
return rslt.Init_obj(valid);
}
public boolean FetchLanguageName(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] lang_code = args.Pull_bry(0);
// byte[] trans_code = args.Get_bry_or_null(1); // TODO: FetchLanguageName("en", "fr") -> Anglais; WHEN: needs global database of languages;
Xol_lang_itm lang_itm = Xol_lang_itm_.Get_by_key(lang_code);
return rslt.Init_obj(lang_itm == null ? String_.Empty : String_.new_u8(lang_itm.Canonical_name()));
}
public boolean FetchLanguageNames(Scrib_proc_args args, Scrib_proc_rslt rslt) {
// byte[] lang_code = args.Cast_bry_or_null(0);
// byte[] include = args.Form_bry_or(1, FetchLanguageNames_mw);
return rslt.Init_obj(core.Wiki().Cache_mgr().Scrib_lang_names()); // NOTE: return current language only; MW iterates over langs in wiki; XO is more complicated as technically all langs are available; need to map subset of langs per wiki, which is not trivial
}
public boolean GetFallbacksFor(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] lang_code = args.Pull_bry(0);
Xol_lang lang = core.App().Lang_mgr().Get_by_key(lang_code); if (lang == null) return rslt.Init_many_empty(); // lang is not valid; return empty array per MW;
return rslt.Init_bry_ary(lang.Fallback_bry_ary());
}
public boolean Lcfirst(Scrib_proc_args args, Scrib_proc_rslt rslt) {return Case_1st(args, rslt, Bool_.N);}
public boolean Ucfirst(Scrib_proc_args args, Scrib_proc_rslt rslt) {return Case_1st(args, rslt, Bool_.Y);}
private boolean Case_1st(Scrib_proc_args args, Scrib_proc_rslt rslt, boolean upper) {
Xol_lang lang = lang_(args);
byte[] word = args.Pull_bry(1);
Bry_bfr bfr = core.Wiki().Appe().Utl__bfr_mkr().Get_b128().Mkr_rls();
return rslt.Init_obj(lang.Case_mgr().Case_build_1st(bfr, upper, word, 0, word.length));
}
public boolean Lc(Scrib_proc_args args, Scrib_proc_rslt rslt) {return Case_all(args, rslt, Bool_.N);}
public boolean Uc(Scrib_proc_args args, Scrib_proc_rslt rslt) {return Case_all(args, rslt, Bool_.Y);}
private boolean Case_all(Scrib_proc_args args, Scrib_proc_rslt rslt, boolean upper) {
Xol_lang lang = lang_(args);
byte[] word = args.Pull_bry(1);
return rslt.Init_obj(lang.Case_mgr().Case_build(upper, word, 0, word.length));
}
public boolean CaseFold(Scrib_proc_args args, Scrib_proc_rslt rslt) {return Uc(args, rslt);} // REF.MW:Language.php!caseFold; http://www.w3.org/International/wiki/Case_folding
public boolean FormatNum(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
byte[] num = args.Xstr_bry_or_null(1);
boolean skip_commafy = false;
if (num != null) { // MW: if num present, check options table for noCommafy arg;
KeyVal[] kv_ary = args.Cast_kv_ary_or_null(2);
if (kv_ary != null) {
Object skip_commafy_obj = KeyVal_.Ary_get_by_key_or_null(kv_ary, "noCommafy");
if (skip_commafy_obj != null)
skip_commafy = Bool_.cast_(skip_commafy_obj);
}
}
byte[] rv = lang.Num_mgr().Format_num(num, skip_commafy);
return rslt.Init_obj(rv);
}
public boolean FormatDate(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
byte[] fmt_bry = args.Pull_bry(1);
byte[] date_bry = args.Cast_bry_or_empty(2); // NOTE: optional empty is required b/c date is sometimes null; use Bry_.Empty b/c this is what Pft_func_time.ParseDate takes; DATE:2013-04-05
boolean utc = args.Cast_bool_or_n(3);
Bry_bfr tmp_bfr = core.App().Utl__bfr_mkr().Get_b512();
Pft_fmt_itm[] fmt_ary = Pft_fmt_itm_.Parse(core.Ctx(), fmt_bry);
DateAdp date
= Bry_.Len_eq_0(date_bry)
? DateAdp_.Now()
: Pft_func_time.ParseDate(date_bry, utc, tmp_bfr)
; // NOTE: MW is actually more strict about date; however, not sure about PHP's date parse, so using more lax version
if (date == null || tmp_bfr.Len() > 0) {tmp_bfr.Mkr_rls(); return rslt.Init_fail("bad argument #2 to 'formatDate' (not a valid timestamp)");}
Pft_func_formatdate.Date_bldr().Format(tmp_bfr, core.Wiki(), lang, date, fmt_ary);
byte[] rv = tmp_bfr.To_bry_and_rls();
return rslt.Init_obj(rv);
}
public boolean ParseFormattedNumber(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
byte[] num = args.Xstr_bry_or_null(1);
if (num == null) return rslt.Init_null(); // ParseFormattedNumber can sometimes take 1 arg ({'en'}), or null arg ({'en', null}); return null (not ""); DATE:2014-01-07
byte[] rv = lang.Num_mgr().Raw(num);
return rslt.Init_obj(rv);
}
public boolean FormatDuration(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
long seconds = args.Pull_long(1);
KeyVal[] intervals_kv_ary = args.Cast_kv_ary_or_null(2);
Xol_duration_itm[] intervals = Xol_duration_itm_.Xto_itm_ary(intervals_kv_ary);
byte[] rv = lang.Duration_mgr().Format_durations(core.Ctx(), seconds, intervals);
return rslt.Init_obj(rv);
}
public boolean GetDurationIntervals(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
long seconds = args.Pull_long(1);
KeyVal[] intervals_kv_ary = args.Cast_kv_ary_or_null(2);
Xol_duration_itm[] intervals = Xol_duration_itm_.Xto_itm_ary(intervals_kv_ary);
Xol_interval_itm[] rv = lang.Duration_mgr().Get_duration_intervals(seconds, intervals);
return rslt.Init_obj(Xol_interval_itm.Xto_kv_ary(rv));
}
public boolean ConvertPlural(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
int count = args.Pull_int(1);
byte[][] words = args.Cast_params_as_bry_ary_or_rest_of_ary(2);
byte[] rv = lang.Plural().Plural_eval(lang, count, words);
return rslt.Init_obj(rv);
}
public boolean ConvertGrammar(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
byte[] word = args.Pull_bry(1);
byte[] type = args.Pull_bry(2);
Bry_bfr bfr = core.Wiki().Utl__bfr_mkr().Get_b512();
lang.Grammar().Grammar_eval(bfr, lang, word, type);
return rslt.Init_obj(bfr.To_str_and_rls());
}
public boolean gender(Scrib_proc_args args, Scrib_proc_rslt rslt) {throw Exc_.new_unimplemented();}
public boolean IsRTL(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xol_lang lang = lang_(args);
return rslt.Init_obj(!lang.Dir_ltr());
}
private Xol_lang lang_(Scrib_proc_args args) {
byte[] lang_code = args.Cast_bry_or_null(0);
Xol_lang lang = lang_code == null ? null : core.App().Lang_mgr().Get_by_key_or_load(lang_code);
if (lang == null) throw Exc_.new_("lang_code is not valid", "lang_code", String_.new_u8(lang_code));
return lang;
}
}

View File

@@ -0,0 +1,156 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
import gplx.xowa.langs.numbers.*;
public class Scrib_lib_language_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_language().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void GetContLangCode() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_getContLangCode, Object_.Ary_empty, "en");
}
@Test public void IsSupportedLanguage() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("fr"), true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("qq"), false);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("EN"), false);
}
@Test public void IsKnownLanguageTag() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("fr"), true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("qq"), false);
}
@Test public void IsValidCode() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a,b"), true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a'b"), false);
}
@Test public void IsValidBuiltInCode() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e-N"), true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e n"), false);
}
@Test public void FetchLanguageName() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("en"), "English");
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("fr"), "Français");
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("enx"), "");
}
@Test public void GetFallbacksFor() {
Xol_lang other_lang = fxt.Core().App().Lang_mgr().Get_by_key_or_new(Bry_.new_a7("zh"));
other_lang.Fallback_bry_(Bry_.new_a7("gan-hant, zh-hant, zh-hans"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("zh"), String_.Concat_lines_nl
( "1="
, " 1=gan-hant"
, " 2=zh-hant"
, " 3=zh-hans"
, " 4=en" // auto-add en
));
}
@Test public void GetFallbacksFor_unknown() {
fxt.Test_scrib_proc_empty(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("unknown"));
}
@Test public void FormatNum() {
Xol_lang other_lang = fxt.Core().App().Lang_mgr().Get_by_key_or_new(Bry_.new_a7("de")).Init_by_load_assert(); // NOTE: must call Init_by_load_assert, else load will be called by scrib and sprs below will get overwritten during load;
fxt.Parser_fxt().Init_lang_numbers_separators(other_lang, ".", ",");
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("de", 1234), "1.234"); // german spr
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", 1234), "1,234"); // english spr
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234"), "1,234"); // String passed (not int)
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", KeyVal_.Ary(KeyVal_.new_("noCommafy", true))) , "1234"); // noCommafy.y
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", KeyVal_.Ary(KeyVal_.new_("noCommafy", false))) , "1,234"); // noCommafy.n
}
@Test public void FormatDate() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "2013-03-17", false), "2013-03-17");
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d"), DateAdp_.Now().XtoStr_fmt_yyyy_MM_dd()); // empty date should default to today;
}
@Test public void FormatDate_date_omitted() { // PURPOSE: some calls skip the date; retrieve arg_4 by int; EX: pl.w:L._Frank_Baum
Tfds.Now_enabled_y_();
Tfds.Now_set(DateAdp_.new_(2013, 12, 19, 1, 2, 3, 4));
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, KeyVal_.Ary(KeyVal_.int_(1, "en"), KeyVal_.int_(2, "Y-m-d"), KeyVal_.int_(4, false)), "2013-12-19");
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", ""), "2013-12-19");// PURPOSE: '' should return today, not fail; EX: th.w:สถานีรถไฟตรัง
Tfds.Now_enabled_n_();
}
@Test public void Lc() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lc, Object_.Ary("en", "ABC"), "abc");
}
@Test public void Uc() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC");
}
@Test public void LcFirst() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lcfirst, Object_.Ary("en", "ABC"), "aBC");
}
@Test public void UcFirst() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC");
}
@Test public void ParseFormattedNumber() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", "1,234.56") , "1234.56"); // formatted String
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", "1234") , "1234"); // String
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234) , "1234"); // int
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234.56) , "1234.56"); // double
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en"), Scrib_invoke_func_fxt.Null_rslt); // PURPOSE: missing arg should not fail; EX: ru.w:Туйон DATE:2014-01-06
}
@Test public void ConvertGrammar() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertGrammar, Object_.Ary("fi", "talo", "elative"), "talosta");
}
@Test public void ConvertPlural() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertPlural, Object_.Ary("ru", 5, Kv_ary_("a", "b", "c")), "c"); // forms in kv_ary
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertPlural, Object_.Ary("ru", 5, "a", "b", "c"), "c"); // forms as rest of ary; PAGE:ru.w:Ленин,_Владимир_Ильич DATE:2014-07-01
}
@Test public void IsRTL() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("en"), false);
Xol_lang other_lang = fxt.Core().App().Lang_mgr().Get_by_key_or_new(Bry_.new_a7("ar"));
GfoInvkAble_.InvkCmd_val(other_lang, Xol_lang.Invk_dir_rtl_, true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("ar"), true);
}
@Test public void Format_duration() {
Init_lang_durations(fxt.Core().Wiki());
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 3723d, Kv_ary_("hours", "minutes", "seconds")), "1 hour, 2 minutes and 3 seconds"); // basic
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours", "minutes", "seconds")), "2 minutes and 3 seconds"); // omit hour since < 1
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours")) , "0 hours"); // handle fractional duration
}
@Test public void Get_duration_intervals() {
Init_lang_durations(fxt.Core().Wiki());
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getDurationIntervals, Object_.Ary("en", 3723d, Kv_ary_("hours", "minutes", "seconds")), String_.Concat_lines_nl_skip_last
( "1="
, " hours=1"
, " minutes=2"
, " seconds=3"
));
}
private static KeyVal[] Kv_ary_(String... ary) {
int ary_len = ary.length;
KeyVal[] rv = new KeyVal[ary_len];
for (int i = 0; i < ary_len; i++) {
rv[i] = KeyVal_.int_(i, ary[i]);
}
return rv;
}
public static void Init_lang_durations(Xowe_wiki wiki) {
Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr();
msg_mgr.Clear();
msg_mgr.Itm_by_key_or_new("duration-millenia" , "~{0} {{PLURAL:~{0}\u007Cmillennium\u007Cmillennia}}");
msg_mgr.Itm_by_key_or_new("duration-centuries" , "~{0} {{PLURAL:~{0}\u007Ccentury\u007Ccenturies}}");
msg_mgr.Itm_by_key_or_new("duration-decades" , "~{0} {{PLURAL:~{0}\u007Cdecade\u007Cdecades}}");
msg_mgr.Itm_by_key_or_new("duration-years" , "~{0} {{PLURAL:~{0}\u007Cyear\u007Cyears}}");
msg_mgr.Itm_by_key_or_new("duration-weeks" , "~{0} {{PLURAL:~{0}\u007Cweek\u007Cweeks}}");
msg_mgr.Itm_by_key_or_new("duration-days" , "~{0} {{PLURAL:~{0}\u007Cday\u007Cdays}}");
msg_mgr.Itm_by_key_or_new("duration-hours" , "~{0} {{PLURAL:~{0}\u007Chour\u007Chours}}");
msg_mgr.Itm_by_key_or_new("duration-minutes" , "~{0} {{PLURAL:~{0}\u007Cminute\u007Cminutes}}");
msg_mgr.Itm_by_key_or_new("duration-seconds" , "~{0} {{PLURAL:~{0}\u007Csecond\u007Cseconds}}");
msg_mgr.Itm_by_key_or_new("and" , "&#32;and");
msg_mgr.Itm_by_key_or_new("word-separator" , "&#32;");
msg_mgr.Itm_by_key_or_new("comma-separator" , ",&#32;");
}
}

View File

@@ -0,0 +1,179 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.core.primitives.*; import gplx.html.*;
public class Scrib_lib_message implements Scrib_lib {
public Scrib_lib_message(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.message.lua"));// NOTE: "lang" not passed in
notify_lang_changed_fnc = mod.Fncs_get_by_key("notify_lang_changed");
return mod;
} private Scrib_lua_proc notify_lang_changed_fnc;
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_plain: return Plain(args, rslt);
case Proc_check: return Check(args, rslt);
case Proc_init_message_for_lang: return Init_message_for_lang(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_plain = 0, Proc_check = 1, Proc_init_message_for_lang = 2;
public static final String Invk_plain = "plain", Invk_check = "check", Invk_init_message_for_lang = "init_message_for_lang";
private static final String[] Proc_names = String_.Ary(Invk_plain, Invk_check, Invk_init_message_for_lang);
public void Notify_lang_changed() {if (notify_lang_changed_fnc != null) core.Interpreter().CallFunction(notify_lang_changed_fnc.Id(), KeyVal_.Ary_empty);}
public boolean Plain(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte fmt_tid = Scrib_lib_message_data.Fmt_tid_plain;
KeyVal[] data_kvary = args.Pull_kv_ary(0);
Scrib_lib_message_data msg_data = new Scrib_lib_message_data().Parse(data_kvary);
return rslt.Init_obj(String_.new_u8(msg_data.Make_msg(core.Cur_lang(), core.Wiki(), core.Ctx(), true, fmt_tid)));
}
public boolean Check(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte chk_tid = Scrib_lib_message_data.parse_chk_(args.Pull_bry(0));
KeyVal[] data_kvary = args.Pull_kv_ary(1);
Scrib_lib_message_data msg_data = new Scrib_lib_message_data().Parse(data_kvary);
return rslt.Init_obj(msg_data.Chk_msg(core.Cur_lang(), core.Wiki(), core.Ctx(), false, chk_tid));
}
public boolean Init_message_for_lang(Scrib_proc_args args, Scrib_proc_rslt rslt) {return rslt.Init_obj(KeyVal_.new_("lang", core.Wiki().Lang().Key_str()));}
}
class Scrib_lib_message_data {
public boolean Use_db() {return use_db;} private boolean use_db;
public byte[] Lang_key() {return lang_key;} private byte[] lang_key = Bry_.Empty;
public byte[] Title_bry() {return title_bry;} private byte[] title_bry;
public byte[] Msg_key() {return msg_key;} private byte[] msg_key;
public byte[] Raw_msg_key() {return raw_msg_key;} private byte[] raw_msg_key;
public Object[] Args() {return args;} Object[] args;
public Xoa_ttl Ttl() {return ttl;} public Scrib_lib_message_data Ttl_(Xoa_ttl v) {ttl = v; return this;} Xoa_ttl ttl;
public Scrib_lib_message_data Parse(KeyVal[] ary) {
int len = ary.length;
for (int i = 0; i < len; i++) {
KeyVal kv = ary[i];
byte[] kv_key = Bry_.new_a7(kv.Key());
Object key_obj = key_hash.Get_by(kv_key); if (key_obj == null) throw Exc_.new_("msg_key is invalid", "key", kv_key);
byte key_tid = ((Byte_obj_val)key_obj).Val();
switch (key_tid) {
case Key_tid_keys:
KeyVal[] keys_ary = (KeyVal[])kv.Val();
msg_key = keys_ary[0].Val_to_bry();
break;
case Key_tid_rawMessage: raw_msg_key = kv.Val_to_bry(); break;
case Key_tid_lang: lang_key = kv.Val_to_bry(); break;
case Key_tid_useDB: use_db = Bool_.cast_(kv.Val()); break;
case Key_tid_title: title_bry = kv.Val_to_bry(); break;
case Key_tid_params:
KeyVal[] args_ary = (KeyVal[])kv.Val();
int args_ary_len = args_ary.length;
args = new String[args_ary_len];
for (int j = 0; j < args_ary_len; j++)
args[j] = args_ary[j].Val_to_str_or_empty();
break;
default: throw Exc_.new_unhandled(key_tid);
}
}
return this;
}
public byte[] Fetch_msg(byte[] cur_lang, Xowe_wiki wiki, Xop_ctx ctx, boolean exec_params) {
if (exec_params) {
byte[] data_ttl = title_bry;
if (data_ttl == null)
ttl = ctx.Cur_page().Ttl();
else
ttl = Xoa_ttl.parse_(wiki, data_ttl);
}
if (raw_msg_key != null) {
Xol_msg_itm raw_msg_itm = new Xol_msg_itm(-1, Bry_.Empty);
Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512();
byte[] raw_msg_val = gplx.xowa.apps.Xoa_gfs_php_mgr.Xto_gfs(tmp_bfr, raw_msg_key);
Xol_msg_itm_.update_val_(raw_msg_itm, raw_msg_val);
byte[] raw_msg_rv = wiki.Msg_mgr().Val_by_itm(tmp_bfr, raw_msg_itm, args);
tmp_bfr.Mkr_rls();
return raw_msg_rv;
}
if (msg_key == null) return Bry_.Empty;
if (Bry_.Eq(lang_key, cur_lang) || Bry_.Len_eq_0(lang_key)) // if lang_key == core_lang then use wiki.msg_mgr; also same if lang_key == blank (arg not given)
return wiki.Msg_mgr().Val_by_key_args(msg_key, args);
else {
Xol_lang lang = wiki.Appe().Lang_mgr().Get_by_key_or_new(lang_key); lang.Init_by_load_assert();
Xol_msg_itm msg_itm = lang.Msg_mgr().Itm_by_key_or_null(msg_key); if (msg_itm == null) return Bry_.Empty;
return msg_itm.Val();
}
}
public boolean Chk_msg(byte[] cur_lang, Xowe_wiki wiki, Xop_ctx ctx, boolean exec_params, byte chk_tid) {
byte[] msg_val = Fetch_msg(cur_lang, wiki, ctx, false);
switch (chk_tid) {
case Check_tid_exists : return Bry_.Len_gt_0(msg_val);
case Check_tid_isBlank : return Bry_.Len_eq_0(msg_val); // REF.MW: $message === false || $message === ''
case Check_tid_isDisabled : return Bry_.Len_eq_0(msg_val) || msg_val.length == 1 && msg_val[0] == Byte_ascii.Dash; // REF.MW: $message === false || $message === '' || $message === '-'
default : throw Exc_.new_unhandled(chk_tid);
}
}
public byte[] Make_msg(byte[] cur_lang, Xowe_wiki wiki, Xop_ctx ctx, boolean exec_params, byte fmt_tid) {
byte[] msg_val = Fetch_msg(cur_lang, wiki, ctx, exec_params);
if ( Bry_.Len_eq_0(msg_val) // msg_key returned empty/null msg_val; assume not found
&& raw_msg_key == null // ignore if raw_msg; note that raw_msg can generate empty String; EX:raw_msg={{empty}} -> ""; PAGE:it.w:L'Internazionale DATE:2015-02-25
) {
Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512();
bfr.Add(gplx.html.Html_entity_.Lt_bry).Add(msg_key).Add(gplx.html.Html_entity_.Gt_bry); // NOTE: Message.php has logic that says: if plain, "< >", else "&lt; &gt;"; for now, always use escaped
return bfr.To_bry_and_rls();
}
switch (fmt_tid) {
case Fmt_tid_parse:
case Fmt_tid_text: // NOTE: not sure what this does; seems to be a "lighter" parser
break;
case Fmt_tid_parseAsBlock: // NOTE: MW passes msg_val through parser and strips <p> if tid==parse; XOWA does the opposite, so add <p> if parseAsBlock requested
Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512();
msg_val = bfr.Add(Html_tag_.P_lhs).Add(msg_val).Add(Html_tag_.P_rhs).To_bry_and_rls();
break;
case Fmt_tid_escaped:
msg_val = gplx.html.Html_utl.Escape_html_as_bry(msg_val);
break;
}
return msg_val;
}
static final byte Key_tid_keys = 1, Key_tid_rawMessage = 2, Key_tid_lang = 3, Key_tid_useDB = 4, Key_tid_title = 5, Key_tid_params = 6;
private static final Hash_adp_bry key_hash = Hash_adp_bry.ci_ascii_()
.Add_str_byte("keys", Key_tid_keys)
.Add_str_byte("rawMessage", Key_tid_rawMessage)
.Add_str_byte("lang", Key_tid_lang)
.Add_str_byte("useDB", Key_tid_useDB)
.Add_str_byte("title", Key_tid_title)
.Add_str_byte("params", Key_tid_params);
public static byte parse_fmt_(byte[] key) {return parse_or_fail(fmt_hash, key, "invalid message format: {0}");}
public static byte parse_chk_(byte[] key) {return parse_or_fail(check_hash, key, "invalid check arg: {0}");}
public static byte parse_or_fail(Hash_adp_bry hash, byte[] key, String fmt) {
Object o = hash.Get_by_bry(key);
if (o == null) throw Exc_.new_(fmt, "key", String_.new_u8(key)).Stack_erase_1_();
return ((Byte_obj_val)o).Val();
}
public static final byte Fmt_tid_parse = 1, Fmt_tid_text = 2, Fmt_tid_plain = 3, Fmt_tid_escaped = 4, Fmt_tid_parseAsBlock = 5;
private static final Hash_adp_bry fmt_hash = Hash_adp_bry.ci_ascii_()
.Add_str_byte("parse", Fmt_tid_parse)
.Add_str_byte("text", Fmt_tid_text)
.Add_str_byte("plain", Fmt_tid_plain)
.Add_str_byte("escaped", Fmt_tid_escaped)
.Add_str_byte("parseAsBlock", Fmt_tid_parseAsBlock);
public static final byte Check_tid_exists = 1, Check_tid_isBlank = 2, Check_tid_isDisabled = 3;
private static final Hash_adp_bry check_hash = Hash_adp_bry.ci_ascii_()
.Add_str_byte("exists", Check_tid_exists)
.Add_str_byte("isBlank", Check_tid_isBlank)
.Add_str_byte("isDisabled", Check_tid_isDisabled);
}

View File

@@ -0,0 +1,76 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_message_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_message().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void Plain() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_plain, Object_.Ary((Object)keys_ary("sun")) , "Sun");
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_plain, Object_.Ary((Object)keys_ary("sunx")) , "&lt;sunx&gt;");
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_plain, Object_.Ary((Object)keys_ary_arg("redirectedfrom", "A")) , "(Redirected from A)");
}
@Test public void Plain_lang() {
Xol_lang lang = fxt.Parser_fxt().Wiki().Appe().Lang_mgr().Get_by_key_or_new(Bry_.new_a7("fr"));
Init_msg(lang, "sun", "dim");
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_plain, Object_.Ary((Object)keys_ary_lang("sun", "fr")) , "dim");
}
@Test public void Plain_rawMessage() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_plain, Object_.Ary((Object)Scrib_kv_utl_.flat_many_("rawMessage", "$1", "params", KeyVal_.Ary(KeyVal_.int_(1, "abc")))), "abc");
}
@Test public void Plain_rawMessage_empty() {// PURPOSE:rawMessage would throw null ref if rawMessage called template that returns empty value; PAGE:it.w:L'Internazionale DATE:2015-02-25
fxt.Parser_fxt().Init_page_create("Template:Msg", "");
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_plain, Object_.Ary((Object)Scrib_kv_utl_.flat_many_("rawMessage", "{{Msg}}", "params", KeyVal_.Ary(KeyVal_.int_(1, "abc")))), "");
}
@Test public void Check() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("exists" , keys_ary("sun")) , true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("exists" , keys_ary("sunx")) , false);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("isBlank" , keys_ary("sun")) , false);
Init_msg("blank", "");
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("isBlank" , keys_ary("blank")) , true);
Init_msg("disabled", "-");
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("isDisabled" , keys_ary("sun")) , false);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("isDisabled" , keys_ary("blank")) , true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("isDisabled" , keys_ary("disabled")) , true);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_message.Invk_check, Object_.Ary("isBlank" , keys_ary("disabled")) , false);
}
@Test public void Init_message_for_lang() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_message.Invk_init_message_for_lang, Object_.Ary_empty , "lang=en");
}
private void Init_msg(String key, String val) {Init_msg(fxt.Core().Wiki().Lang(), key, val);}
private void Init_msg(Xol_lang lang, String key, String val) {
lang.Msg_mgr().Itm_by_key_or_new(Bry_.new_a7(key)).Atrs_set(Bry_.new_a7(val), false, false);
}
KeyVal[] keys_ary(String msg_key) {return keys_ary(msg_key, null, null);}
KeyVal[] keys_ary_arg(String msg_key, String arg) {return keys_ary(msg_key, null, arg);}
KeyVal[] keys_ary_lang(String msg_key, String lang) {return keys_ary(msg_key, lang, null);}
KeyVal[] keys_ary(String msg_key, String lang, String arg) {
boolean arg_exists = arg != null;
boolean lang_exists = lang != null;
int idx = 0;
KeyVal[] rv = new KeyVal[1 + (arg_exists ? 1 : 0) + (lang_exists ? 1 : 0)];
rv[0] = KeyVal_.new_("keys", KeyVal_.Ary(KeyVal_.int_(1, msg_key)));
if (arg_exists)
rv[++idx] = KeyVal_.new_("params", KeyVal_.Ary(KeyVal_.int_(1, arg)));
if (lang_exists)
rv[++idx] = KeyVal_.new_("lang", lang);
return rv;
}
}

View File

@@ -0,0 +1,390 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.core.primitives.*;
import gplx.xowa.langs.*;
public class Scrib_lib_mw implements Scrib_lib {
private Scrib_core core; private Scrib_fsys_mgr fsys_mgr;
public Scrib_lib_mw(Scrib_core core) {this.core = core; this.fsys_mgr = core.Fsys_mgr();}
public Scrib_lua_mod Mod() {return mod;} public void Mod_(Scrib_lua_mod v) {this.mod = v;} private Scrib_lua_mod mod;
public boolean Allow_env_funcs() {return allow_env_funcs;} private boolean allow_env_funcs = true;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
core.RegisterInterface(this, script_dir.GenSubFil("mwInit.lua")); // DATE:2014-07-12
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.lua")
, KeyVal_.new_("allowEnvFuncs", allow_env_funcs));
return mod;
}
public void Invoke_bgn(Xowe_wiki wiki, Xop_ctx ctx, byte[] new_src) {
if (src != null) // src exists; indicates that Invoke being called recursively; push existing src onto stack
src_stack.Add(src);
this.cur_wiki = wiki; this.ctx = ctx; this.src = new_src;
} private Xowe_wiki cur_wiki; private byte[] src; private Xop_ctx ctx; private List_adp src_stack = List_adp_.new_();
public void Invoke_end() {
if (src_stack.Count() > 0) // src_stack item exists; pop
src = (byte[])List_adp_.Pop(src_stack);
else // entry point; set to null
src = null;
}
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_loadPackage: return LoadPackage(args, rslt);
case Proc_loadPHPLibrary: return LoadPHPLibrary(args, rslt);
case Proc_frameExists: return FrameExists(args, rslt);
case Proc_parentFrameExists: return ParentFrameExists(args, rslt);
case Proc_getExpandedArgument: return GetExpandedArgument(args, rslt);
case Proc_getAllExpandedArguments: return GetAllExpandedArguments(args, rslt);
case Proc_expandTemplate: return ExpandTemplate(args, rslt);
case Proc_preprocess: return Preprocess(args, rslt);
case Proc_callParserFunction: return CallParserFunction(args, rslt);
case Proc_incrementExpensiveFunctionCount: return IncrementExpensiveFunctionCount(args, rslt);
case Proc_isSubsting: return IsSubsting(args, rslt);
case Proc_newChildFrame: return NewChildFrame(args, rslt);
case Proc_getFrameTitle: return GetFrameTitle(args, rslt);
case Proc_setTTL: return SetTTL(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
public static final int
Proc_loadPackage = 0, Proc_loadPHPLibrary = 1
, Proc_frameExists = 2, Proc_parentFrameExists = 3
, Proc_getExpandedArgument = 4, Proc_getAllExpandedArguments = 5
, Proc_expandTemplate = 6, Proc_preprocess = 7, Proc_callParserFunction = 8
, Proc_incrementExpensiveFunctionCount = 9, Proc_isSubsting = 10
, Proc_newChildFrame = 11, Proc_getFrameTitle = 12, Proc_setTTL = 13
;
public static final String
Invk_loadPackage = "loadPackage", Invk_loadPHPLibrary = "loadPHPLibrary"
, Invk_frameExists = "frameExists", Invk_parentFrameExists = "parentFrameExists"
, Invk_getExpandedArgument = "getExpandedArgument", Invk_getAllExpandedArguments = "getAllExpandedArguments"
, Invk_expandTemplate = "expandTemplate", Invk_preprocess = "preprocess", Invk_callParserFunction = "callParserFunction"
, Invk_incrementExpensiveFunctionCount = "incrementExpensiveFunctionCount", Invk_isSubsting = "isSubsting"
, Invk_newChildFrame = "newChildFrame", Invk_getFrameTitle = "getFrameTitle", Invk_setTTL = "setTTL"
;
private static final String[] Proc_names = String_.Ary
( Invk_loadPackage, Invk_loadPHPLibrary
, Invk_frameExists, Invk_parentFrameExists
, Invk_getExpandedArgument, Invk_getAllExpandedArguments
, Invk_expandTemplate, Invk_preprocess, Invk_callParserFunction
, Invk_incrementExpensiveFunctionCount, Invk_isSubsting
, Invk_newChildFrame, Invk_getFrameTitle, Invk_setTTL
);
public boolean LoadPackage(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String mod_name = args.Pull_str(0);
String mod_code = fsys_mgr.Get_or_null(mod_name); // check if mod_name a file in /lualib/ directoryScribunto .lua file (in /lualib/)
if (mod_code != null)
return rslt.Init_obj(core.Interpreter().LoadString("@" + mod_name + ".lua", mod_code));
Xoa_ttl ttl = Xoa_ttl.parse_(cur_wiki, Bry_.new_u8(mod_name));// NOTE: should have Module: prefix
if (ttl == null) return rslt.Init_ary_empty();
Xoae_page page = cur_wiki.Data_mgr().Get_page(ttl, false);
if (page.Missing()) return rslt.Init_ary_empty();
Scrib_lua_mod mod = new Scrib_lua_mod(core, mod_name);
return rslt.Init_obj(mod.LoadString(String_.new_u8(page.Data_raw())));
}
public boolean LoadPHPLibrary(Scrib_proc_args args, Scrib_proc_rslt rslt) { // NOTE: noop; Scribunto uses this to load the Scribunto_*Library classses (EX: Scribunto_TitleLibrary); DATE:2015-01-21
return rslt.Init_obj(null);
}
public boolean GetExpandedArgument(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String frame_id = args.Pull_str(0);
Xot_invk frame = Scrib_frame_.Get_frame(core, frame_id);
int frame_arg_adj = Scrib_frame_.Get_arg_adj(frame.Frame_tid());
String idx_str = args.Pull_str(1);
int idx_int = Int_.parse_or_(idx_str, Int_.MinValue); // NOTE: should not receive int value < -1; idx >= 0
Bry_bfr tmp_bfr = Bry_bfr.new_(); // NOTE: do not make modular level variable, else random failures; DATE:2013-10-14
if (idx_int != Int_.MinValue) { // idx is integer
Arg_nde_tkn nde = Get_arg(frame, idx_int, frame_arg_adj);
//frame.Args_eval_by_idx(core.Ctx().Src(), idx_int); // NOTE: arg[0] is always MW function name; EX: {{#invoke:Mod_0|Func_0|Arg_1}}; arg_x = "Mod_0"; args[0] = "Func_0"; args[1] = "Arg_1"
if (nde == null) return rslt.Init_ary_empty();
nde.Val_tkn().Tmpl_evaluate(ctx, src, core.Frame_parent(), tmp_bfr);
return rslt.Init_obj(tmp_bfr.Xto_str_and_clear());
}
else {
Arg_nde_tkn nde = frame.Args_get_by_key(src, Bry_.new_u8(idx_str));
if (nde == null) return rslt.Init_ary_empty(); // idx_str does not exist;
nde.Val_tkn().Tmpl_evaluate(ctx, src, core.Frame_parent(), tmp_bfr);
return rslt.Init_obj(tmp_bfr.Xto_str_and_clear_and_trim()); // NOTE: must trim if key_exists; DUPE:TRIM_IF_KEY
}
}
private Arg_nde_tkn Get_arg(Xot_invk invk, int idx, int frame_arg_adj) { // DUPE:MW_ARG_RETRIEVE
int cur = List_adp_.Base1, len = invk.Args_len() - frame_arg_adj;
for (int i = 0; i < len; i++) { // iterate over list to find nth *non-keyd* arg; SEE:NOTE_1
Arg_nde_tkn nde = (Arg_nde_tkn)invk.Args_get_by_idx(i + frame_arg_adj);
if (nde.KeyTkn_exists()) { // BLOCK:ignore_args_with_empty_keys;
if (Verify_arg_key(src, idx, nde))
return nde;
else
continue;
}
if (idx == cur) return nde;
else ++cur;
}
return invk.Args_get_by_key(src, Bry_.XtoStrBytesByInt(idx + 1, 1));
}
private static boolean Verify_arg_key(byte[] src, int idx, Arg_nde_tkn nde) {
int key_int = Bry_.NotFound;
byte[] key_dat_ary = nde.Key_tkn().Dat_ary();
if (Env_.Mode_testing() && src == null) // some tests will always pass a null src;
key_int = Bry_.Xto_int_or(key_dat_ary, 0, key_dat_ary.length, Bry_.NotFound);
else {
if (Bry_.Len_eq_0(key_dat_ary)) // should be called by current context;
key_int = Bry_.Xto_int_or(src, nde.Key_tkn().Src_bgn(), nde.Key_tkn().Src_end(), Bry_.NotFound);
else // will be called by parent context; note that this calls Xot_defn_tmpl_.Make_itm which sets a key_dat_ary; DATE:2013-09-23
key_int = Bry_.Xto_int_or(key_dat_ary, 0, key_dat_ary.length, Bry_.NotFound);
}
if (key_int == Bry_.NotFound) // key is not-numeric
return false;
else // key is numeric
return idx == key_int;
}
public boolean GetAllExpandedArguments(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String frame_id = args.Pull_str(0);
Xot_invk frame = Scrib_frame_.Get_frame(core, frame_id);
byte frame_tid = frame.Frame_tid();
Xot_invk parent_frame = Scrib_frame_.Get_parent(core, frame_tid);
int frame_arg_adj = Scrib_frame_.Get_arg_adj(frame_tid);
int args_len = frame.Args_len() - frame_arg_adj;
if (args_len < 1) return rslt.Init_obj(KeyVal_.Ary_empty); // occurs when "frame:getParent().args" but no parent frame
Bry_bfr tmp_bfr = ctx.Wiki().Utl__bfr_mkr().Get_b128(); // NOTE: do not make modular level variable, else random failures; DATE:2013-10-14
List_adp rv = List_adp_.new_();
int arg_idx = 0;
for (int i = 0; i < args_len; i++) {
Arg_nde_tkn nde = frame.Args_get_by_idx(i + frame_arg_adj);
if (nde.KeyTkn_exists()) { // BLOCK:ignore_args_with_empty_keys;
byte[] key_dat_ary = nde.Key_tkn().Dat_ary();
int key_dat_len = Bry_.Len(key_dat_ary);
if (Env_.Mode_testing() && src == null) {
if (key_dat_len == 0) continue;
}
else {
if (key_dat_len == 0) // should be called by current context;
if (nde.Key_tkn().Src_end() - nde.Key_tkn().Src_bgn() == 0) continue;
}
}
nde.Key_tkn().Tmpl_evaluate(ctx, src, parent_frame, tmp_bfr);
int key_len = tmp_bfr.Len();
boolean key_missing = key_len == 0;
String key_as_str = null; int key_as_int = Int_.MinValue;
boolean key_is_str = false;
if (key_missing) // key missing; EX: {{a|val}}
key_as_int = ++arg_idx;// NOTE: MW requires a key; if none, then default to int index; NOTE: must be int, not String; NOTE: must be indexed to keyless args; EX: in "key1=val1,val2", "val2" must be "1" (1st keyless arg) not "2" (2nd arg); DATE:2013-11-09
else { // key exists; EX:{{a|key=val}}
key_as_int = Bry_.Xto_int_or(tmp_bfr.Bfr(), 0, tmp_bfr.Len(), Int_.MinValue);
if (key_as_int == Int_.MinValue) { // key is not int; create str
key_as_str = tmp_bfr.Xto_str_and_clear();
key_is_str = true;
}
else { // key is int; must return int for key b/c lua treats table[1] different than table["1"]; DATE:2014-02-13
tmp_bfr.Clear(); // must clear bfr, else key will be added to val;
}
}
nde.Val_tkn().Tmpl_evaluate(ctx, src, parent_frame, tmp_bfr);
String val = key_missing ? tmp_bfr.Xto_str_and_clear() : tmp_bfr.Xto_str_and_clear_and_trim(); // NOTE: must trim if key_exists; DUPE:TRIM_IF_KEY
KeyVal kv = key_is_str ? KeyVal_.new_(key_as_str, val) : KeyVal_.int_(key_as_int, val);
rv.Add(kv);
}
tmp_bfr.Mkr_rls();
return rslt.Init_obj((KeyVal[])rv.To_ary(KeyVal.class));
}
public boolean FrameExists(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String frame_id = args.Pull_str(0);
Xot_invk frame = Scrib_frame_.Get_frame(core, frame_id);
return rslt.Init_obj(frame != null);
}
public boolean ParentFrameExists(Scrib_proc_args args, Scrib_proc_rslt rslt) {
return rslt.Init_obj(!core.Frame_parent().Frame_is_root());
}
public boolean Preprocess(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String frame_id = args.Pull_str(0);
Xot_invk frame = Scrib_frame_.Get_frame(core, frame_id);
byte frame_tid = frame.Frame_tid();
Xot_invk parent_frame = Scrib_frame_.Get_parent(core, frame_tid);
String text_str = args.Pull_str(1);
byte[] text_bry = Bry_.new_u8(text_str);
Xop_root_tkn tmp_root = ctx.Tkn_mkr().Root(text_bry);
Xop_ctx tmp_ctx = Xop_ctx.new_sub_(cur_wiki);
int args_adj = Scrib_frame_.Get_arg_adj(frame_tid);
int args_len = frame.Args_len() - args_adj;
KeyVal[] kv_args = new KeyVal[args_len];
Bry_bfr tmp_bfr = core.Wiki().Utl__bfr_mkr().Get_b512();
for (int i = 0; i < args_len; i++) {
Arg_nde_tkn arg = frame.Args_get_by_idx(i + args_adj);
arg.Key_tkn().Tmpl_evaluate(ctx, src, frame, tmp_bfr);
String key = tmp_bfr.Xto_str_and_clear();
if (String_.Eq(key, "")) key = Int_.Xto_str(i);
arg.Val_tkn().Tmpl_evaluate(ctx, src, parent_frame, tmp_bfr); // NOTE: must evaluate against parent_frame; evaluating against current frame may cause stack-overflow; DATE:2013-04-04
String val = tmp_bfr.Xto_str_and_clear();
kv_args[i] = KeyVal_.new_(key, val);
}
Xot_invk_mock mock_frame = Xot_invk_mock.new_(Bry_.new_u8(frame_id), kv_args); // use frame_id for Frame_ttl; in lieu of a better candidate; DATE:2014-09-21
tmp_ctx.Parse_tid_(Xop_parser_.Parse_tid_page_tmpl); // default xnde names to template; needed for test, but should be in place; DATE:2014-06-27
cur_wiki.Parser().Parse_text_to_wtxt(tmp_root, tmp_ctx, tmp_ctx.Tkn_mkr(), text_bry);
tmp_root.Tmpl_evaluate(tmp_ctx, text_bry, mock_frame, tmp_bfr);
return rslt.Init_obj(tmp_bfr.To_str_and_rls());
}
public boolean CallParserFunction(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String frame_id = args.Pull_str(0);
int frame_tid = Scrib_frame_.Get_frame_tid(frame_id);
Xot_invk parent_frame = frame_tid == Scrib_frame_.Tid_current ? core.Frame_current() : core.Frame_parent();
byte[] fnc_name = args.Pull_bry(1);
int fnc_name_len = fnc_name.length;
Bry_obj_ref argx_ref = Bry_obj_ref.null_();
Bry_obj_ref fnc_name_ref = Bry_obj_ref.new_(fnc_name);
KeyVal[] parser_func_args = CallParserFunction_parse_args(cur_wiki.Appe().Utl_num_parser(), argx_ref, fnc_name_ref, args.Ary());
Xot_invk_mock frame = Xot_invk_mock.new_(parent_frame.Defn_tid(), 0, fnc_name, parser_func_args); // pass something as frame_ttl; choosng fnc_name; DATE:2014-09-21
Xol_func_name_itm finder = cur_wiki.Lang().Func_regy().Find_defn(fnc_name, 0, fnc_name_len);
Xot_defn defn = finder.Func();
if (defn == Xot_defn_.Null) throw Exc_.new_("callParserFunction: function was not found", "function", String_.new_u8(fnc_name));
Bry_bfr bfr = cur_wiki.Utl__bfr_mkr().Get_k004();
Xop_ctx fnc_ctx = Xop_ctx.new_sub_(cur_wiki);
fnc_ctx.Parse_tid_(Xop_parser_.Parse_tid_page_tmpl); // default xnde names to template; needed for test, but should be in place; DATE:2014-06-27
Xot_invk_tkn.Eval_func(fnc_ctx, src, parent_frame, frame, bfr, defn, argx_ref.Val());
bfr.Mkr_rls();
return rslt.Init_obj(bfr.Xto_str_and_clear());
}
private KeyVal[] CallParserFunction_parse_args(Number_parser num_parser, Bry_obj_ref argx_ref, Bry_obj_ref fnc_name_ref, KeyVal[] args) {
List_adp rv = List_adp_.new_();
// flatten args
int args_len = args.length;
for (int i = 2; i < args_len; i++) {
KeyVal arg = args[i];
if (Is_kv_ary(arg)) {
KeyVal[] arg_kv_ary = (KeyVal[])arg.Val();
int arg_kv_ary_len = arg_kv_ary.length;
for (int j = 0; j < arg_kv_ary_len; j++) {
KeyVal sub_arg = arg_kv_ary[j];
rv.Add(sub_arg);
}
}
else
rv.Add(arg);
}
rv.Sort_by(Scrib_lib_mw_callParserFunction_sorter._);
// get argx
byte[] fnc_name = fnc_name_ref.Val();
int fnc_name_len = fnc_name.length;
int fnc_name_colon_pos = Bry_finder.Find_fwd(fnc_name, Byte_ascii.Colon, 0, fnc_name_len);
if (fnc_name_colon_pos == Bry_.NotFound) {
KeyVal arg_argx = (KeyVal)rv.Get_at(0);
argx_ref.Val_(arg_argx.Val_to_bry());
rv.Del_at(0);
}
else {
argx_ref.Val_(Bry_.Mid(fnc_name, fnc_name_colon_pos + 1, fnc_name_len));
fnc_name = Bry_.Mid(fnc_name, 0, fnc_name_colon_pos);
fnc_name_ref.Val_(fnc_name);
}
return (KeyVal[])rv.To_ary(KeyVal.class);
}
private static boolean Is_kv_ary(KeyVal kv) {return ClassAdp_.Eq_typeSafe(kv.Val(), KeyVal[].class);}
public boolean ExpandTemplate(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String ttl_str = args.Pull_str(1);
byte[] ttl_bry = Bry_.new_u8(ttl_str);
Xoa_ttl ttl = Xoa_ttl.parse_(cur_wiki, ttl_bry); // parse directly; handles titles where template is already part of title; EX: "Template:A"
if (ttl == null) return rslt.Init_ary_empty(); // invalid ttl;
if (!ttl.ForceLiteralLink() && ttl.Ns().Id_main()) // title is not literal and is not prefixed with Template; parse again as template; EX: ":A" and "Template:A" are fine; "A" is parsed again as "Template:A"
ttl = Xoa_ttl.parse_(cur_wiki, Bry_.Add(cur_wiki.Ns_mgr().Ns_template().Name_db_w_colon(), ttl_bry)); // parse again, but add "Template:"
KeyVal[] args_ary = args.Pull_kv_ary(2);
// BLOCK.bgn:Xot_invk_tkn.Transclude; cannot reuse b/c Transclude needs invk_tkn, and invk_tkn is manufactured late; DATE:2014-01-02
byte[] sub_src = null;
if (ttl.Ns().Id_tmpl()) { // ttl is template; check tmpl_regy first before going to data_mgr
Xot_defn_tmpl tmpl = (Xot_defn_tmpl)core.Wiki().Cache_mgr().Defn_cache().Get_by_key(ttl.Page_db());
if (tmpl != null) sub_src = tmpl.Data_raw();
}
if (sub_src == null) // ttl is not in template cache, or is a ttl in non-Template ns; load title
sub_src = core.Wiki().Cache_mgr().Page_cache().Get_or_load_as_src(ttl);
if (sub_src != null) {
Xot_invk_mock sub_frame = Xot_invk_mock.new_(core.Frame_current().Defn_tid(), 0, ttl.Full_txt(), args_ary); // NOTE: (1) must have ns (Full); (2) must be txt (space, not underscore); EX:Template:Location map+; DATE:2014-09-21
Xot_defn_tmpl transclude_tmpl = ctx.Wiki().Parser().Parse_text_to_defn_obj(ctx, ctx.Tkn_mkr(), ttl.Ns(), ttl.Page_db(), sub_src);
Bry_bfr sub_bfr = cur_wiki.Utl__bfr_mkr().Get_k004();
transclude_tmpl.Tmpl_evaluate(ctx, sub_frame, sub_bfr);
return rslt.Init_obj(sub_bfr.To_str_and_rls());
}
else {
// String err_msg = "expand_template failed; ttl=" + ttl_str;
// cur_wiki.App().Usr_dlg().Warn_many("", "", err_msg);
return rslt.Init_ary_empty();
}
// BLOCK.end:Xot_invk_tkn.Transclude
}
public boolean IncrementExpensiveFunctionCount(Scrib_proc_args args, Scrib_proc_rslt rslt) {return rslt.Init_obj(KeyVal_.Ary_empty);} // NOTE: for now, always return null (XOWA does not care about expensive parser functions)
public boolean IsSubsting(Scrib_proc_args args, Scrib_proc_rslt rslt) {
boolean is_substing = false;
Xot_invk current_frame = core.Frame_current();
if (current_frame != null && Xot_defn_.Tid_is_substing(current_frame.Defn_tid())) // check current frame first
is_substing = true;
else { // check owner frame next
Xot_invk parent_frame = core.Frame_parent();
if (parent_frame != null && Xot_defn_.Tid_is_substing(parent_frame.Defn_tid()))
is_substing = true;
}
return rslt.Init_obj(is_substing);
}
public boolean NewChildFrame(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Ordered_hash frame_list = core.Frame_created_list();
int frame_list_len = frame_list.Count();
if (frame_list_len > 100) throw Exc_.new_("newChild: too many frames");
String frame_id = args.Pull_str(0);
Xot_invk frame = Scrib_frame_.Get_frame(core, frame_id);
Object ttl_obj = args.Cast_obj_or_null(1); // NOTE: callers must pass named title else title will be false; EX: frame:newChild{'current', 'title0'} -> false; frame:newChild{'current', title='title0'} -> 'title0'; DATE:2014-05-20
Xoa_ttl ttl = null;
if (ClassAdp_.ClassOf_obj(ttl_obj) != String.class) { // title = false
byte[] ttl_bry = frame.Frame_ttl();
ttl = Xoa_ttl.parse_(core.Wiki(), ttl_bry);
}
else {
ttl = Xoa_ttl.parse_(cur_wiki, Bry_.new_u8((String)ttl_obj));
if (ttl == null) throw Exc_.new_("newChild: invalid title", "title", (String)ttl_obj);
}
KeyVal[] args_ary = args.Pull_kv_ary(2);
Xot_invk_mock new_frame = Xot_invk_mock.new_(core.Frame_current().Defn_tid(), 0, ttl.Full_txt(), args_ary); // NOTE: use spaces, not unders; REF.MW:$frame->getTitle()->getPrefixedText(); DATE:2014-08-14
String new_frame_id = "frame" + Int_.Xto_str(frame_list_len);
frame_list.Add(new_frame_id, new_frame);
return rslt.Init_obj(new_frame_id);
}
public boolean GetFrameTitle(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String frame_id = args.Pull_str(0);
Xot_invk frame = Scrib_frame_.Get_frame(core, frame_id);
return rslt.Init_obj(frame.Frame_ttl());
}
public boolean SetTTL(Scrib_proc_args args, Scrib_proc_rslt rslt) { // needed for {{cite web}} PAGE:en.w:A DATE:2014-07-12
int timeToLive = args.Pull_int(0);
Xot_invk current_frame = core.Frame_current();
current_frame.Frame_lifetime_(timeToLive);
return rslt.Init_ary_empty();
}
}
class Scrib_lib_mw_callParserFunction_sorter implements gplx.lists.ComparerAble {
public int compare(Object lhsObj, Object rhsObj) {
KeyVal lhs = (KeyVal)lhsObj;
KeyVal rhs = (KeyVal)rhsObj;
Object lhs_key = lhs.Key_as_obj();
Object rhs_key = rhs.Key_as_obj();
boolean lhs_is_int = Int_.TypeMatch(lhs_key.getClass());
boolean rhs_is_int = Int_.TypeMatch(rhs_key.getClass());
if (lhs_is_int != rhs_is_int) // different types (int vs String or String vs int)
return lhs_is_int ? CompareAble_.Less : CompareAble_.More; // sort ints before strings
if (lhs_is_int) // both are ints
return Int_.Compare(Int_.cast_(lhs_key), Int_.cast_(rhs_key));
else // both are strings
return String_.Compare(String_.cast_(lhs_key), String_.cast_(rhs_key));
}
public static final Scrib_lib_mw_callParserFunction_sorter _ = new Scrib_lib_mw_callParserFunction_sorter(); Scrib_lib_mw_callParserFunction_sorter() {}
}

View File

@@ -0,0 +1,126 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_mw__invoke_tst {
@Before public void init() {
fxt.Clear_for_invoke();
lib = fxt.Core().Lib_mw().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void GetAllExpandedArguments_ws_prm_key_exists() { // PURPOSE: trim val if key exists; parameterized value ("key={{{1}}}")
fxt.Init_tmpl("{{#invoke:Mod_0|Prc_0|key={{{1}}}}}");
fxt.Init_page("{{test| a }}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getAllExpandedArguments, Object_.Ary("current"), "\n a"); // " a " -> "a"
}
@Test public void GetAllExpandedArguments_ws_prm_key_missing() { // PURPOSE: do not trim val if key missing; parameterized value ("{{{1}}}")
fxt.Init_tmpl("{{#invoke:Mod_0|Prc_0|{{{1}}}}}");
fxt.Init_page("{{test| a }}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getAllExpandedArguments, Object_.Ary("current"), "\n a "); // " a " -> " a "
}
@Test public void GetAllExpandedArguments__ignore_empty_key() {// PURPOSE: ignore arguents that have an empty key (|=8|); EX:w:Fool's_mate; DATE:2014-03-05
fxt.Init_tmpl("{{#invoke:Mod_0|Prc_0}}");
fxt.Init_page("{{test|a1||a2|=a3|a4}}");
fxt.Init_server_print_key_y_();
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getAllExpandedArguments, Object_.Ary("parent"), "\n 1:a1;2:;3:a2;4:a4"); // NOTE: || is not ignored but |=a3| is
fxt.Init_server_print_key_n_();
}
@Test public void GetExpandedArgument_ws_key_exists() { // PURPOSE: trim val if key exists; literal value
fxt.Init_page("{{#invoke:Mod_0|Prc_0| key1 = val1 }}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "key1") , "val1"); // "key1" -> "key1"
}
@Test public void GetExpandedArgument_ws_key_missing() { // PURPOSE: do not trim val if key missing; literal value
fxt.Init_page("{{#invoke:Mod_0|Prc_0| a }}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "1") , " a "); // " a " -> " a "
}
@Test public void GetExpandedArgument_ws_key_prm_key_exists() { // PURPOSE: trim val if key exists; parameterized value ("key={{{1}}}")
fxt.Init_tmpl("{{#invoke:Mod_0|Prc_0|key1={{{1}}}}}");
fxt.Init_page("{{test| a }}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "key1") , "a"); // " a " -> "a"
}
@Test public void GetExpandedArgument_ws_key_prm_key_missing() { // PURPOSE: do not trim val if key missing; parameterized value ("{{{1}}}")
fxt.Init_tmpl("{{#invoke:Mod_0|Prc_0|{{{1}}}}}");
fxt.Init_page("{{test| a }}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "1") , " a "); // " a " -> " a "
}
@Test public void Preprocess() {
this.Init_preprocess();
this.Exec_preprocess(Scrib_core.Frame_key_module , "1", "c");
this.Exec_preprocess(Scrib_core.Frame_key_module , "2", "d");
this.Exec_preprocess(Scrib_core.Frame_key_template , "1", "a");
this.Exec_preprocess(Scrib_core.Frame_key_template , "2", "b");
}
@Test public void Preprocess_duplicate_key() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0|key1=a|key2=b|key1=c}}"); // add key1 twice
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_preprocess, Object_.Ary("current", "{{#ifeq:1|1|{{{key1}}}|{{{key2}}}}}"), "c");
}
@Test public void CallParserFunction() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Test_lib_proc_kv(lib, Scrib_lib_mw.Invk_callParserFunction, Scrib_kv_utl_.base1_many_ary_("current", "#expr", "1") , "1"); // named: args is scalar
fxt.Test_lib_proc_kv(lib, Scrib_lib_mw.Invk_callParserFunction, Scrib_kv_utl_.base1_many_ary_("current", "#if", Scrib_kv_utl_.base1_many_("", "y", "n")) , "n"); // named: args is table
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_callParserFunction, Object_.Ary("current", "#if", "", "y", "n") , "n"); // list: args is ary
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_callParserFunction, Object_.Ary("current", "#if", Scrib_kv_utl_.base1_many_("", "y", "n")) , "n"); // list: args is table
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_callParserFunction, Object_.Ary("current", "#if:", "y", "n") , "n"); // colon_in_name
}
@Test public void CallParserFunction_tag() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Test_lib_proc_kv(lib, Scrib_lib_mw.Invk_callParserFunction, Scrib_kv_utl_.flat_many_(1, "current", 2, "#tag", 3, Scrib_kv_utl_.flat_many_("3", "id=1", "2", "text", "1", "pre")), "<pre 3=\"id=1\">2=text</pre>");// named: sort args; NOTE: keys should probably be stripped
}
@Test public void CallParserFunction_displayTitle() { // PURPOSE: DISPLAYTITLE not being set when called through CallParserFunction; DATE:2013-08-05
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Test_lib_proc_kv(lib, Scrib_lib_mw.Invk_callParserFunction, Scrib_kv_utl_.base1_many_ary_("current", "DISPLAYTITLE", "''a''"), "");
Tfds.Eq("<i>a</i>", String_.new_a7(fxt.Parser_fxt().Ctx().Cur_page().Html_data().Display_ttl()));
}
@Test public void ExpandTemplate_tmpl() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Parser_fxt().Data_create("Template:A", "b{{{key1}}}c");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_expandTemplate, Object_.Ary("current", "A", Scrib_kv_utl_.flat_many_("key1", "val1")) , "bval1c"); // list: args is ary
}
@Test public void ExpandTemplate() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Parser_fxt().Init_page_create("Template:Format", "{{{1}}}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_expandTemplate, Object_.Ary("current", "Format", Scrib_kv_utl_.base1_many_("a")), "a");
}
@Test public void ExpandTemplate_ns_specified() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Parser_fxt().Init_page_create("Template:Format", "{{{1}}}");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_expandTemplate, Object_.Ary("current", "Template:Format", Scrib_kv_utl_.base1_many_("a")), "a"); // NOTE: "Template:" specified
}
@Test public void ExpandTemplate_tmpl_bool() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Parser_fxt().Data_create("Template:Scribunto_bool", "bool_true={{{bool_true}}};bool_false={{{bool_false}}};");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_expandTemplate, Object_.Ary("current", "Scribunto_bool", Scrib_kv_utl_.flat_many_("bool_true", true, "bool_false", false)), "bool_true=1;bool_false={{{bool_false}}};");
}
@Test public void ExpandTemplate_page() {
fxt.Init_page("{{#invoke:Mod_0|Prc_0}}");
fxt.Parser_fxt().Data_create("A", "b{{{key1}}}c");
fxt.Test_lib_proc(lib, Scrib_lib_mw.Invk_expandTemplate, Object_.Ary("current", ":A", Scrib_kv_utl_.flat_many_("key1", "val1")) , "bval1c"); // list: args is ary
}
@Test public void Err_mod_blank() {fxt.Test_parse_err("{{#invoke:}}" , Scrib_invoke_func.Err_mod_missing);}
@Test public void Err_mod_missing() {fxt.Test_parse_err("{{#invoke:Missing}}" , Scrib_invoke_func.Err_mod_missing);}
private void Init_preprocess() {
fxt.Init_tmpl("{{#invoke:Mod_0|Func_0|1|c|d}}"); // current
fxt.Init_page("{{test|1|a|b|c}}"); // parent
fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_mw(), Scrib_lib_mw.Invk_preprocess);
}
private void Exec_preprocess(String frame, String arg_idx, String expd) {
fxt.Parser_fxt().Wiki().Cache_mgr().Tmpl_result_cache().Clear();
fxt.Init_lua_module();
fxt.Init_lua_rcvd_preprocess(frame, "{{#ifeq:" + arg_idx + "|{{{1}}}|{{{2}}}|{{{3}}}}}");
fxt.Test_invoke(expd);
}
}

View File

@@ -0,0 +1,89 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_mw__lib_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_mw().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void ParentFrameExists() {
fxt.Init_frame_parent("test");
fxt.Test_scrib_proc_bool(lib, Scrib_lib_mw.Invk_parentFrameExists, Object_.Ary_empty, true);
}
@Test public void ParentFrameExists_false() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_mw.Invk_parentFrameExists, Object_.Ary_empty, false);
}
@Test public void GetAllExpandedArguments() {
fxt.Init_frame_current(KeyVal_.new_("k1", "v1"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_mw.Invk_getAllExpandedArguments, Object_.Ary("current"), "1=\n k1=v1");
}
@Test public void GetAllExpandedArguments_parent() {
fxt.Init_frame_parent("test", KeyVal_.new_("1", "a1"), KeyVal_.new_("2", "a2"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_mw.Invk_getAllExpandedArguments, Object_.Ary("parent"), "1=\n 1=a1\n 2=a2");
}
@Test public void GetExpandedArgument() {
fxt.Init_frame_current(KeyVal_.int_(1, "val_1"), KeyVal_.new_("key_2", "val_2"), KeyVal_.int_(3, "val_3"));
fxt.Test_scrib_proc_str (lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "1") , "val_1"); // get 1st by idx
fxt.Test_scrib_proc_str (lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "2") , "val_3"); // get 2nd by idx (which is "3", not "key_2)
fxt.Test_scrib_proc_empty (lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "3")); // get 3rd by idx (which is n/a, not "val_3")
fxt.Test_scrib_proc_str (lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "key_2") , "val_2"); // get key_2
fxt.Test_scrib_proc_empty (lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "key_3")); // key_3 n/a
}
@Test public void GetExpandedArgument_parent() {
fxt.Init_frame_parent ("test", KeyVal_.new_("1", "a1"), KeyVal_.new_("2", "a2"));
fxt.Init_frame_current(KeyVal_.new_("2", "b2"));
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("parent", "1"), "a1");
}
@Test public void GetExpandedArgument_numeric_key() { // PURPOSE.FIX: frame.args[1] was ignoring "1=val_1" b/c it was looking for 1st unnamed arg (and 1 is the name for "1=val_1")
fxt.Init_frame_current(KeyVal_.new_("1", "val_1"));
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("current", "1"), "val_1"); // get 1st by idx, even though idx is String
}
@Test public void GetExpandedArgument_numeric_key_2() { // PURPOSE.FIX: same as above, but for parent context; DATE:2013-09-23
fxt.Init_frame_parent ("test", KeyVal_.new_("2", "a1"));
fxt.Init_frame_current(KeyVal_.new_("2", "a2"));
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("parent", "2"), "a1"); // get 1st by idx, even though idx is String
}
@Test public void GetExpandedArgument_out_of_bounds() {
fxt.Init_frame_parent ("test");
fxt.Test_scrib_proc_empty(lib, Scrib_lib_mw.Invk_getExpandedArgument, Object_.Ary("parent", "2"));
}
@Test public void IsSubsting() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_mw.Invk_isSubsting, Object_.Ary_empty, false);
}
@Test public void GetFrameTitle_current() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_getFrameTitle, Object_.Ary("current") , "Module:Mod_0");
}
@Test public void GetFrameTitle_parent() {
fxt.Init_frame_parent("Template:Test A");
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_getFrameTitle, Object_.Ary("parent") , "Template:Test A");
}
@Test public void GetFrameTitle_empty() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_getFrameTitle, Object_.Ary("empty") , Scrib_invoke_func_fxt.Null_rslt);
}
@Test public void NewChildFrame() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_newChildFrame, Object_.Ary("current", "Page_0", Scrib_kv_utl_.flat_many_("key1", "val1")), "frame0");
}
@Test public void SetTTL() {
fxt.Test_scrib_proc_empty(lib, Scrib_lib_mw.Invk_setTTL, Object_.Ary(123));
Tfds.Eq(123, fxt.Core().Frame_current().Frame_lifetime());
}
@Test public void LoadPHPLibrary() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_mw.Invk_loadPHPLibrary, Object_.Ary("mock_library") , Scrib_invoke_func_fxt.Null_rslt);
}
}

View File

@@ -0,0 +1,181 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.wikis.xwikis.*;
public class Scrib_lib_site implements Scrib_lib {
public Scrib_lib_site(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.site.lua"));// NOTE: info not passed by default; rely on Init_for_wiki
notify_wiki_changed_fnc = mod.Fncs_get_by_key("notify_wiki_changed");
return mod;
} private Scrib_lua_proc notify_wiki_changed_fnc;
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_getNsIndex: return GetNsIndex(args, rslt);
case Proc_pagesInCategory: return PagesInCategory(args, rslt);
case Proc_pagesInNs: return PagesInNs(args, rslt);
case Proc_usersInGroup: return UsersInGroup(args, rslt);
case Proc_interwikiMap: return InterwikiMap(args, rslt);
case Proc_init_site_for_wiki: return Init_site_for_wiki(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_getNsIndex = 0, Proc_pagesInCategory = 1, Proc_pagesInNs = 2, Proc_usersInGroup = 3, Proc_interwikiMap = 4, Proc_init_site_for_wiki = 5;
public static final String Invk_getNsIndex = "getNsIndex", Invk_pagesInCategory = "pagesInCategory", Invk_pagesInNs = "pagesInName"+"space", Invk_usersInGroup = "usersInGroup", Invk_interwikiMap = "interwikiMap", Invk_init_site_for_wiki = "init_site_for_wiki";
private static final String[] Proc_names = String_.Ary(Invk_getNsIndex, Invk_pagesInCategory, Invk_pagesInNs, Invk_usersInGroup, Invk_interwikiMap, Invk_init_site_for_wiki);
public boolean GetNsIndex(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ns_name = args.Pull_bry(0);
Object ns_obj = core.Wiki().Ns_mgr().Names_get_or_null(ns_name, 0, ns_name.length);
return ns_obj == null ? rslt.Init_ary_empty() : rslt.Init_obj(((Xow_ns)ns_obj).Id());
}
public boolean PagesInCategory(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ctg_name = args.Pull_bry(0);
// byte[] ctg_type = Scrib_kv_utl_.Val_to_bry(values, 1); // TODO.7: get by categoy type; WHEN: categories
int ctg_count = core.Wiki().Db_mgr().Load_mgr().Load_ctg_count(ctg_name);
return rslt.Init_obj(ctg_count);
}
public boolean PagesInNs(Scrib_proc_args args, Scrib_proc_rslt rslt) {
int ns_id = args.Pull_int(0);
Xow_ns ns = core.Wiki().Ns_mgr().Ids_get_or_null(ns_id);
int ns_count = ns == null ? 0 : ns.Count();
return rslt.Init_obj(ns_count);
}
public boolean UsersInGroup(Scrib_proc_args args, Scrib_proc_rslt rslt) { // TODO.9: get user_groups table
// byte[] grp_name = args.Pull_bry(0);
return rslt.Init_obj(0);
}
public boolean InterwikiMap(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String filter = args.Cast_str_or_null(0);
int local = -1;
if (String_.Eq(filter, "local"))
local = 1;
else if (String_.Eq(filter, "!local"))
local = 0;
else if (filter != null)
throw Exc_.new_("bad argument #1 to 'interwikiMap' (unknown filter '$filter')", "filter", filter);
// TODO: cache interwikimap results
Xow_xwiki_mgr xwiki_mgr = core.Wiki().Xwiki_mgr();
int xwiki_len = xwiki_mgr.Len();
KeyVal[][] rv = new KeyVal[xwiki_len][];
for (int i = 0; i < xwiki_len; ++i) {
Xow_xwiki_itm itm = xwiki_mgr.Get_at(i);
boolean itm_is_local = itm.Offline();
if (local == 1 && !itm_is_local) continue;
if (local == 0 && itm_is_local) continue;
String prefix = itm.Key_str();
rv[i] = InterwikiMap_itm(itm, prefix, itm_is_local);
}
return rslt.Init_obj(rv);
}
private KeyVal[] InterwikiMap_itm(Xow_xwiki_itm itm, String prefix, boolean itm_is_local) {
boolean is_extralanguage_link = false;
int rv_len = 7;
if (is_extralanguage_link) rv_len += 2;
String url = String_.new_u8(itm.Domain_bry());
boolean url_is_relative = String_.Has_at_bgn(url, "//");
KeyVal[] rv = new KeyVal[rv_len];
rv[ 0] = KeyVal_.new_("prefix" , prefix);
rv[ 1] = KeyVal_.new_("url" , url); // wfExpandUrl( $row['iw_url'], PROTO_RELATIVE ),
rv[ 2] = KeyVal_.new_("isProtocolRelative" , url_is_relative); // substr( $row['iw_url'], 0, 2 ) === '//',
rv[ 3] = KeyVal_.new_("isLocal" , itm_is_local); // isset( $row['iw_local'] ) && $row['iw_local'] == '1',
rv[ 4] = KeyVal_.new_("isTranscludable" , Bool_.N); // isset( $row['iw_trans'] ) && $row['iw_trans'] == '1',
rv[ 5] = KeyVal_.new_("isCurrentWiki" , Bool_.N); // in_array( $prefix, $wgLocalInterwikis ),
rv[ 6] = KeyVal_.new_("isExtraLanguageLink" , is_extralanguage_link); // in_array( $prefix, $wgExtraInterlanguageLinkPrefixes ),
if (is_extralanguage_link) {
rv[7] = KeyVal_.new_("displayText" , "displayText_TODO"); // $displayText = wfMessage( "interlanguage-link-$prefix" ); if ( !$displayText->isDisabled() ) ...
rv[7] = KeyVal_.new_("tooltip" , "tooltip_TODO"); // $tooltip = wfMessage( "interlanguage-link-sitename-$prefix" );
}
return rv;
}
public void Notify_wiki_changed() {if (notify_wiki_changed_fnc != null) core.Interpreter().CallFunction(notify_wiki_changed_fnc.Id(), KeyVal_.Ary_empty);}
public boolean Init_site_for_wiki(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xowe_wiki wiki = core.Wiki();
KeyVal[] rv = new KeyVal[7];
Bld_info(rv);
rv[5] = KeyVal_.new_("name" + "spaces", Bld_ns_ary(wiki));
rv[6] = KeyVal_.new_("stats", Bld_stats(wiki));
return rslt.Init_obj(rv);
}
private void Bld_info(KeyVal[] rv) {
Xow_wiki_props props = core.Wiki().Props();
rv[0] = KeyVal_.new_("siteName" , props.Site_name());
rv[1] = KeyVal_.new_("server" , props.Server());
rv[2] = KeyVal_.new_("scriptPath" , props.ScriptPath());
rv[3] = KeyVal_.new_("stylePath" , props.StylePath());
rv[4] = KeyVal_.new_("currentVersion" , props.Current_version());
}
private KeyVal[] Bld_ns_ary(Xowe_wiki wiki) {
Xow_ns_mgr ns_mgr = wiki.Ns_mgr();
int len = ns_mgr.Ids_len();
KeyVal[] rv = new KeyVal[len];
int rv_idx = 0;
for (int i = 0; i < len; i++) {
Xow_ns ns = ns_mgr.Ids_get_at(i);
if (ns == null) continue;
int ns_id = ns.Id();
rv[rv_idx++] = KeyVal_.int_(ns_id, Bld_ns(wiki, ns, ns_id));
}
return rv;
}
private KeyVal[] Bld_ns(Xowe_wiki wiki, Xow_ns ns, int ns_id) {
int len = 16;
if (ns_id < Xow_ns_.Id_main) len = 14;
else if (ns_id == Xow_ns_.Id_main) len = 17;
KeyVal[] rv = new KeyVal[len];
rv[ 0] = KeyVal_.new_("id" , ns_id);
rv[ 1] = KeyVal_.new_("name" , ns.Name_txt());
rv[ 2] = KeyVal_.new_("canonicalName" , ns.Name_str()); // strtr( $canonical, "_", " " ),
rv[ 3] = KeyVal_.new_("hasSubpages" , ns.Subpages_enabled()); // MWNs::hasSubpages( $ns ),
rv[ 4] = KeyVal_.new_("hasGenderDistinction" , ns.Is_gender_aware()); // MWNs::hasGenderDistinction( $ns ),
rv[ 5] = KeyVal_.new_("isCapitalized" , ns.Is_capitalized()); // MWNs::isCapitalized( $ns ),
rv[ 6] = KeyVal_.new_("isContent" , ns.Is_content()); // MWNs::isContent( $ns ),
rv[ 7] = KeyVal_.new_("isIncludable" , ns.Is_includable()); // !MWNs::isNonincludable( $ns ),
rv[ 8] = KeyVal_.new_("isMovable" , ns.Is_movable()); // MWNs::isMovable( $ns ),
rv[ 9] = KeyVal_.new_("isSubject" , ns.Id_subj());
rv[10] = KeyVal_.new_("isTalk" , ns.Id_talk());
rv[11] = KeyVal_.new_("defaultContentModel" , null); // MWNs::getNsContentModel( $ns ), ASSUME: always null?
rv[12] = KeyVal_.new_("aliases" , ns.Aliases_as_scrib_ary()); // DATE:2014-02-15
if (ns_id < 0)
rv[13] = KeyVal_.new_("subject" , ns_id); // MWNs::getSubject( $ns );
else {
rv[13] = KeyVal_.new_("subject" , ns_id); // MWNs::getSubject( $ns );
rv[14] = KeyVal_.new_("talk" , ns.Id_talk_id()); // MWNs::getTalk( $ns );
rv[15] = KeyVal_.new_("associated" , ns.Id_alt_id()); // MWNs::getAssociated( $ns );
if (ns_id == Xow_ns_.Id_main)
rv[16] = KeyVal_.new_("displayName" , wiki.Msg_mgr().Val_by_id(Xol_msg_itm_.Id_ns_blankns)); // MWNs::getAssociated( $ns );
}
return rv;
}
private KeyVal[] Bld_stats(Xowe_wiki wiki) {
Xow_wiki_stats stats = wiki.Stats();
KeyVal[] rv = new KeyVal[8];
rv[0] = KeyVal_.new_("pages", stats.NumPages()); // SiteStats::pages(),
rv[1] = KeyVal_.new_("articles", stats.NumArticles()); // SiteStats::articles(),
rv[2] = KeyVal_.new_("files", stats.NumFiles()); // SiteStats::images(),
rv[3] = KeyVal_.new_("edits", stats.NumEdits()); // SiteStats::edits(),
rv[4] = KeyVal_.new_("views", stats.NumViews()); // $wgDisableCounters ? null : (int)SiteStats::views(),
rv[5] = KeyVal_.new_("users", stats.NumUsers()); // SiteStats::users(),
rv[6] = KeyVal_.new_("activeUsers", stats.NumUsersActive());// SiteStats::activeUsers(),
rv[7] = KeyVal_.new_("admins", stats.NumAdmins()); // SiteStats::activeUsers(),
return rv;
}
}

View File

@@ -0,0 +1,159 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_site_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_site().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void GetNsIndex() {
fxt.Test_scrib_proc_int(lib, Scrib_lib_site.Invk_getNsIndex, Object_.Ary("Help"), 12);
}
@Test public void GetNsIndex_invalid() {
fxt.Test_scrib_proc_empty(lib, Scrib_lib_site.Invk_getNsIndex, Object_.Ary("Helpx")); // unknown ns; return empty String
}
@Test public void UsersInGroup() {
fxt.Test_scrib_proc_int(lib, Scrib_lib_site.Invk_usersInGroup, Object_.Ary("sysop"), 0); // SELECT * FROM user_groups;
}
@Test public void PagesInCategory() {
fxt.Test_scrib_proc_int(lib, Scrib_lib_site.Invk_pagesInCategory, Object_.Ary("A"), 0);
}
@Test public void PagesInNs() {
fxt.Test_scrib_proc_int(lib, Scrib_lib_site.Invk_pagesInNs, Object_.Ary("12"), 0);
}
@Test public void Init_lib_site() {
Xowe_wiki wiki = fxt.Parser_fxt().Wiki();
fxt.Parser_fxt().Wiki().Stats().NumPages_(1).NumArticles_(2).NumFiles_(3).NumEdits_(4).NumViews_(5).NumUsers_(6).NumUsersActive_(7).NumAdmins_(8);
wiki.Ns_mgr()
.Clear()
.Add_new(Scrib_xtn_mgr.Ns_id_module, "Module")
.Add_new(Scrib_xtn_mgr.Ns_id_module_talk, "Module talk")
.Add_new(Xow_ns_.Id_special, "Special")
.Add_new(Xow_ns_.Id_main, "")
.Add_new(Xow_ns_.Id_talk, "Talk")
.Init_w_defaults()
;
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_site.Invk_init_site_for_wiki, Object_.Ary_empty, String_.Concat_lines_nl_skip_last
( "1="
, " siteName=Wikipedia"
, " server=http://en.wikipedia.org"
, " scriptPath=/wiki"
, " stylePath=/wiki/skins"
, " currentVersion=1.21wmf11"
, " namespaces="
, " -1="
, " id=-1"
, " name=Special"
, " canonicalName=Special"
, " hasSubpages=false"
, " hasGenderDistinction=false"
, " isCapitalized=false"
, " isContent=false"
, " isIncludable=true"
, " isMovable=false"
, " isSubject=true"
, " isTalk=false"
, " defaultContentModel=<<NULL>>"
, " aliases="
, " subject=-1"
, " 0="
, " id=0"
, " name="
, " canonicalName="
, " hasSubpages=false"
, " hasGenderDistinction=false"
, " isCapitalized=false"
, " isContent=true"
, " isIncludable=true"
, " isMovable=true"
, " isSubject=true"
, " isTalk=false"
, " defaultContentModel=<<NULL>>"
, " aliases="
, " subject=0"
, " talk=1"
, " associated=1"
, " displayName=(Main)"
, " 1="
, " id=1"
, " name=Talk"
, " canonicalName=Talk"
, " hasSubpages=false"
, " hasGenderDistinction=false"
, " isCapitalized=false"
, " isContent=false"
, " isIncludable=true"
, " isMovable=true"
, " isSubject=false"
, " isTalk=true"
, " defaultContentModel=<<NULL>>"
, " aliases="
, " subject=1"
, " talk=1"
, " associated=0"
, " 828="
, " id=828"
, " name=Module"
, " canonicalName=Module"
, " hasSubpages=false"
, " hasGenderDistinction=false"
, " isCapitalized=false"
, " isContent=false"
, " isIncludable=true"
, " isMovable=true"
, " isSubject=true"
, " isTalk=false"
, " defaultContentModel=<<NULL>>"
, " aliases="
, " subject=828"
, " talk=829"
, " associated=829"
, " 829="
, " id=829"
, " name=Module talk"
, " canonicalName=Module_talk"
, " hasSubpages=false"
, " hasGenderDistinction=false"
, " isCapitalized=false"
, " isContent=false"
, " isIncludable=true"
, " isMovable=true"
, " isSubject=false"
, " isTalk=true"
, " defaultContentModel=<<NULL>>"
, " aliases="
, " subject=829"
, " talk=829"
, " associated=828"
, " stats="
, " pages=1"
, " articles=2"
, " files=3"
, " edits=4"
, " views=5"
, " users=6"
, " activeUsers=7"
, " admins=8"
));
}
// @Test public void LoadSiteStats() { // deprecated by Scribunto; DATE:2013-04-12
// fxt.Parser_fxt().Wiki().Stats().NumPages_(1).NumArticles_(2).NumFiles_(3).NumEdits_(4).NumViews_(5).NumUsers_(6).NumUsersActive_(7);
// fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_site.Invk_loadSiteStats, Object_.Ary_empty, "1;2;3;4;5;6;7");
// }
}

View File

@@ -0,0 +1,112 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.net.*;
public class Scrib_lib_text implements Scrib_lib {
public Scrib_lib_text(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.text.lua"));
notify_wiki_changed_fnc = mod.Fncs_get_by_key("notify_wiki_changed");
return mod;
} private Scrib_lua_proc notify_wiki_changed_fnc;
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_unstrip: return Unstrip(args, rslt);
case Proc_unstripNoWiki: return UnstripNoWiki(args, rslt);
case Proc_killMarkers: return KillMarkers(args, rslt);
case Proc_getEntityTable: return GetEntityTable(args, rslt);
case Proc_init_text_for_wiki: return Init_text_for_wiki(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_unstrip = 0, Proc_unstripNoWiki = 1, Proc_killMarkers = 2, Proc_getEntityTable = 3, Proc_init_text_for_wiki = 4;
public static final String Invk_unstrip = "unstrip", Invk_unstripNoWiki = "unstripNoWiki", Invk_killMarkers = "killMarkers", Invk_getEntityTable = "getEntityTable", Invk_init_text_for_wiki = "init_text_for_wiki";
private static final String[] Proc_names = String_.Ary(Invk_unstrip, Invk_unstripNoWiki, Invk_killMarkers, Invk_getEntityTable, Invk_init_text_for_wiki);
public boolean Unstrip(Scrib_proc_args args, Scrib_proc_rslt rslt) {return rslt.Init_obj(args.Pull_str(0));} // NOTE: XOWA does not use MediaWiki strip markers; just return original; DATE:2015-01-20
public boolean UnstripNoWiki(Scrib_proc_args args, Scrib_proc_rslt rslt) {return rslt.Init_obj(args.Pull_str(0));} // NOTE: XOWA does not use MediaWiki strip markers; just return original; DATE:2015-01-20
public boolean KillMarkers(Scrib_proc_args args, Scrib_proc_rslt rslt) {return rslt.Init_obj(args.Pull_str(0));} // NOTE: XOWA does not use MediaWiki strip markers; just return original; DATE:2015-01-20
public boolean GetEntityTable(Scrib_proc_args args, Scrib_proc_rslt rslt) {
if (Html_entity_ == null) Html_entity_ = Scrib_lib_text_html_entities.new_();
return rslt.Init_obj(Html_entity_);
} private static KeyVal[] Html_entity_;
public void Notify_wiki_changed() {if (notify_wiki_changed_fnc != null) core.Interpreter().CallFunction(notify_wiki_changed_fnc.Id(), KeyVal_.Ary_empty);}
public boolean Init_text_for_wiki(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xow_msg_mgr msg_mgr = core.Wiki().Msg_mgr();
KeyVal[] rv = new KeyVal[4];
rv[0] = KeyVal_.new_("comma", Init_lib_text_get_msg(msg_mgr, "comma-separator"));
rv[1] = KeyVal_.new_("and", Init_lib_text_get_msg(msg_mgr, "and") + Init_lib_text_get_msg(msg_mgr, "word-separator"));
rv[2] = KeyVal_.new_("ellipsis", Init_lib_text_get_msg(msg_mgr, "ellipsis"));
rv[3] = KeyVal_.new_("nowiki_protocols", KeyVal_.Ary_empty); // NOTE: code implemented, but waiting for it to be used; DATE:2014-03-20
return rslt.Init_obj(rv);
}
private String Init_lib_text_get_msg(Xow_msg_mgr msg_mgr, String msg_key) {
return String_.new_u8(msg_mgr.Val_by_key_obj(Bry_.new_u8(msg_key)));
}
}
class Scrib_lib_text_ {
public static KeyVal[] Init_nowiki_protocols(Xowe_wiki wiki) {
Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b128();
Ordered_hash protocols = Xoo_protocol_itm.Regy;
int len = protocols.Count();
List_adp rv = List_adp_.new_();
for (int i = 0; i < len; i++) {
Xoo_protocol_itm itm = (Xoo_protocol_itm)protocols.Get_at(i);
if (itm.Text_ends_w_colon()) { // To convert the protocol into a case-insensitive Lua pattern, we need to replace letters with a character class like [Xx] and insert a '%' before various punctuation.
KeyVal kv = Init_nowiki_protocols_itm(bfr, itm);
rv.Add(kv);
}
}
bfr.Mkr_rls();
return (KeyVal[])rv.To_ary(KeyVal.class);
}
private static KeyVal Init_nowiki_protocols_itm(Bry_bfr bfr, Xoo_protocol_itm itm) {
byte[] key = itm.Key_wo_colon_bry();
int end = key.length - 1; // -1 to ignore final colon
for (int i = 0; i < end; i++) {
byte b = key[i];
switch (b) {
case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E:
case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J:
case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O:
case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T:
case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z:
bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(b).Add_byte(Byte_ascii.Case_lower(b)).Add_byte(Byte_ascii.Brack_end); // [Aa]
break;
case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e:
case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j:
case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o:
case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t:
case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z:
bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Case_upper(b)).Add_byte(b).Add_byte(Byte_ascii.Brack_end); // [Aa]
break;
case Byte_ascii.Paren_bgn: case Byte_ascii.Paren_end: case Byte_ascii.Pow: case Byte_ascii.Dollar: case Byte_ascii.Percent: case Byte_ascii.Dot:
case Byte_ascii.Brack_bgn: case Byte_ascii.Brack_end: case Byte_ascii.Star: case Byte_ascii.Plus: case Byte_ascii.Question: case Byte_ascii.Dash:
bfr.Add_byte(Byte_ascii.Percent).Add_byte(b); // regex is '/([a-zA-Z])|([()^$%.\[\]*+?-])/'
break;
default: // ignore
break;
}
}
bfr.Add(Colon_encoded);
return KeyVal_.new_(itm.Key_wo_colon_str(), bfr.Xto_str_and_clear());
} private static final byte[] Colon_encoded = Bry_.new_a7("&#58;");
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_text_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_text().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void Unstrip() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_text.Invk_unstrip, Object_.Ary("a"), "a");
}
@Test public void GetEntityTable() {
KeyVal[] actl = fxt.Test_scrib_proc_rv_as_kv_ary(lib, Scrib_lib_text.Invk_getEntityTable, Object_.Ary());
Tfds.Eq(1510, actl.length); // large result; only test # of entries
}
}

View File

@@ -0,0 +1,230 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.core.primitives.*;
import gplx.xowa.wikis.caches.*; import gplx.xowa.xtns.pfuncs.ttls.*; import gplx.xowa.wikis.xwikis.*; import gplx.xowa.wikis.data.tbls.*;
import gplx.xowa2.files.commons.*; import gplx.xowa.files.origs.*;
import gplx.xowa.wmfs.apis.*;
public class Scrib_lib_title implements Scrib_lib {
public Scrib_lib_title(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.title.lua")
, KeyVal_.new_("thisTitle", "") // NOTE: pass blank; will be updated by GetCurrentTitle
, KeyVal_.new_("NS_MEDIA", Xow_ns_.Id_media) // NOTE: MW passes down NS_MEDIA; this should be -2 on all wikis...
);
notify_page_changed_fnc = mod.Fncs_get_by_key("notify_page_changed");
return mod;
} private Scrib_lua_proc notify_page_changed_fnc;
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_newTitle: return NewTitle(args, rslt);
case Proc_makeTitle: return MakeTitle(args, rslt);
case Proc_getExpensiveData: return GetExpensiveData(args, rslt);
case Proc_getUrl: return GetUrl(args, rslt);
case Proc_getContent: return GetContent(args, rslt);
case Proc_getFileInfo: return GetFileInfo(args, rslt);
case Proc_getCurrentTitle: return GetCurrentTitle(args, rslt);
case Proc_protectionLevels: return ProtectionLevels(args, rslt);
case Proc_cascadingProtection: return CascadingProtection(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_newTitle = 0, Proc_makeTitle = 1, Proc_getExpensiveData = 2, Proc_getUrl = 3, Proc_getContent = 4, Proc_getFileInfo = 5, Proc_getCurrentTitle = 6, Proc_protectionLevels = 7, Proc_cascadingProtection = 8;
public static final String
Invk_newTitle = "newTitle", Invk_getExpensiveData = "getExpensiveData", Invk_makeTitle = "makeTitle"
, Invk_getUrl = "getUrl", Invk_getContent = "getContent", Invk_getFileInfo = "getFileInfo", Invk_getCurrentTitle = "getCurrentTitle"
, Invk_protectionLevels = "protectionLevels", Invk_cascadingProtection = "cascadingProtection"
;
private static final String[] Proc_names = String_.Ary(Invk_newTitle, Invk_makeTitle, Invk_getExpensiveData, Invk_getUrl, Invk_getContent, Invk_getFileInfo, Invk_getCurrentTitle, Invk_protectionLevels, Invk_cascadingProtection);
public boolean NewTitle(Scrib_proc_args args, Scrib_proc_rslt rslt) {
if (args.Len() == 0) return rslt.Init_obj(null); // invalid title, return null; EX:{{#invoke:Message box|fmbox}} DATE:2015-03-04
byte[] ttl_bry = args.Pull_bry(0);
Object ns_obj = args.Cast_obj_or_null(1);
Xowe_wiki wiki = core.Wiki();
byte[] ns_bry = null;
if (ns_obj != null) {
ns_bry = Parse_ns(wiki, ns_obj); if (ns_bry == null) throw Exc_.new_("unknown ns", "ns", Object_.Xto_str_strict_or_empty(ns_bry));
}
if (ns_bry != null) {
Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512();
ttl_bry = bfr.Add(ns_bry).Add_byte(Byte_ascii.Colon).Add(ttl_bry).To_bry_and_rls();
}
Xoa_ttl ttl = Xoa_ttl.parse_(core.Wiki(), ttl_bry);
if (ttl == null) return rslt.Init_obj(null); // invalid title; exit; matches MW logic
return rslt.Init_obj(GetInexpensiveTitleData(ttl));
}
public void Notify_page_changed() {if (notify_page_changed_fnc != null) core.Interpreter().CallFunction(notify_page_changed_fnc.Id(), KeyVal_.Ary_empty);}
public boolean GetUrl(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xowe_wiki wiki = core.Wiki();
byte[] ttl_bry = args.Pull_bry(0);
byte[] url_func_bry = args.Pull_bry(1);
Object url_func_obj = url_func_hash.Get_by(url_func_bry);
if (url_func_obj == null) throw Exc_.new_("url_function is not valid", "url_func", String_.new_u8(url_func_bry));
byte url_func_tid = ((Byte_obj_val)url_func_obj).Val();
byte[] qry_bry = args.Extract_qry_args(wiki, 2);
// byte[] proto = Scrib_kv_utl_.Val_to_bry_or(values, 3, null); // NOTE: Scribunto has more conditional logic around argument 2 and setting protocols; DATE:2014-07-07
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry); if (ttl == null) return rslt.Init_obj(null);
Bry_bfr bfr = wiki.Appe().Utl__bfr_mkr().Get_b512();
//if (url_func_tid == Pfunc_urlfunc.Tid_full) {
// if (proto == null) proto = Proto_relative;
// Object proto_obj = proto_hash.Get_by(proto); if (proto_obj == null) throw Err_.new_fmt_("protocol is not valid: {0}", proto);
// //qry_bry = (byte[])proto_obj;
// byte proto_tid = ((Byte_obj_val)proto_obj).Val();
// bfr.Add();
//}
Pfunc_urlfunc.UrlString(core.Ctx(), url_func_tid, false, ttl_bry, bfr, qry_bry);
return rslt.Init_obj(bfr.To_str_and_rls());
}
private static final Hash_adp_bry url_func_hash = Hash_adp_bry.ci_ascii_()
.Add_str_byte("fullUrl", Pfunc_urlfunc.Tid_full)
.Add_str_byte("localUrl", Pfunc_urlfunc.Tid_local)
.Add_str_byte("canonicalUrl", Pfunc_urlfunc.Tid_canonical);
// private static final byte[] Proto_relative = Bry_.new_a7("relative");
// private static final Hash_adp_bry proto_hash = Hash_adp_bry.ci_ascii_().Add_str_obj("http", Bry_.new_a7("http://")).Add_str_obj("https", Bry_.new_a7("https://")).Add_str_obj("relative", Bry_.new_a7("//")).Add_str_obj("canonical", Bry_.new_a7("1"));
private byte[] Parse_ns(Xowe_wiki wiki, Object ns_obj) {
if (ClassAdp_.Eq_typeSafe(ns_obj, String.class))
return Bry_.new_u8(String_.cast_(ns_obj));
else {
int ns_id = Int_.cast_(ns_obj);
Xow_ns ns = wiki.Ns_mgr().Ids_get_or_null(ns_id);
if (ns != null)
return ns.Name_bry();
}
return null;
}
public boolean MakeTitle(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xowe_wiki wiki = core.Wiki();
byte[] ns_bry = Parse_ns(wiki, args.Cast_obj_or_null(0));
String ttl_str = args.Pull_str(1);
String anchor_str = args.Cast_str_or_null(2);
String xwiki_str = args.Cast_str_or_null(3);
Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_k004();
if (xwiki_str != null) tmp_bfr.Add_str(xwiki_str).Add_byte(Byte_ascii.Colon);
if (Bry_.Len_gt_0(ns_bry)) // only prefix ns if available; EX:"Template:Title"; else will get ":Title"; DATE:2014-10-30
tmp_bfr.Add(ns_bry).Add_byte(Byte_ascii.Colon);
tmp_bfr.Add_str(ttl_str);
if (anchor_str != null) tmp_bfr.Add_byte(Byte_ascii.Hash).Add_str(anchor_str);
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, tmp_bfr.To_bry_and_rls());
if (ttl == null) return rslt.Init_obj(null); // invalid title; exit;
return rslt.Init_obj(GetInexpensiveTitleData(ttl));
}
public boolean GetExpensiveData(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ttl_bry = args.Pull_bry(0);
Xoa_ttl ttl = Xoa_ttl.parse_(core.Wiki(), ttl_bry);
if (ttl == Xoa_ttl.Null) return rslt.Init_null();
// TODO: MW does extra logic here to cache ttl in ttl cache to avoid extra title lookups
boolean ttl_exists = false, ttl_redirect = false; int ttl_id = 0;
synchronized (tmp_db_page) {
ttl_exists = core.Wiki().Db_mgr().Load_mgr().Load_by_ttl(tmp_db_page, ttl.Ns(), ttl.Page_db());
}
if (ttl_exists) {
ttl_redirect = tmp_db_page.Redirected();
ttl_id = tmp_db_page.Id();
}
KeyVal[] rv = new KeyVal[4];
rv[ 0] = KeyVal_.new_("isRedirect" , ttl_redirect); // title.isRedirect
rv[ 1] = KeyVal_.new_("id" , ttl_id); // $title->getArticleID(),
rv[ 2] = KeyVal_.new_("contentModel" , Key_wikitexet); // $title->getContentModel(); see Defines.php and CONTENT_MODEL_
rv[ 3] = KeyVal_.new_("exists" , ttl_exists); // $ret['id'] > 0; TODO: if Special: check regy of implemented pages
return rslt.Init_obj(rv);
}
public boolean GetFileInfo(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ttl_bry = args.Pull_bry(0);
Xowe_wiki wiki = core.Wiki();
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry);
if ( ttl == null
|| !ttl.Ns().Id_file_or_media()
) return rslt.Init_obj(GetFileInfo_absent);
if (ttl.Ns().Id_media()) ttl = Xoa_ttl.parse_(wiki, Xow_ns_.Id_file, ttl.Page_db()); // if [[Media:]] change to [[File:]]; theoretically, this should be changed in Get_page, but not sure if I want to put this logic that low; DATE:2014-01-07
// Xoae_page file_page = Pfunc_filepath.Load_page(wiki, ttl); // EXPENSIVE
// boolean exists = !file_page.Missing();
// if (!exists) return rslt.Init_obj(KeyVal_.Ary(KeyVal_.new_("exists", false))); // NOTE: do not reinstate; will exit early if commons is not installed; DATE:2015-01-25; NOTE: Media objects are often flagged as absent in offline mode
// NOTE: MW registers image if deleted; XOWA doesn't register b/c needs width / height also, not just image name
wiki.File_mgr().Init_file_mgr_by_load(wiki);
Xof_orig_itm itm = wiki.File__orig_mgr().Find_by_ttl_or_null(ttl.Page_db());
if (itm == Xof_orig_itm.Null) return rslt.Init_obj(GetFileInfo_absent);
KeyVal[] rv = KeyVal_.Ary
( KeyVal_.new_("exists" , true)
, KeyVal_.new_("width" , itm.W())
, KeyVal_.new_("height" , itm.H())
, KeyVal_.new_("pages" , null) // TODO: get pages info
);
return rslt.Init_obj(rv);
}
private static final KeyVal[] GetFileInfo_absent = KeyVal_.Ary(KeyVal_.new_("exists", false));
public boolean GetContent(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ttl_bry = args.Pull_bry(0);
Xowe_wiki wiki = core.Wiki();
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry); if (ttl == null) return rslt.Init_obj(null);
Xow_page_cache_itm page_itm = wiki.Cache_mgr().Page_cache().Get_or_load_as_itm(ttl);
byte[] rv = null;
if (page_itm != null) {
byte[] redirected_src = page_itm.Redirected_src_wtxt();
if (redirected_src != null) { // page is redirect; use its src, not its target's src; DATE:2014-07-11
rv = redirected_src;
core.Frame_parent().Rslt_is_redirect_(true); // flag frame as redirect, so that \n won't be prepended; EX:"#REDIRECT" x> "\n#REDIRECT"
}
else
rv = page_itm.Wtxt();
}
return rv == null ? rslt.Init_obj(null) : rslt.Init_obj(String_.new_u8(rv));
}
public boolean GetCurrentTitle(Scrib_proc_args args, Scrib_proc_rslt rslt) {
return rslt.Init_obj(GetInexpensiveTitleData(core.Page().Ttl()));
}
public boolean ProtectionLevels(Scrib_proc_args args, Scrib_proc_rslt rslt) {
// byte[] ttl_bry = args.Pull_bry(0);
// Xowe_wiki wiki = core.Wiki();
// Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry); if (ttl == null) return rslt.Init_obj(null);
return rslt.Init_many_objs(""); // NOTE: always return no protection; protectionLevels are stored in different table which is currently not mirrored; DATE:2014-04-09
}
public boolean CascadingProtection(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ttl_bry = args.Pull_bry(0);
Xowe_wiki wiki = core.Wiki();
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry); if (ttl == null) return rslt.Init_obj(null);
return rslt.Init_obj(CascadingProtection_rv);
}
public static final KeyVal[] CascadingProtection_rv = KeyVal_.Ary(KeyVal_.new_("sources", Bool_.N), KeyVal_.new_("restrictions", KeyVal_.Ary_empty));
private KeyVal[] GetInexpensiveTitleData(Xoa_ttl ttl) {
Xow_ns ns = ttl.Ns();
boolean ns_file_or_media = ns.Id_file_or_media(), ns_special = ns.Id_special();
int rv_len = 7, rv_idx = 7;
if (ns_special) ++rv_len;
if (!ns_file_or_media) ++rv_len;
Xow_xwiki_itm xwiki_itm = ttl.Wik_itm();
String xwiki_str = xwiki_itm == null ? "" : xwiki_itm.Key_str();
KeyVal[] rv = new KeyVal[rv_len];
rv[ 0] = KeyVal_.new_("isLocal" , true); // title.isLocal; NOTE: always true; passing "http:" also returns true; not sure how to handle "Interwiki::fetch( $this->mInterwiki )->isLocal()"
rv[ 1] = KeyVal_.new_("interwiki" , xwiki_str); // $title->getInterwiki(),
rv[ 2] = KeyVal_.new_("namespace" , ns.Id()); // $ns,
rv[ 3] = KeyVal_.new_("nsText" , ns.Name_str()); // $title->getNsText(),
rv[ 4] = KeyVal_.new_("text" , ttl.Page_txt()); // $title->getText(),
rv[ 5] = KeyVal_.new_("fragment" , ttl.Anch_txt()); // $title->getFragment(),
rv[ 6] = KeyVal_.new_("thePartialUrl" , ttl.Page_db()); // $title->getPartialUrl(),
if (ns_special)
rv[rv_idx++] = KeyVal_.new_("exists" , false); // TODO: lookup specials
if (!ns_file_or_media)
rv[rv_idx++] = KeyVal_.new_("file" , false); // REF.MW: if ( $ns !== NS_FILE && $ns !== NS_MEDIA ) $ret['file'] = false;
return rv;
} private static final Xowd_page_itm tmp_db_page = Xowd_page_itm.new_tmp();
public static final String Key_wikitexet = "wikitext";
}

View File

@@ -0,0 +1,168 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
import gplx.dbs.*; import gplx.xowa2.files.commons.*; import gplx.xowa.wikis.data.*;
import gplx.fsdb.*; import gplx.xowa.wikis.*; import gplx.xowa.files.*; import gplx.xowa.files.origs.*; import gplx.xowa.files.repos.*;
public class Scrib_lib_title_tst {
@Before public void init() {
Db_conn_bldr.I.Reg_default_mem();
fxt.Clear_for_lib();
fxt.Core().Wiki().File__fsdb_mode().Tid_v2_bld_y_();
lib = fxt.Core().Lib_title().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void NewTitle() {
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_newTitle, Object_.Ary("Page_0") , ttl_fast(0 , "", "Page 0", "", "", "Page_0"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_newTitle, Object_.Ary("A", "Template") , ttl_fast(10 , "Template", "A"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_newTitle, Object_.Ary("a[b") , Scrib_invoke_func_fxt.Null_rslt_ary); // invalid
}
@Test public void GetUrl() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getUrl, Object_.Ary("Main_Page", "fullUrl") , "//en.wikipedia.org/wiki/Main_Page");
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getUrl, Object_.Ary("Main_Page", "fullUrl", "action=edit") , "//en.wikipedia.org/wiki/Main_Page?action=edit");
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getUrl, Object_.Ary("Main_Page", "localUrl") , "/wiki/Main_Page");
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getUrl, Object_.Ary("Main_Page", "canonicalUrl") , "http://en.wikipedia.org/wiki/Main_Page");
// fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getUrl, Object_.Ary("Main_Page", "fullUrl", "", "http") , "http://en.wikipedia.org/wiki/Main_Page"); // TODO
}
@Test public void GetUrl__args_many() { // PUPROSE: GetUrl sometimes passes in kvs for qry_args; fr.w:Wikip<69>dia:Image_du_jour/Date; DATE:2013-12-24
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getUrl, Object_.Ary("Main_Page", "canonicalUrl", KeyVal_.Ary(KeyVal_.new_("action", "edit"), KeyVal_.new_("preload", "b"))), "http://en.wikipedia.org/wiki/Main_Page?action=edit&preload=b");
}
@Test public void MakeTitle() {
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_makeTitle, Object_.Ary("Module", "A") , ttl_fast(828, "Module", "A"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_makeTitle, Object_.Ary(828, "A") , ttl_fast(828, "Module", "A"));
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_makeTitle, Object_.Ary("Template", "A", "b") , ttl_fast(10, "Template", "A", "b"));
fxt.Parser_fxt().Wiki().Xwiki_mgr().Add_full("fr", "fr.wikipedia.org");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_makeTitle, Object_.Ary("Template", "A", "b", "fr") , ttl_fast(0, "", "Template:A", "b", "fr"));
fxt.Parser_fxt().Init_log_(Xop_ttl_log.Invalid_char);
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_makeTitle, Object_.Ary("Template", "a[b"), Scrib_invoke_func_fxt.Null_rslt_ary); // PURPOSE: handle bad MakeTitle cmds; PAGE:en.w:Disney; DATE:2013-10-15
}
@Test public void GetExpensiveData_absent() {
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getExpensiveData, Object_.Ary("A") , ttl_slow(Bool_.N, 0, Bool_.N));
}
@Test public void GetExpensiveData_exists() {
fxt.Parser_fxt().Init_page_create("A");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getExpensiveData, Object_.Ary("A") , ttl_slow(Bool_.Y, 0, Bool_.N));
}
@Test public void GetFileInfo() {
Wiki_orig_tbl__create(fxt.Core().Wiki());
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getFileInfo, Object_.Ary("A") , file_info_absent());
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getFileInfo, Object_.Ary("Template:A") , file_info_absent());
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getFileInfo, Object_.Ary("File:A.png") , file_info_absent());
fxt.Parser_fxt().Init_page_create("File:A.png");
Wiki_orig_tbl__insert(fxt.Core().Wiki(), "A.png", 220, 200);
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getFileInfo, Object_.Ary("File:A.png") , file_info_exists("A.png", 220, 200));
}
@Test public void GetFileInfo_commons() { // PURPOSE: check that Scribunto GetFileInfo calls filepath.FileExists; DATE:2014-01-07
Xowe_wiki commons_wiki = fxt.Parser_fxt().Wiki().Appe().Wiki_mgr().Get_by_key_or_make(Xow_domain_.Domain_bry_commons).Init_assert();
Wiki_orig_tbl__create(fxt.Core().Wiki());
Wiki_orig_tbl__insert(fxt.Core().Wiki(), "A.png", 220, 200);
fxt.Parser_fxt().Init_page_create(commons_wiki, "File:A.png", "text_is_blank");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getFileInfo, Object_.Ary("File:A.png") , file_info_exists("A.png", 220, 200));
}
@Test public void GetFileInfo_media() { // PURPOSE: [[Media:]] ns should find entries in [[File:]]; DATE:2014-01-07
Wiki_orig_tbl__create(fxt.Core().Wiki());
Wiki_orig_tbl__insert(fxt.Core().Wiki(), "A.png", 220, 200);
fxt.Parser_fxt().Init_page_create("File:A.png");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_title.Invk_getFileInfo, Object_.Ary("Media:A.png") , file_info_exists("A.png", 220, 200));
}
@Test public void GetContent() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getContent, Object_.Ary("A") , Scrib_invoke_func_fxt.Null_rslt);
fxt.Parser_fxt().Init_page_create("A", "test");
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getContent, Object_.Ary("A") , "test");
}
@Test public void GetContent_redirect() {// PURPOSE: GetContent should return source text for redirect, not target; PAGE:de.w:Wikipedia:Autorenportal DATE:2014-07-11
fxt.Parser_fxt().Init_page_create("A", "#REDIRECT [[B]]");
fxt.Parser_fxt().Init_page_create("B", "C");
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_getContent, Object_.Ary("A") , "#REDIRECT [[B]]"); // should not be "C"
}
@Test public void ProtectionLevels() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_title.Invk_protectionLevels, Object_.Ary("A") , "");
}
@Test public void CascadingProtection() {
fxt.Test_scrib_proc_obj(lib, Scrib_lib_title.Invk_cascadingProtection, Object_.Ary("A") , Scrib_lib_title.CascadingProtection_rv);
}
private static void Wiki_orig_tbl__create(Xowe_wiki wiki) {
Xowe_wiki_bldr.Create(wiki, 1, "dump.xml");
Xowd_db_file text_db = wiki.Data__core_mgr().Dbs__make_by_tid(Xowd_db_file_.Tid_text); text_db.Tbl__text().Create_tbl();
Fsdb_db_mgr__v2_bldr.I.Get_or_make(wiki, Bool_.Y);
wiki.File_mgr().Init_file_mgr_by_load(wiki);
}
private static void Wiki_orig_tbl__insert(Xowe_wiki wiki, String ttl_str, int w, int h) {
byte[] ttl_bry = Bry_.new_u8(ttl_str);
wiki.File__orig_mgr().Insert(Xof_repo_itm_.Repo_remote, ttl_bry, Xof_ext_.new_by_ttl_(ttl_bry).Id(), w, h, Bry_.Empty);
}
// private static void Init_page_regy(Xowe_wiki wiki, String ttl, int id, boolean is_redirect) {
// String url_str = "test/en.wikipedia.org/wiki_page_regy";
// Db_meta_tbl meta = new Xowd_page_tbl().new_meta();
// Db_conn_pool.I.Set_mem(url_str, meta);
// Db_conn_info url = Db_conn_info_.mem_(url_str);
// Xowd_page_tbl tbl = new Xowd_page_tbl(Bool_.N, url);
// tbl.Insert(id, ns_id, Bry_.new_u8(ttl), is_redirect, modified_on, page_len, random_int, text_db_id, html_db_id);
// }
private static String ttl_fast(int ns_id, String ns_str, String ttl) {return ttl_fast(ns_id, ns_str, ttl, "", "", ttl);}
private static String ttl_fast(int ns_id, String ns_str, String ttl, String anchor) {return ttl_fast(ns_id, ns_str, ttl, anchor, "", ttl);}
private static String ttl_fast(int ns_id, String ns_str, String ttl, String anchor, String xwiki) {return ttl_fast(ns_id, ns_str, ttl, anchor, xwiki, ttl);}
private static String ttl_fast(int ns_id, String ns_str, String ttl, String anchor, String xwiki, String partial_url) {
return String_.Concat_lines_nl_skip_last
( "1="
, " isLocal=true"
, " interwiki=" + xwiki
, " namespace=" + Int_.Xto_str(ns_id)
, " nsText=" + ns_str
, " text=" + ttl
, " fragment=" + anchor
, " thePartialUrl=" + partial_url
, " file=false"
);
}
private static String ttl_slow(boolean exists, int ttl_id, boolean redirect) {
return String_.Concat_lines_nl_skip_last
( "1="
, " isRedirect=" + Bool_.Xto_str_lower(redirect)
, " id=" + Int_.Xto_str(ttl_id)
, " contentModel=" + Scrib_lib_title.Key_wikitexet
, " exists=" + Bool_.Xto_str_lower(exists)
);
}
private static String file_info_absent() {
return String_.Concat_lines_nl_skip_last
( "1="
, " exists=false"
);
}
private static String file_info_exists(String ttl, int w, int h) {
return String_.Concat_lines_nl_skip_last
( "1="
, " exists=true"
, " width=" + Int_.Xto_str(w)
, " height=" + Int_.Xto_str(h)
, " pages=<<NULL>>"
);
}
}
//0000: '' != '1=isLocal=True'
//0001: ' true;false;;828;Module;A;0;;wikitext;A;false;false' != 'isRedirect=False'
//0002: <<N/A>> != 'interwiki='
//0003: <<N/A>> != 'namespace=828'
//0004: <<N/A>> != 'nsText=Module'
//0005: <<N/A>> != 'text=A'
//0006: <<N/A>> != 'id=0'
//0007: <<N/A>> != 'fragment='
//0008: <<N/A>> != 'contentModel=wikitext'
//0009: <<N/A>> != 'thePartialUrl=A'
//0010: <<N/A>> != 'exists=False'
//0011: <<N/A>> != 'fileExists=False'

View File

@@ -0,0 +1,76 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.xtns.pfuncs.ttls.*;
public class Scrib_lib_uri implements Scrib_lib {
public Scrib_lib_uri(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.uri.lua")); // NOTE: defaultUrl handled by Init_lib_url
notify_page_changed_fnc = mod.Fncs_get_by_key("notify_page_changed");
return mod;
} private Scrib_lua_proc notify_page_changed_fnc;
public void Notify_page_changed() {if (notify_page_changed_fnc != null) core.Interpreter().CallFunction(notify_page_changed_fnc.Id(), KeyVal_.Ary_empty);}
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_anchorEncode: return AnchorEncode(args, rslt);
case Proc_localUrl: return Url_func(args, rslt, Pfunc_urlfunc.Tid_local);
case Proc_fullUrl: return Url_func(args, rslt, Pfunc_urlfunc.Tid_full);
case Proc_canonicalUrl: return Url_func(args, rslt, Pfunc_urlfunc.Tid_canonical);
case Proc_init_uri_for_page: return Init_uri_for_page(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_anchorEncode = 0, Proc_localUrl = 1, Proc_fullUrl = 2, Proc_canonicalUrl = 3, Proc_init_uri_for_page = 4;
public static final String Invk_anchorEncode = "anchorEncode", Invk_localUrl = "localUrl", Invk_fullUrl = "fullUrl", Invk_canonicalUrl = "canonicalUrl", Invk_init_uri_for_page = "init_uri_for_page";
private static final String[] Proc_names = String_.Ary(Invk_anchorEncode, Invk_localUrl, Invk_fullUrl, Invk_canonicalUrl, Invk_init_uri_for_page);
public boolean AnchorEncode(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] raw_bry = args.Pull_bry(0);
Bry_bfr bfr = core.App().Utl__bfr_mkr().Get_b512();
Bry_bfr tmp_bfr = core.App().Utl__bfr_mkr().Get_b512();
Pfunc_anchorencode.Func_init(core.Ctx());
Pfunc_anchorencode.Anchor_encode(raw_bry, bfr, tmp_bfr);
tmp_bfr.Clear_and_rls();
return rslt.Init_obj(bfr.To_str_and_rls());
}
public boolean Url_func(Scrib_proc_args args, Scrib_proc_rslt rslt, byte url_tid) {
Xowe_wiki wiki = core.Wiki();
byte[] ttl_bry = args.Pull_bry(0);
byte[] qry_bry = args.Extract_qry_args(wiki, 1);
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry);
if (ttl == null) return rslt.Init_null();
Bry_bfr bfr = core.App().Utl__bfr_mkr().Get_b512();
if (ttl.Ns().Id() == Xow_ns_.Id_media) { // change "Media:" -> "File:"
bfr.Add(wiki.Ns_mgr().Ns_file().Name_db_w_colon());
bfr.Add(ttl.Page_db());
ttl_bry = bfr.Xto_bry_and_clear();
}
Pfunc_urlfunc.UrlString(core.Ctx(), url_tid, false, ttl_bry, bfr, qry_bry);
return rslt.Init_obj(bfr.To_str_and_rls());
}
private boolean Init_uri_for_page(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Xop_ctx ctx = core.Ctx();
byte[] ttl_bry = ctx.Cur_page().Ttl().Raw();
Bry_bfr tmp_bfr = ctx.Wiki().Utl__bfr_mkr().Get_b512();
Pfunc_urlfunc.UrlString(ctx, Pfunc_urlfunc.Tid_full, false, ttl_bry, tmp_bfr, Bry_.Empty);
return rslt.Init_obj(tmp_bfr.To_bry_and_rls());
}
}

View File

@@ -0,0 +1,43 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_uri_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_uri().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void Url() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_localUrl , Object_.Ary("a&b! c" ), "/wiki/A%26b!_c");
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_fullUrl , Object_.Ary("a&b! c" ), "//en.wikipedia.org/wiki/A%26b!_c");
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_canonicalUrl , Object_.Ary("a&b! c" ), "http://en.wikipedia.org/wiki/A%26b!_c");
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_localUrl , Object_.Ary("a&b! c" , "action=edit" ), "/wiki/A%26b!_c?action=edit");
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_localUrl , Object_.Ary("Media:A.png" ), "/wiki/File:A.png");
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_localUrl , Object_.Ary("[bad]" ), Scrib_invoke_func_fxt.Null_rslt); // handle invalid titles; EX:it.w:Billy_the_Kid; DATE:2014-01-20
}
@Test public void Url__args_many() { // PUPROSE: GetUrl sometimes passes in kvs for qry_args; it.w:Astronomie; DATE:2014-01-18
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_fullUrl, Object_.Ary("A", KeyVal_.Ary(KeyVal_.new_("action", "edit"))), "//en.wikipedia.org/wiki/A?action=edit");
}
@Test public void AnchorEncode() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_anchorEncode , Object_.Ary("[irc://a b c]" ), "b_c");
}
@Test public void Init_uri_for_page() {
fxt.Parser_fxt().Page_ttl_("Page_1");
fxt.Test_scrib_proc_str(lib, Scrib_lib_uri.Invk_init_uri_for_page , Object_.Ary_empty , "//en.wikipedia.org/wiki/Page_1");
}
}

View File

@@ -0,0 +1,323 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.texts.*; import gplx.intl.*;
public class Scrib_lib_ustring implements Scrib_lib {
private final String_surrogate_utl surrogate_utl = new String_surrogate_utl();
public Scrib_lib_ustring(Scrib_core core) {this.core = core; gsub_mgr = new Scrib_lib_ustring_gsub_mgr(core, regx_converter);} private Scrib_core core; Scrib_lib_ustring_gsub_mgr gsub_mgr;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public int String_len_max() {return string_len_max;} public Scrib_lib_ustring String_len_max_(int v) {string_len_max = v; return this;} private int string_len_max = Xoa_page_.Page_len_max;
public int Pattern_len_max() {return pattern_len_max;} public Scrib_lib_ustring Pattern_len_max_(int v) {pattern_len_max = v; return this;} private int pattern_len_max = 10000;
private Scrib_regx_converter regx_converter = new Scrib_regx_converter();
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.ustring.lua")
, KeyVal_.new_("stringLengthLimit", string_len_max)
, KeyVal_.new_("patternLengthLimit", pattern_len_max)
);
return mod;
}
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_find: return Find(args, rslt);
case Proc_match: return Match(args, rslt);
case Proc_gmatch_init: return Gmatch_init(args, rslt);
case Proc_gmatch_callback: return Gmatch_callback(args, rslt);
case Proc_gsub: return Gsub(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_find = 0, Proc_match = 1, Proc_gmatch_init = 2, Proc_gmatch_callback = 3, Proc_gsub = 4;
public static final String Invk_find = "find", Invk_match = "match", Invk_gmatch_init = "gmatch_init", Invk_gmatch_callback = "gmatch_callback", Invk_gsub = "gsub";
private static final String[] Proc_names = String_.Ary(Invk_find, Invk_match, Invk_gmatch_init, Invk_gmatch_callback, Invk_gsub);
public boolean Find(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String text_str = args.Pull_str(0);
String regx = args.Pull_str(1);
int bgn_char_idx = args.Cast_int_or(2, 1);
boolean plain = args.Cast_bool_or_n(3);
synchronized (surrogate_utl) {
byte[] text_bry = Bry_.new_u8(text_str); int text_bry_len = text_bry.length;
bgn_char_idx = Bgn_adjust(text_str, bgn_char_idx);
int bgn_adj = surrogate_utl.Count_surrogates__char_idx(text_bry, text_bry_len, 0, bgn_char_idx); // NOTE: convert from lua / php charidx to java regex codepoint; PAGE:zh.w:南北鐵路 (越南) DATE:2014-08-27
int bgn_codepoint_idx = bgn_char_idx + bgn_adj;
int bgn_byte_pos = surrogate_utl.Byte_pos();
if (String_.Len_eq_0(regx)) // regx of "" should return (bgn, bgn - 1) regardless of whether plain is true or false
return rslt.Init_many_objs(bgn_codepoint_idx + Scrib_lib_ustring.Base1, bgn_codepoint_idx + Scrib_lib_ustring.Base1 - 1);
if (plain) {
int pos = String_.FindFwd(text_str, regx, bgn_codepoint_idx);
boolean found = pos != Bry_.NotFound;
return found
? rslt.Init_many_objs(pos + Scrib_lib_ustring.Base1, pos + Scrib_lib_ustring.Base1 + String_.Len(regx) - Scrib_lib_ustring.End_adj)
: rslt.Init_ary_empty()
;
}
regx = regx_converter.Parse(Bry_.new_u8(regx), Scrib_regx_converter.Anchor_G);
RegxAdp regx_adp = Scrib_lib_ustring.RegxAdp_new_(core.Ctx(), regx);
RegxMatch[] regx_rslts = regx_adp.Match_all(text_str, bgn_codepoint_idx); // NOTE: MW calculates an offset to handle mb strings. however, java's regex always takes offset in chars (not bytes like PHP preg_match); DATE:2014-03-04
int len = regx_rslts.length;
if (len == 0) return rslt.Init_ary_empty();
List_adp tmp_list = List_adp_.new_();
RegxMatch match = regx_rslts[0]; // NOTE: take only 1st result; DATE:2014-08-27
int match_find_bgn_codepoint = match.Find_bgn(); // NOTE: java regex returns results in codepoint; PAGE:zh.w:南北鐵路 (越南) DATE:2014-08-27
int match_find_bgn_adj = -surrogate_utl.Count_surrogates__codepoint_idx1(text_bry, text_bry_len, bgn_byte_pos, match_find_bgn_codepoint - bgn_codepoint_idx); // NOTE: convert from java regex codepoint to lua / php char_idx; PAGE:zh.w:南北鐵路 (越南) DATE:2014-08-27
tmp_list.Add(match_find_bgn_codepoint + match_find_bgn_adj + -bgn_adj + Scrib_lib_ustring.Base1);
tmp_list.Add(match.Find_end() + match_find_bgn_adj + -bgn_adj + Scrib_lib_ustring.Base1 - Scrib_lib_ustring.End_adj);
//Tfds.Write(match_find_bgn_codepoint + match_find_bgn_adj + -bgn_adj + Scrib_lib_ustring.Base1
// ,match.Find_end() + match_find_bgn_adj + -bgn_adj + Scrib_lib_ustring.Base1 - Scrib_lib_ustring.End_adj);
AddCapturesFromMatch(tmp_list, match, text_str, regx_converter.Capt_ary(), false);
return rslt.Init_many_list(tmp_list);
}
}
private int Bgn_adjust(String text, int bgn) { // adjust to handle bgn < 0 or bgn > len (which PHP allows)
if (bgn > 0) bgn -= Scrib_lib_ustring.Base1;
int text_len = String_.Len(text);
if (bgn < 0) // negative number means search from rear of String
bgn += text_len; // NOTE: PHP has extra + 1 for Base 1
else if (bgn > text_len) // bgn > text_len; confine to text_len; NOTE: PHP has extra + 1 for Base 1
bgn = text_len; // NOTE: PHP has extra + 1 for Base 1
return bgn;
}
public boolean Match(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String text = args.Xstr_str_or_null(0); // Module can pass raw ints; PAGE:en.w:Budget_of_the_European_Union; DATE:2015-01-22
if (text == null) return rslt.Init_many_list(List_adp_.Noop); // if no text is passed, do not fail; return empty; EX:d:changed; DATE:2014-02-06
String regx = regx_converter.Parse(args.Cast_bry_or_null(1), Scrib_regx_converter.Anchor_G);
int bgn = args.Cast_int_or(2, 1);
bgn = Bgn_adjust(text, bgn);
RegxAdp regx_adp = Scrib_lib_ustring.RegxAdp_new_(core.Ctx(), regx);
RegxMatch[] regx_rslts = regx_adp.Match_all(text, bgn);
int len = regx_rslts.length;
if (len == 0) return rslt.Init_null(); // return null if no matches found; EX:w:Mount_Gambier_(volcano); DATE:2014-04-02; confirmed with en.d:民; DATE:2015-01-30
List_adp tmp_list = List_adp_.new_();
for (int i = 0; i < len; i++) {
RegxMatch match = regx_rslts[i];
AddCapturesFromMatch(tmp_list, match, text, regx_converter.Capt_ary(), true);
}
return rslt.Init_many_list(tmp_list);
}
public boolean Gsub(Scrib_proc_args args, Scrib_proc_rslt rslt) {return gsub_mgr.Exec(args, rslt);}
public boolean Gmatch_init(Scrib_proc_args args, Scrib_proc_rslt rslt) {
// String text = Scrib_kv_utl_.Val_to_str(values, 0);
byte[] regx = args.Pull_bry(1);
String pcre = regx_converter.Parse(regx, Scrib_regx_converter.Anchor_null);
return rslt.Init_many_objs(pcre, regx_converter.Capt_ary());
}
public boolean Gmatch_callback(Scrib_proc_args args, Scrib_proc_rslt rslt) {
String text = args.Xstr_str_or_null(0); // NOTE: UstringLibrary.php!ustringGmatchCallback calls preg_match directly; $s can be any type, and php casts automatically;
String regx = args.Pull_str(1);
KeyVal[] capt = args.Cast_kv_ary_or_null(2);
int pos = args.Pull_int(3);
RegxAdp regx_adp = Scrib_lib_ustring.RegxAdp_new_(core.Ctx(), regx);
RegxMatch[] regx_rslts = regx_adp.Match_all(text, pos);
int len = regx_rslts.length;
if (len == 0) return rslt.Init_many_objs(pos, KeyVal_.Ary_empty);
RegxMatch match = regx_rslts[0]; // NOTE: take only 1st result
List_adp tmp_list = List_adp_.new_();
AddCapturesFromMatch(tmp_list, match, text, capt, true); // NOTE: was incorrectly set as false; DATE:2014-04-23
return rslt.Init_many_objs(match.Find_end(), Scrib_kv_utl_.base1_list_(tmp_list));
}
private void AddCapturesFromMatch(List_adp tmp_list, RegxMatch rslt, String text, KeyVal[] capts, boolean op_is_match) {// NOTE: this matches behavior in UstringLibrary.php!addCapturesFromMatch
RegxGroup[] grps = rslt.Groups();
int grps_len = grps.length;
int capts_len = capts == null ? 0 : capts.length;
if (grps_len > 0) {
for (int j = 0; j < grps_len; j++) {
RegxGroup grp = grps[j];
if ( j < capts_len // bounds check b/c null can be passed
&& Bool_.cast_(capts[j].Val()) // check if true; indicates that group is "()" or "anypos" see regex converter; DATE:2014-04-23
)
tmp_list.Add(Int_.Xto_str(grp.Bgn() + Scrib_lib_ustring.Base1)); // return index only for (); NOTE: always return as String; callers expect String, and may do operations like len(result), which will fail if int; DATE:2013-12-20
else
tmp_list.Add(grp.Val()); // return match
}
}
else if ( op_is_match // if op_is_match, and no captures, extract find_txt; note that UstringLibrary.php says "$arr[] = $m[0][0];" which means get the 1st match;
&& tmp_list.Count() == 0) // only add match once; EX: "aaaa", "a" will have four matches; get 1st; DATE:2014-04-02
tmp_list.Add(String_.Mid(text, rslt.Find_bgn(), rslt.Find_end()));
}
public static RegxAdp RegxAdp_new_(Xop_ctx ctx, String regx) {
RegxAdp rv = RegxAdp_.new_(regx);
if (rv.Pattern_is_invalid()) {
ctx.App().Usr_dlg().Warn_many("", "", "regx is invalid: regx=~{0} page=~{1}", regx, String_.new_u8(ctx.Cur_page().Ttl().Page_db()));
}
return rv;
}
private static final int Base1 = 1
, End_adj = 1; // lua / php uses "end" as <= not <; EX: "abc" and bgn=0, end= 1; for XOWA, this is "a"; for MW / PHP it is "ab"
}
class Scrib_lib_ustring_gsub_mgr {
private Scrib_regx_converter regx_converter;
public Scrib_lib_ustring_gsub_mgr(Scrib_core core, Scrib_regx_converter regx_converter) {this.core = core; this.regx_converter = regx_converter;} private Scrib_core core;
private byte tmp_repl_tid = Repl_tid_null; private byte[] tmp_repl_bry = null;
private Hash_adp repl_hash = null; private Scrib_lua_proc repl_func = null;
private int repl_count = 0;
public boolean Exec(Scrib_proc_args args, Scrib_proc_rslt rslt) {
Object text_obj = args.Cast_obj_or_null(0);
String text = String_.as_(text_obj);
if (text == null) text = Object_.Xto_str_strict_or_empty(text_obj);
String regx = args.Xstr_str_or_null(1); // NOTE: @pattern sometimes int; PAGE:en.d:λύω; DATE:2014-09-02
if (args.Len() == 2) return rslt.Init_obj(text); // if no replace arg, return self; PAGE:en.d:'orse; DATE:2013-10-13
Object repl_obj = args.Cast_obj_or_null(2);
regx = regx_converter.Parse(Bry_.new_u8(regx), Scrib_regx_converter.Anchor_pow);
int limit = args.Cast_int_or(3, -1);
repl_count = 0;
Identify_repl(repl_obj);
String repl = Exec_repl(tmp_repl_tid, tmp_repl_bry, text, regx, limit);
return rslt.Init_many_objs(repl, repl_count);
}
private void Identify_repl(Object repl_obj) {
Class<?> repl_type = repl_obj.getClass();
if (Object_.Eq(repl_type, String_.Cls_ref_type)) {
tmp_repl_tid = Repl_tid_string;
tmp_repl_bry = Bry_.new_u8((String)repl_obj);
}
else if (Object_.Eq(repl_type, Int_.Cls_ref_type)) { // NOTE:@replace sometimes int; PAGE:en.d:λύω; DATE:2014-09-02
tmp_repl_tid = Repl_tid_string;
tmp_repl_bry = Bry_.new_u8(Int_.Xto_str(Int_.cast_(repl_obj)));
}
else if (Object_.Eq(repl_type, KeyVal[].class)) {
tmp_repl_tid = Repl_tid_table;
KeyVal[] repl_tbl = (KeyVal[])repl_obj;
if (repl_hash == null)
repl_hash = Hash_adp_.new_();
else
repl_hash.Clear();
int repl_tbl_len = repl_tbl.length;
for (int i = 0; i < repl_tbl_len; i++) {
KeyVal repl_itm = repl_tbl[i];
String repl_itm_val = repl_itm.Val_to_str_or_empty();
repl_hash.Add(repl_itm.Key(), Bry_.new_u8(repl_itm_val));
}
}
else if (Object_.Eq(repl_type, Scrib_lua_proc.class)) {
tmp_repl_tid = Repl_tid_luacbk;
repl_func = (Scrib_lua_proc)repl_obj;
}
else throw Exc_.new_unhandled(ClassAdp_.NameOf_type(repl_type));
}
private String Exec_repl(byte repl_tid, byte[] repl_bry, String text, String regx, int limit) {
RegxAdp regx_mgr = Scrib_lib_ustring.RegxAdp_new_(core.Ctx(), regx);
RegxMatch[] rslts = regx_mgr.Match_all(text, 0);
if ( rslts.length == 0 // PHP: If matches are found, the new subject will be returned, otherwise subject will be returned unchanged.; http://php.net/manual/en/function.preg-replace-callback.php
|| regx_mgr.Pattern_is_invalid() // NOTE: invalid patterns should return self; EX:[^]; DATE:2014-09-02
) return text;
Bry_bfr tmp_bfr = Bry_bfr.new_();
int len = rslts.length;
int pos = 0;
for (int i = 0; i < len; i++) {
if (limit > -1 && repl_count == limit) break;
RegxMatch rslt = rslts[i];
tmp_bfr.Add_str(String_.Mid(text, pos, rslt.Find_bgn())); // NOTE: regx returns char pos (not bry); must add as String, not bry; DATE:2013-07-17
Exec_repl_itm(tmp_bfr, repl_tid, repl_bry, text, rslt);
pos = rslt.Find_end();
++repl_count;
}
int text_len = String_.Len(text);
if (pos < text_len)
tmp_bfr.Add_str(String_.Mid(text, pos, text_len)); // NOTE: regx returns char pos (not bry); must add as String, not bry; DATE:2013-07-17
return tmp_bfr.Xto_str_and_clear();
}
private void Exec_repl_itm(Bry_bfr tmp_bfr, byte repl_tid, byte[] repl_bry, String text, RegxMatch match) {
switch (repl_tid) {
case Repl_tid_string:
int len = repl_bry.length;
for (int i = 0; i < len; i++) {
byte b = repl_bry[i];
switch (b) {
case Byte_ascii.Percent: {
++i;
if (i == len) // % at end of stream; just add %;
tmp_bfr.Add_byte(b);
else {
b = repl_bry[i];
switch (b) {
case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4:
case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9:
int idx = b - Byte_ascii.Num_0 - List_adp_.Base1;
if (idx < match.Groups().length) { // retrieve numbered capture; TODO: support more than 9 captures
RegxGroup grp = match.Groups()[idx];
tmp_bfr.Add_str(String_.Mid(text, grp.Bgn(), grp.End())); // NOTE: grp.Bgn() / .End() is for String pos (bry pos will fail for utf8 strings)
}
else {
tmp_bfr.Add_byte(Byte_ascii.Percent);
tmp_bfr.Add_byte(b);
}
break;
case Byte_ascii.Percent:
tmp_bfr.Add_byte(Byte_ascii.Percent);
break;
default: // not a number; add literal
tmp_bfr.Add_byte(Byte_ascii.Percent);
tmp_bfr.Add_byte(b);
break;
}
}
break;
}
default:
tmp_bfr.Add_byte(b);
break;
}
}
break;
case Repl_tid_table: {
int match_bgn = -1, match_end = -1;
RegxGroup[] grps = match.Groups();
if (grps.length == 0) {
match_bgn = match.Find_bgn();
match_end = match.Find_end();
}
else { // group exists, take first one (logic matches Scribunto); PAGE:en.w:Bannered_routes_of_U.S._Route_60; DATE:2014-08-15
RegxGroup grp = grps[0];
match_bgn = grp.Bgn();
match_end = grp.End();
}
String find_str = String_.Mid(text, match_bgn, match_end); // NOTE: rslt.Bgn() / .End() is for String pos (bry pos will fail for utf8 strings)
Object actl_repl_obj = repl_hash.Get_by(find_str);
if (actl_repl_obj == null) // match found, but no replacement specified; EX:"abc", "[ab]", "a:A"; "b" in regex but not in tbl; EX:d:DVD; DATE:2014-03-31
tmp_bfr.Add_str(find_str);
else
tmp_bfr.Add((byte[])actl_repl_obj);
break;
}
case Repl_tid_luacbk: {
KeyVal[] luacbk_args = null;
RegxGroup[] grps = match.Groups();
int grps_len = grps.length;
if (grps_len == 0) { // no match; use original String
String find_str = String_.Mid(text, match.Find_bgn(), match.Find_end());
luacbk_args = Scrib_kv_utl_.base1_obj_(find_str);
}
else { // match; build ary of matches; (see UStringLibrary.php)
luacbk_args = new KeyVal[grps_len];
for (int i = 0; i < grps_len; i++) {
RegxGroup grp = grps[i];
String find_str = String_.Mid(text, grp.Bgn(), grp.End());
luacbk_args[i] = KeyVal_.int_(i + Scrib_core.Base_1, find_str);
}
}
KeyVal[] rslts = core.Interpreter().CallFunction(repl_func.Id(), luacbk_args);
tmp_bfr.Add_str(Scrib_kv_utl_.Val_to_str(rslts, 0));
break;
}
default: throw Exc_.new_unhandled(repl_tid);
}
}
static final byte Repl_tid_null = 0, Repl_tid_string = 1, Repl_tid_table = 2, Repl_tid_luacbk = 3;
}

View File

@@ -0,0 +1,71 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_ustring__invoke_tst {
@Before public void init() {
fxt.Clear_for_invoke();
fxt.Init_page("{{#invoke:Mod_0|Func_0}}");
fxt.Core().Lib_ustring().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt();
@Test public void Gsub_proc() {
fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_ustring(), Scrib_lib_ustring.Invk_gsub);
Exec_gsub_regx_func_0("abcd", "([a])", "Abcd;1");
}
@Test public void Gsub_proc_w_grouped() { // PURPOSE: gsub_proc should pass matched String, not entire String; DATE:2013-12-01
fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_ustring(), Scrib_lib_ustring.Invk_gsub);
Exec_gsub_regx_func_1("[[a]]", "%[%[([^#|%]]-)%]%]" , "A;1");
fxt.Test_log_rcvd(3, "000000370000006D{[\"op\"]=\"call\",[\"id\"]=1,[\"nargs\"]=1,[\"args\"]={[1]=\"a\"}}"); // should be "a", not "[[a]]"
}
@Test public void Gsub_proc_w_grouped_2() {// PURPOSE: gsub_proc failed when passing multiple matches; DATE:2013-12-01
fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_ustring(), Scrib_lib_ustring.Invk_gsub);
Exec_gsub_regx_func_2("[[a]] [[b]]", "%[%[([^#|%]]-)%]%]" , "A B;2");
fxt.Test_log_rcvd(3, "000000370000006D{[\"op\"]=\"call\",[\"id\"]=1,[\"nargs\"]=1,[\"args\"]={[1]=\"a\"}}"); // should be "a", not "[[a]]"
fxt.Test_log_rcvd(4, "000000370000006D{[\"op\"]=\"call\",[\"id\"]=1,[\"nargs\"]=1,[\"args\"]={[1]=\"b\"}}"); // should be "b", not "[[b]]"
}
@Test public void Gsub_int() { // PURPOSE: gsub with integer arg should not fail; DATE:2013-11-06
fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_ustring(), Scrib_lib_ustring.Invk_gsub);
fxt.Init_lua_module();
fxt.Init_lua_rcvd(Scrib_lib_ustring.Invk_gsub, Scrib_kv_utl_.base1_many_(1, "[1]", "2", 1)); // NOTE: text is integer (lua / php are type-less)
fxt.Init_lua_rcvd_rv();
fxt.Test_invoke("2;1");
}
private void Exec_gsub_regx_func_0(String text, String regx, String expd) {
fxt.Init_lua_module();
fxt.Init_lua_rcvd(Scrib_lib_ustring.Invk_gsub, Scrib_kv_utl_.base1_many_(text, regx, new Scrib_lua_proc("ignore_key", 1)));
fxt.Init_lua_rcvd_raw("a:3:{s:2:\"op\";s:6:\"return\";s:7:\"nvalues\";i:1;s:6:\"values\";a:1:{i:1;s:1:\"A\";}}");
fxt.Init_lua_rcvd_rv();
fxt.Test_invoke(expd);
}
private void Exec_gsub_regx_func_1(String text, String regx, String expd) {
fxt.Init_lua_module();
fxt.Init_lua_rcvd(Scrib_lib_ustring.Invk_gsub, Scrib_kv_utl_.base1_many_(text, regx, new Scrib_lua_proc("ignore_key", 1)));
fxt.Init_lua_rcvd_raw("a:3:{s:2:\"op\";s:6:\"return\";s:7:\"nvalues\";i:1;s:6:\"values\";a:1:{i:1;s:1:\"A\";}}");
fxt.Init_lua_rcvd_rv();
fxt.Test_invoke(expd);
}
private void Exec_gsub_regx_func_2(String text, String regx, String expd) {
fxt.Init_lua_module();
fxt.Init_lua_rcvd(Scrib_lib_ustring.Invk_gsub, Scrib_kv_utl_.base1_many_(text, regx, new Scrib_lua_proc("ignore_key", 1)));
fxt.Init_lua_rcvd_raw("a:3:{s:2:\"op\";s:6:\"return\";s:7:\"nvalues\";i:1;s:6:\"values\";a:1:{i:1;s:1:\"A\";}}");
fxt.Init_lua_rcvd_raw("a:3:{s:2:\"op\";s:6:\"return\";s:7:\"nvalues\";i:1;s:6:\"values\";a:1:{i:1;s:1:\"B\";}}");
fxt.Init_lua_rcvd_rv();
fxt.Init_lua_rcvd_rv();
fxt.Test_invoke(expd);
}
}

View File

@@ -0,0 +1,134 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
public class Scrib_lib_ustring__lib_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_ustring().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void Find() {
Exec_find("abcd" , "b" , 1, Bool_.N, "2;2"); // basic
Exec_find("abac" , "a" , 2, Bool_.N, "3;3"); // bgn
Exec_find("()()" , "(" , 2, Bool_.Y, "3;3"); // plain; note that ( would "break" regx
Exec_find("a bcd e" , "(b(c)d)" , 2, Bool_.N, "3;5;bcd;c"); // groups
Exec_find("a bcd e" , "()(b)" , 2, Bool_.N, "3;3;3;b"); // groups; empty capture
Exec_find("abcd" , "x" , 1, Bool_.N, ""); // empty
Exec_find("abcd" , "" , 2, Bool_.Y, "2;1"); // empty regx should return values; plain; EX:w:Fool's_mate; DATE:2014-03-04
Exec_find("abcd" , "" , 2, Bool_.N, "2;1"); // empty regx should return values; regx; EX:w:Fool's_mate; DATE:2014-03-04
Exec_find("abcd" , "^(c)" , 3, Bool_.N, "3;3;c"); // ^ should be converted to \G; regx; EX:cs.n:Category:1._září_2008; DATE:2014-05-07
}
@Test public void Find_surrogate() { // PURPOSE: handle surrogates in Find PAGE:zh.w:南北鐵路_(越南); DATE:2014-08-28
Exec_find("aé𡼾\nbî𡼾\n" , "\n" , 1, Bool_.N, "4;4"); // 4 b/c \n starts at pos 4 (super 1)
Exec_find("aé𡼾\nbî𡼾\n" , "\n" , 5, Bool_.N, "8;8"); // 8 b/c \n starts at pos 8 (super 1)
}
@Test public void Match() {
Exec_match("abcd" , "bc" , 1, "bc"); // basic
Exec_match("abcd" , "x" , 1, String_.Null_mark); // empty
Exec_match("abcd" , "a" , 2, String_.Null_mark); // bgn
Exec_match("abcd" , "b(c)" , 1, "c"); // group
Exec_match(" a b " , "^%s*(.-)%s*$" , 1, "a b;"); // trim; NOTE: changed from "a b" to "a b;"; DATE:2015-01-30
Exec_match("abcd" , "a" , 0, "a"); // handle 0; note that php/lua is super-1, but some modules pass in 0; ru.w:Module:Infocards; DATE:2013-11-08
Exec_match("abcd" , "." , -1, "d"); // -1
Exec_match("aaa" , "a" , 1, "a"); // should return 1st match not many
Exec_match("aaa" , "(a)" , 1, "a;a;a"); // should return all matches
Exec_match("a b" , "%S" , 1, "a"); // %S was returning every match instead of 1st; PAGE:en.w:Bertrand_Russell; DATE:2014-04-02
Exec_match(1 , "a" , 1, String_.Null_mark); // Module can pass raw ints; PAGE:en.w:Budget_of_the_European_Union; DATE:2015-01-22
Exec_match("" , "a?" , 1, ""); // no results with ? should return "" not nil; PAGE:en.d:民; DATE:2015-01-30
}
@Test public void Match_args_out_of_order() {
fxt.Test_scrib_proc_empty(lib, Scrib_lib_ustring.Invk_match, KeyVal_.Ary(KeyVal_.int_(2, "[a]")));
}
@Test public void Gsub() {
Exec_gsub_regx("abcd", "[a]" , -1, "A" , "Abcd;1");
Exec_gsub_regx("aaaa", "[a]" , 2, "A" , "AAaa;2");
Exec_gsub_regx("a" , "(a)" , 1, "%%%1" , "%a;1");
Exec_gsub_regx("à{b}c", "{b}" , 1, "b" , "àbc;1"); // utf8
Exec_gsub_regx("àbc", "^%s*(.-)%s*$", 1, "%1" , "àbc;1"); // utf8; regx is for trim line
Exec_gsub_regx("a" , "[^]" , 1, "b" , "a;0"); // invalid regx should not fail; should return self; DATE:2013-10-20
}
@Test public void Gsub_table() {
Exec_gsub_regx("abcd", "[ac]" , -1, Scrib_kv_utl_.flat_many_("a", "A", "c", "C") , "AbCd;2");
Exec_gsub_regx("abc" , "[ab]" , -1, Scrib_kv_utl_.flat_many_("a", "A") , "Abc;2"); // PURPOSE: match not in regex should still print itself; in this case [c] is not in tbl regex; DATE:2014-03-31
}
@Test public void Gsub_table_match() { // PURPOSE: replace using group, not found term; EX:"b" not "%b%" PAGE:en.w:Bannered_routes_of_U.S._Route_60; DATE:2014-08-15
Exec_gsub_regx("a%b%c", "%%(%w+)%%" , -1, Scrib_kv_utl_.flat_many_("b", "B") , "aBc;1");
}
@Test public void Gsub_capture() {
Exec_gsub_regx("aa" , "(a)%1" , 1, "%1z", "az;1"); // capture
Exec_gsub_regx("a\"b'c\"d" , "([\"'])(.-)%1" , 1, "%1z", "a\"zd;1"); // capture; http://www.lua.org/pil/20.3.html; {{#invoke:test|gsub_string|a"b'c"d|(["'])(.-)%1|%1z}}
}
@Test public void Gsub_no_replace() {// PURPOSE: gsub with no replace argument should not fail; EX:d:'orse; DATE:2013-10-14
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gsub, Object_.Ary("text", "regx") , "1=text"); // NOTE: repl, limit deliberately omitted
}
@Test public void Gsub_pattern_is_int() { // PURPOSE: do not fail if integer is passed in for @regx; PAGE:en.d:λύω DATE:2014-09-02
Exec_gsub_regx("abcd", 1 , -1, "A" , "abcd;0");
}
@Test public void Gsub_replace_is_int() { // PURPOSE: do not fail if integer is passed in for @replace; PAGE:en.d:λύω DATE:2014-09-02
Exec_gsub_regx("abcd", 1 , -1, 1 , "abcd;0");
}
@Test public void Gsub_word_class() { // PURPOSE: handle %w in extended regex; PAGE:en.w:A♯_(musical_note) DATE:2015-06-10
Exec_gsub_regx("(a b)", "[^%w%p%s]", -1, "x", "(a b);0"); // was returning "(x x)" b/c ^%w was incorrectly matching "a" and "b"
}
@Test public void Gmatch_init() {
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_init, Object_.Ary("abcabc", "a(b)") , "1=a(b)\n2=\n 1=false");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_init, Object_.Ary("abcabc", "a()(b)") , "1=a()(b)\n2=\n 1=true\n 2=false");
}
@Test public void Gmatch_callback() {
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_callback, Object_.Ary("abcabc", "a(b)", Scrib_kv_utl_.base1_many_(false), 0) , "1=2\n2=\n 1=b");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_callback, Object_.Ary("abcabc", "a(b)", Scrib_kv_utl_.base1_many_(false), 2) , "1=5\n2=\n 1=b");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_callback, Object_.Ary("abcabc", "a(b)", Scrib_kv_utl_.base1_many_(false), 8) , "1=8\n2=");
}
@Test public void Gmatch_callback_nomatch() {// PURPOSE.fix: was originally returning "" instead of original String; EX:vi.d:trở_thành; DATE:2014-04-23
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_callback, Object_.Ary("a", "a" , KeyVal_.Ary_empty, 0) , "1=1\n2=\n 1=a");
}
@Test public void Gmatch_callback_anypos() {// PURPOSE.fix: was not handling $capt argument; EX:vi.d:trở_thành; DATE:2014-04-23
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_callback, Object_.Ary("a bcd e", "()(b)" , Scrib_kv_utl_.base1_many_(true, false), 0), String_.Concat_lines_nl_skip_last
( "1=3"
, "2="
, " 1=3"
, " 2=b"
));
}
@Test public void Gsub_balanced_group() { // PURPOSE: handle balanced group regex; EX:"%b()"; NOTE:test will fail if run in 1.6 environment; DATE:2013-12-20
fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_ustring(), Scrib_lib_ustring.Invk_gsub);
Exec_gsub_regx("(a)", "%b()", 1, "c", "c;1");
}
@Test public void Gmatch_callback__text_as_number() { // PURPOSE: Gmatch_callback must be able to take non String value; DATE:2013-12-20
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_ustring.Invk_gmatch_callback, Object_.Ary(1234, "1(2)", Scrib_kv_utl_.base1_many_(false), 0), String_.Concat_lines_nl_skip_last
( "1=2"
, "2="
, " 1=2"
));
}
// @Test public void Match_viwiktionary() {
// fxt.Init_cbk(Scrib_core.Key_mw_interface, fxt.Core().Lib_ustring(), Scrib_lib_ustring.Invk_match);
// Exec_match("tr" , "()(r)", 1, ";"); // should return all matches
// Exec_match("tr" , "^([b]*).-([c]*)$", 1, ";"); // should return all matches
// }
private void Exec_find(String text, String regx, int bgn, boolean plain, String expd) {
fxt.Test_scrib_proc_kv_vals(lib, Scrib_lib_ustring.Invk_find, Scrib_kv_utl_.base1_many_(text, regx, bgn, plain), expd);
}
private void Exec_match(Object text, String regx, int bgn, String expd) {
fxt.Test_scrib_proc_kv_vals(lib, Scrib_lib_ustring.Invk_match, Scrib_kv_utl_.base1_many_(text, regx, bgn), expd);
}
private void Exec_gsub_regx(String text, Object regx, int limit, Object repl, String expd) {Exec_gsub(text, regx, limit, repl, expd);}
private void Exec_gsub(String text, Object regx, int limit, Object repl, String expd) {
fxt.Test_scrib_proc_kv_vals(lib, Scrib_lib_ustring.Invk_gsub, Scrib_kv_utl_.base1_many_(text, regx, repl, limit), expd);
}
}

View File

@@ -0,0 +1,64 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.json.*; import gplx.xowa.xtns.wdatas.*; import gplx.xowa.xtns.wdatas.parsers.*;
public class Scrib_lib_wikibase implements Scrib_lib {
public Scrib_lib_wikibase(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.wikibase.lua"));
notify_page_changed_fnc = mod.Fncs_get_by_key("notify_page_changed");
return mod;
} private Scrib_lua_proc notify_page_changed_fnc;
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_getEntity: return GetEntity(args, rslt);
case Proc_getEntityId: return GetEntityId(args, rslt);
case Proc_getGlobalSiteId: return GetGlobalSiteId(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_getEntity = 0, Proc_getEntityId = 1, Proc_getGlobalSiteId = 2;
public static final String Invk_getEntity = "getEntity", Invk_getEntityId = "getEntityId", Invk_getGlobalSiteId = "getGlobalSiteId";
private static final String[] Proc_names = String_.Ary(Invk_getEntity, Invk_getEntityId, Invk_getGlobalSiteId);
public void Notify_page_changed() {if (notify_page_changed_fnc != null) core.Interpreter().CallFunction(notify_page_changed_fnc.Id(), KeyVal_.Ary_empty);}
public boolean GetEntity(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ttl_bry = args.Pull_bry(0);
if (Bry_.Len_eq_0(ttl_bry)) return rslt.Init_ary_empty(); // NOTE: some Modules do not pass in an argument; return early, else spurious warning "invalid qid for ttl" (since ttl is blank); EX:w:Module:Authority_control; DATE:2013-10-27
boolean base_0 = args.Pull_bool(1);
Xowe_wiki wiki = core.Wiki();
Wdata_wiki_mgr wdata_mgr = wiki.Appe().Wiki_mgr().Wdata_mgr();
Wdata_doc wdoc = wdata_mgr.Pages_get_by_ttl_name(ttl_bry);
if (wdoc == null) {Wdata_wiki_mgr.Log_missing_qid(core.Ctx(), ttl_bry); return rslt.Init_ary_empty();}
return rslt.Init_obj(Scrib_lib_wikibase_srl.Srl(wdoc, true, base_0));
}
public boolean GetEntityId(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] ttl_bry = args.Pull_bry(0);
Xowe_wiki wiki = core.Wiki();
Wdata_wiki_mgr wdata_mgr = wiki.Appe().Wiki_mgr().Wdata_mgr();
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, ttl_bry);
byte[] rv = wdata_mgr.Qids_get(wiki, ttl); if (rv == null) rv = Bry_.Empty;
return rslt.Init_obj(rv);
}
public boolean GetGlobalSiteId(Scrib_proc_args args, Scrib_proc_rslt rslt) {
return rslt.Init_obj(core.Wiki().Domain_abrv()); // ;siteGlobalID: This site's global ID (e.g. <code>'itwiki'</code>), as used in the sites table. Default: <code>$wgDBname</code>.; REF:/xtns/Wikibase/docs/options.wiki
}
}

View File

@@ -0,0 +1,57 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.xtns.wdatas.*;
import gplx.json.*; import gplx.xowa.xtns.wdatas.core.*;
public class Scrib_lib_wikibase_entity implements Scrib_lib {
public Scrib_lib_wikibase_entity(Scrib_core core) {this.core = core;} private Scrib_core core;
public Scrib_lua_mod Mod() {return mod;} private Scrib_lua_mod mod;
public Scrib_lib Init() {procs.Init_by_lib(this, Proc_names); return this;}
public Scrib_lua_mod Register(Scrib_core core, Io_url script_dir) {
Init();
mod = core.RegisterInterface(this, script_dir.GenSubFil("mw.wikibase.entity.lua"));
return mod;
}
public Scrib_proc_mgr Procs() {return procs;} private Scrib_proc_mgr procs = new Scrib_proc_mgr();
public boolean Procs_exec(int key, Scrib_proc_args args, Scrib_proc_rslt rslt) {
switch (key) {
case Proc_getGlobalSiteId: return GetGlobalSiteId(args, rslt);
case Proc_formatPropertyValues: return FormatPropertyValues(args, rslt);
default: throw Exc_.new_unhandled(key);
}
}
private static final int Proc_getGlobalSiteId = 0, Proc_formatPropertyValues = 1;
public static final String Invk_getGlobalSiteId = "getGlobalSiteId", Invk_formatPropertyValues = "formatPropertyValues";
private static final String[] Proc_names = String_.Ary(Invk_getGlobalSiteId, Invk_formatPropertyValues);
public boolean GetGlobalSiteId(Scrib_proc_args args, Scrib_proc_rslt rslt) {
return rslt.Init_obj(core.Wiki().Domain_abrv()); // ;siteGlobalID: This site's global ID (e.g. <code>'itwiki'</code>), as used in the sites table. Default: <code>$wgDBname</code>.; REF:/xtns/Wikibase/docs/options.wiki
}
public boolean FormatPropertyValues(Scrib_proc_args args, Scrib_proc_rslt rslt) {
byte[] qid = args.Pull_bry(0);
byte[] pid = args.Pull_bry(1);
Xoae_app app = core.App(); Xowe_wiki wiki = core.Wiki();
Wdata_wiki_mgr wdata_mgr = app.Wiki_mgr().Wdata_mgr();
byte[] lang = wiki.Wdata_wiki_lang();
Wdata_doc wdoc = wdata_mgr.Pages_get(qid); if (wdoc == null) {Wdata_wiki_mgr.Log_missing_qid(core.Ctx(), qid); return rslt.Init_str_empty();} // NOTE: return empty String, not nil; PAGE:fr.s:Henri_Bergson; DATE:2014-08-13
int pid_int = wdata_mgr.Pids_get(lang, pid); if (pid_int == Wdata_wiki_mgr.Pid_null) return rslt.Init_str_empty();
Wdata_claim_grp prop_grp = wdoc.Claim_list_get(pid_int); if (prop_grp == null) return rslt.Init_str_empty();
Bry_bfr bfr = app.Utl__bfr_mkr().Get_b512();
wdata_mgr.Resolve_to_bfr(bfr, prop_grp, lang);
return rslt.Init_obj(bfr.To_bry_and_rls());
}
}

View File

@@ -0,0 +1,38 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
import gplx.xowa.xtns.wdatas.*;
public class Scrib_lib_wikibase_entity_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_wikibase_entity().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void GetGlobalSiteId() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_getGlobalSiteId, Object_.Ary_empty, "enwiki");
}
@Test public void FormatPropertyValues() {
Wdata_wiki_mgr_fxt wdata_fxt = new Wdata_wiki_mgr_fxt().Init(fxt.Parser_fxt(), false);
wdata_fxt.Init_pages_add(wdata_fxt.Wdoc_bldr("Q2").Add_claims(wdata_fxt.Make_claim_str(3, "P3_val")).Xto_wdoc());
wdata_fxt.Init_pids_add("en", "P3", 3);
fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_formatPropertyValues, Object_.Ary("Q2", "P3"), "P3_val");
}
@Test public void FormatPropertyValues__not_found() { // PURPOSE: should return "" not null; PAGE:fr.s:Auteur:Henri_Bergson; DATE:2014-08-13
fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase_entity.Invk_formatPropertyValues, Object_.Ary("Q2", "P3"), "");
}
}

View File

@@ -0,0 +1,164 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.xtns.wdatas.*; import gplx.xowa.xtns.wdatas.core.*; import gplx.xowa.xtns.wdatas.parsers.*;
class Scrib_lib_wikibase_srl {
public static KeyVal[] Srl(Wdata_doc wdoc, boolean header_enabled, boolean legacy_style) {// REF.MW:/Wikibase/lib/includes/serializers/EntitySerializer.php!getSerialized; http://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua
int base_adj = legacy_style ? 0 : 1;
List_adp rv = List_adp_.new_();
if (header_enabled) {
rv.Add(KeyVal_.new_("id", wdoc.Qid()));
rv.Add(KeyVal_.new_("type", Wdata_dict_value_entity_tid.Str_item));
rv.Add(KeyVal_.new_("schemaVersion", base_adj + 1)); // NOTE: needed by mw.wikibase.lua
}
Srl_root(rv, Wdata_doc_parser_v2.Str_labels , Srl_langtexts (Wdata_dict_langtext.Str_language , Wdata_dict_langtext.Str_value, wdoc.Label_list()));
Srl_root(rv, Wdata_doc_parser_v2.Str_descriptions , Srl_langtexts (Wdata_dict_langtext.Str_language , Wdata_dict_langtext.Str_value, wdoc.Descr_list()));
Srl_root(rv, Wdata_doc_parser_v2.Str_sitelinks , Srl_sitelinks (Wdata_dict_sitelink.Str_site , Wdata_dict_sitelink.Str_title, wdoc.Slink_list(), base_adj));
Srl_root(rv, Wdata_doc_parser_v2.Str_aliases , Srl_aliases (base_adj, wdoc.Alias_list()));
Srl_root(rv, Wdata_doc_parser_v2.Str_claims , Srl_claims (base_adj, legacy_style, wdoc.Claim_list()));
return (KeyVal[])rv.To_ary(KeyVal.class);
}
private static void Srl_root(List_adp rv, String label, KeyVal[] ary) {
if (ary == null) return; // don't add node if empty; EX: labels:{} should not add "labels" kv
rv.Add(KeyVal_.new_(label, ary));
}
private static KeyVal[] Srl_langtexts(String lang_label, String text_label, Ordered_hash list) {
int len = list.Count(); if (len == 0) return null;
KeyVal[] rv = new KeyVal[len];
for (int i = 0; i < len; i++) {
Wdata_langtext_itm itm = (Wdata_langtext_itm)list.Get_at(i);
String lang = String_.new_u8(itm.Lang());
String text = String_.new_u8(itm.Text());
rv[i] = KeyVal_.new_(lang, KeyVal_.Ary(KeyVal_.new_(lang_label, lang), KeyVal_.new_(text_label, text)));
}
return rv;
}
private static KeyVal[] Srl_sitelinks(String key_label, String val_label, Ordered_hash list, int base_adj) {
int len = list.Count(); if (len == 0) return null;
KeyVal[] rv = new KeyVal[len];
for (int i = 0; i < len; i++) {
Wdata_sitelink_itm itm = (Wdata_sitelink_itm)list.Get_at(i);
String site = String_.new_u8(itm.Site());
String name = String_.new_u8(itm.Name());
rv[i] = KeyVal_.new_(site, KeyVal_.Ary(KeyVal_.new_(key_label, site), KeyVal_.new_(val_label, name), Srl_sitelinks_badges(itm.Badges(), base_adj)));
}
return rv;
}
private static KeyVal Srl_sitelinks_badges(byte[][] badges, int base_adj) { // DATE:2014-11-13
if (badges == null) badges = Bry_.Ary_empty; // null badges -> badges:[]
int len = badges.length;
KeyVal[] kvs = len == 0 ? KeyVal_.Ary_empty : new KeyVal[len];
for (int i = 0; i < len; i++) {
byte[] badge = badges[i];
kvs[i] = KeyVal_.int_(i + base_adj, String_.new_u8(badge));
}
return KeyVal_.new_("badges", kvs);
}
private static KeyVal[] Srl_aliases(int base_adj, Ordered_hash list) {
int len = list.Count(); if (len == 0) return null;
KeyVal[] rv = new KeyVal[len];
for (int i = 0; i < len; i++) {
Wdata_alias_itm itm = (Wdata_alias_itm)list.Get_at(i);
String lang = String_.new_u8(itm.Lang());
rv[i] = KeyVal_.new_(lang, Srl_aliases_langs(base_adj, lang, itm.Vals()));
}
return rv;
}
private static KeyVal[] Srl_aliases_langs(int base_adj, String lang, byte[][] ary) {
int len = ary.length;
KeyVal[] rv = new KeyVal[len];
for (int i = 0; i < len; i++) {
byte[] itm = ary[i];
rv[i] = KeyVal_.int_(i + base_adj, KeyVal_.Ary(KeyVal_.new_(Wdata_dict_langtext.Str_language, lang), KeyVal_.new_(Wdata_dict_langtext.Str_value, String_.new_u8(itm)))); // NOTE: using same base_adj logic as claims
}
return rv;
}
private static KeyVal[] Srl_claims(int base_adj, boolean legacy_style, Ordered_hash claim_grps) {
int len = claim_grps.Count(); if (len == 0) return null;
int rv_len = legacy_style ? len * 2 : len; // NOTE: legacyStyle returns 2 sets of properties: official "P" and legacy "p"; DATE:2014-05-11
KeyVal[] rv = new KeyVal[rv_len];
for (int i = 0; i < len; i++) {
Wdata_claim_grp grp = (Wdata_claim_grp)claim_grps.Get_at(i);
String pid_str = Int_.Xto_str(grp.Id());
KeyVal[] grp_val = Srl_claims_prop_grp("P" + pid_str, grp, base_adj);
rv[i] = KeyVal_.new_("P" + pid_str, grp_val);
if (legacy_style)
rv[i + len] = KeyVal_.new_("p" + pid_str, grp_val); // SEE:WikibaseLuaBindings.php; This is a B/C hack to allow existing lua code to use hardcoded IDs in both lower (legacy) and upper case.; DATE:2014-05-11
}
return rv;
}
private static KeyVal[] Srl_claims_prop_grp(String pid, Wdata_claim_grp grp, int base_adj) {
int len = grp.Len();
KeyVal[] rv = new KeyVal[len];
for (int i = 0; i < len; i++) {
Wdata_claim_itm_core itm = grp.Get_at(i);
rv[i] = KeyVal_.int_(i + base_adj, Srl_claims_prop_itm(pid, itm, base_adj)); // NOTE: must be super 0 or super 1; DATE:2014-05-09
}
return rv;
}
private static KeyVal[] Srl_claims_prop_itm(String pid, Wdata_claim_itm_core itm, int base_adj) {
List_adp list = List_adp_.new_();
list.Add(KeyVal_.new_("id", pid));
list.Add(KeyVal_.new_("mainsnak", Srl_claims_prop_itm_core(pid, itm)));
list.Add(KeyVal_.new_(Wdata_dict_claim_v1.Str_rank, Wdata_dict_rank.Xto_str(itm.Rank_tid())));
list.Add(KeyVal_.new_("type", itm.Prop_type()));
Srl_root(list, Wdata_dict_claim.Str_qualifiers, Srl_qualifiers(itm.Qualifiers(), base_adj));
return (KeyVal[])list.To_ary_and_clear(KeyVal.class);
}
private static KeyVal[] Srl_qualifiers(Wdata_claim_grp_list list, int base_adj) {
if (list == null) return null;
int list_len = list.Len(); if (list_len == 0) return KeyVal_.Ary_empty;
List_adp rv = List_adp_.new_();
List_adp pid_list = List_adp_.new_();
for (int i = 0; i < list_len; ++i) {
Wdata_claim_grp grp = list.Get_at(i);
int grp_len = grp.Len();
pid_list.Clear();
String itm_pid = grp.Id_str();
for (int j = 0; j < grp_len; ++j) {
Wdata_claim_itm_core itm = grp.Get_at(j);
pid_list.Add(KeyVal_.int_(j + base_adj, Srl_claims_prop_itm_core(itm_pid, itm))); // NOTE: was originally "+ 1"; changed to base_adj; PAGE:ru.w:Tor ru.w:Кактусовые DATE:2014-10-25
}
rv.Add(KeyVal_.new_(itm_pid, (KeyVal[])pid_list.To_ary_and_clear(KeyVal.class)));
}
return (KeyVal[])rv.To_ary_and_clear(KeyVal.class);
}
private static KeyVal[] Srl_claims_prop_itm_core(String pid, Wdata_claim_itm_core itm) {
boolean snak_is_valued = itm.Snak_tid() != Wdata_dict_snak_tid.Tid_novalue;
int snak_is_valued_adj = snak_is_valued ? 1 : 0;
KeyVal[] rv = new KeyVal[2 + snak_is_valued_adj];
if (snak_is_valued)
rv[0] = KeyVal_.new_("datavalue", Srl_claims_prop_itm_core_val(itm));
rv[0 + snak_is_valued_adj] = KeyVal_.new_("property", pid);
rv[1 + snak_is_valued_adj] = KeyVal_.new_("snaktype", Wdata_dict_snak_tid.Xto_str(itm.Snak_tid()));
return rv;
}
private static final Scrib_lib_wikibase_srl_visitor visitor = new Scrib_lib_wikibase_srl_visitor();
private static KeyVal[] Srl_claims_prop_itm_core_val(Wdata_claim_itm_core itm) {
switch (itm.Snak_tid()) {
case Wdata_dict_snak_tid.Tid_somevalue: return Datavalue_somevalue;
case Wdata_dict_snak_tid.Tid_novalue: return Datavalue_novalue;
default:
itm.Welcome(visitor);
return visitor.Rv();
}
}
public static final String Key_type = "type", Key_value = "value";
private static final KeyVal[] Datavalue_somevalue = new KeyVal[] {KeyVal_.new_(Key_type, ""), KeyVal_.new_(Key_value, "")}; // NOTE: must return ""; null fails; EX:w:Joseph-François_Malgaigne; DATE:2014-04-07
private static final KeyVal[] Datavalue_novalue = KeyVal_.Ary_empty; // NOTE: novalue must return empty array (no datavalue node in json); PAGE:ru.w:Лимонов,_Эдуард_Вениаминович; DATE:2015-02-16
}

View File

@@ -0,0 +1,417 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
import gplx.json.*; import gplx.xowa.xtns.wdatas.*; import gplx.xowa.xtns.wdatas.core.*; import gplx.xowa.xtns.wdatas.parsers.*;
public class Scrib_lib_wikibase_srl_tst {
@Before public void init() {fxt.Clear();} private Scrib_lib_wikibase_srl_fxt fxt = new Scrib_lib_wikibase_srl_fxt();
@Test public void Label() {
fxt.Init_label("en", "Earth").Init_label("fr", "Terre").Init_label("de", "Erde");
fxt.Test
( "labels:"
, " en:"
, " language:'en'"
, " value:'Earth'"
, " fr:"
, " language:'fr'"
, " value:'Terre'"
, " de:"
, " language:'de'"
, " value:'Erde'"
, ""
);
}
@Test public void Description() {
fxt.Init_description("en", "Earth").Init_description("fr", "Terre").Init_description("de", "Erde");
fxt.Test
( "descriptions:"
, " en:"
, " language:'en'"
, " value:'Earth'"
, " fr:"
, " language:'fr'"
, " value:'Terre'"
, " de:"
, " language:'de'"
, " value:'Erde'"
, ""
);
}
@Test public void Sitelinks() {
fxt.Init_link("enwiki", "Earth").Init_link("frwiki", "Terre").Init_link("dewiki", "Erde");
fxt.Test
( "sitelinks:"
, " enwiki:"
, " site:'enwiki'"
, " title:'Earth'"
, " badges:"
, " frwiki:"
, " site:'frwiki'"
, " title:'Terre'"
, " badges:"
, " dewiki:"
, " site:'dewiki'"
, " title:'Erde'"
, " badges:"
, ""
);
}
@Test public void Sitelinks_both_formats() { // PURPOSE: check that both formats are serializable; DATE:2014-02-06
Json_doc jdoc = Json_doc.new_apos_concat_nl
( "{ 'entity':['item',2]"
, ", 'links':"
, " {"
, " 'enwiki':'Earth'" // old format
, " , 'frwiki':{'name':'Terre','badges':['Q3']}" // new format
, " }"
, "}"
);
Wdata_doc wdoc = new Wdata_doc(Bry_.new_a7("q2"), fxt.Wdata_fxt().App().Wiki_mgr().Wdata_mgr(), jdoc);
fxt.Test
( wdoc
, "sitelinks:"
, " enwiki:"
, " site:'enwiki'"
, " title:'Earth'"
, " badges:"
, " frwiki:"
, " site:'frwiki'"
, " title:'Terre'"
, " badges:"
, " 1:'Q3'"
, ""
);
}
@Test public void Aliases() {
fxt.Init_alias("en", "en_0", "en_1", "en_2").Init_alias("fr", "fr_0").Init_alias("de", "de_0", "de_1");
fxt.Test
( "aliases:"
, " en:"
, " 1:"
, " language:'en'"
, " value:'en_0'"
, " 2:"
, " language:'en'"
, " value:'en_1'"
, " 3:"
, " language:'en'"
, " value:'en_2'"
, " fr:"
, " 1:"
, " language:'fr'"
, " value:'fr_0'"
, " de:"
, " 1:"
, " language:'de'"
, " value:'de_0'"
, " 2:"
, " language:'de'"
, " value:'de_1'"
, ""
);
}
@Test public void Claims_str() {
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_str(2, "Moon"));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'string'"
, " value:'Moon'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_somevalue() { // PURPOSE: somevalue should always return value node; EX:w:Joseph-François_Malgaigne; DATE:2014-04-07
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_somevalue(2));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:''"
, " value:''"
, " property:'P2'"
, " snaktype:'somevalue'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_novalue() { // PURPOSE: novalue must return empty array (no datavalue node in json); PAGE:ru.w:Лимонов,_Эдуард_Вениаминович; DATE:2015-02-16
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_novalue(2));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " property:'P2'"
, " snaktype:'novalue'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_entity() {
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_entity_qid(2, 3));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'wikibase-entityid'"
, " value:"
, " entity-type:'item'"
, " numeric-id:'3'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_base_0() { // PURPOSE: test for legacyStyle (aka base_0); used by pl.w:Module:Wikidane; DATE:2014-05-09
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_entity_qid(2, 3));
fxt.Test(true
, "claims:"
, " P2:"
, " 0:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'wikibase-entityid'"
, " value:"
, " entity-type:'item'"
, " numeric-id:'3'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, " p2:"
, " 0:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'wikibase-entityid'"
, " value:"
, " entity-type:'item'"
, " numeric-id:'3'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_time() {
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_time(2, "2001-02-03 04:05:06"));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'time'"
, " value:"
, " time:'+00000002001-02-03T04:05:06Z'"
, " precision:'11'"
, " before:'0'"
, " after:'0'"
, " timezone:'0'"
, " calendarmodel:'http://www.wikidata.org/entity/Q1985727'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_globecoordinate() {
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_geo(2, "6.789", "1.2345"));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'globecoordinate'"
, " value:"
, " latitude:'1.2345'"
, " longitude:'6.789'"
, " altitude:null"
, " globe:'http://www.wikidata.org/entity/Q2'"
, " precision:'1.0E-5'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_quantity() {
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_quantity(2, "+1,234", "2", "+1,236", "+1232"));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'quantity'"
, " value:"
, " amount:'+1,234'"
, " unit:'2'"
, " upperBound:'+1,236'"
, " lowerBound:'+1232'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Claims_monolingualtext() {// PURPOSE.fix: type was mistakenly "language" instead of "monolingualtext" DATE:2014-10-21
fxt.Init_prop(fxt.Wdata_fxt().Make_claim_monolingual(2, "en", "en_text"));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'monolingualtext'"
, " value:"
, " text:'en_text'"
, " language:'en'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, ""
);
}
@Test public void Qualifiers() {
Wdata_wiki_mgr_fxt wdata_fxt = fxt.Wdata_fxt();
fxt.Init_prop(wdata_fxt.Make_claim_str(2, "Earth").Qualifiers_(wdata_fxt.Make_qualifiers(wdata_fxt.Make_qualifiers_grp(3, wdata_fxt.Make_claim_time(3, "2001-02-03 04:05:06")))));
fxt.Test
( "claims:"
, " P2:"
, " 1:"
, " id:'P2'"
, " mainsnak:"
, " datavalue:"
, " type:'string'"
, " value:'Earth'"
, " property:'P2'"
, " snaktype:'value'"
, " rank:'normal'"
, " type:'statement'"
, " qualifiers:"
, " P3:"
, " 1:"
, " datavalue:"
, " type:'time'"
, " value:"
, " time:'+00000002001-02-03T04:05:06Z'"
, " precision:'11'"
, " before:'0'"
, " after:'0'"
, " timezone:'0'"
, " calendarmodel:'http://www.wikidata.org/entity/Q1985727'"
, " property:'P3'"
, " snaktype:'value'"
, ""
);
}
}
class Scrib_lib_wikibase_srl_fxt {
private Wdata_doc_bldr wdoc_bldr;
public void Clear() {
wdata_fxt = new Wdata_wiki_mgr_fxt();
wdata_fxt.Init();
wdoc_bldr = wdata_fxt.Wdoc_bldr("q2");
header_enabled = false;
}
public Wdata_wiki_mgr_fxt Wdata_fxt() {return wdata_fxt;} private Wdata_wiki_mgr_fxt wdata_fxt;
private boolean header_enabled;
public Scrib_lib_wikibase_srl_fxt Init_header_enabled_y_() {header_enabled = true; return this;}
public Scrib_lib_wikibase_srl_fxt Init_label(String lang, String label) {
wdoc_bldr.Add_label(lang, label);
return this;
}
public Scrib_lib_wikibase_srl_fxt Init_description(String lang, String description) {
wdoc_bldr.Add_description(lang, description);
return this;
}
public Scrib_lib_wikibase_srl_fxt Init_link(String xwiki, String val) {
wdoc_bldr.Add_sitelink(xwiki, val);
return this;
}
public Scrib_lib_wikibase_srl_fxt Init_alias(String lang, String... ary) {
wdoc_bldr.Add_alias(lang, ary);
return this;
}
public Scrib_lib_wikibase_srl_fxt Init_prop(Wdata_claim_itm_core prop) {wdoc_bldr.Add_claims(prop); return this;}
public Scrib_lib_wikibase_srl_fxt Test(String... expd) {return Test(false, expd);}
public Scrib_lib_wikibase_srl_fxt Test(boolean base0, String... expd) {
KeyVal[] actl = Scrib_lib_wikibase_srl.Srl(wdoc_bldr.Xto_wdoc(), header_enabled, base0);
Tfds.Eq_ary_str(expd, String_.SplitLines_nl(Xto_str(actl)));
return this;
}
public Scrib_lib_wikibase_srl_fxt Test(Wdata_doc wdoc, String... expd) {return Test(false, wdoc, expd);}
public Scrib_lib_wikibase_srl_fxt Test(boolean base0, Wdata_doc wdoc, String... expd) {
KeyVal[] actl = Scrib_lib_wikibase_srl.Srl(wdoc, header_enabled, base0);
Tfds.Eq_ary_str(expd, String_.SplitLines_nl(Xto_str(actl)));
return this;
}
private String Xto_str(KeyVal[] ary) {
Bry_bfr bfr = Bry_bfr.new_();
Xto_str(bfr, ary, 0);
return bfr.Xto_str_and_clear();
}
private void Xto_str(Bry_bfr bfr, KeyVal[] ary, int depth) {
int len = ary.length;
for (int i = 0; i < len; i++) {
KeyVal kv = ary[i];
Xto_str(bfr, kv, depth);
}
}
private void Xto_str(Bry_bfr bfr, KeyVal kv, int depth) {
bfr.Add_byte_repeat(Byte_ascii.Space, depth * 2);
bfr.Add_str(kv.Key()).Add_byte(Byte_ascii.Colon);
Object kv_val = kv.Val();
if (kv_val == null) {bfr.Add_str_a7("null").Add_byte_nl(); return;}
Class<?> kv_val_cls = kv_val.getClass();
if (ClassAdp_.Eq(kv_val_cls, KeyVal[].class)) {bfr.Add_byte_nl(); Xto_str(bfr, (KeyVal[])kv_val, depth + 1);}
else if (ClassAdp_.Eq(kv_val_cls, KeyVal[].class)) {bfr.Add_byte_nl(); Xto_str(bfr, (KeyVal)kv_val, depth + 1);}
else bfr.Add_byte(Byte_ascii.Apos).Add_str(Object_.Xto_str_strict_or_empty(kv_val)).Add_byte(Byte_ascii.Apos).Add_byte_nl();
}
}

View File

@@ -0,0 +1,94 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.xtns.wdatas.core.*;
class Scrib_lib_wikibase_srl_visitor implements Wdata_claim_visitor {
public KeyVal[] Rv() {return rv;} KeyVal[] rv;
public void Visit_str(Wdata_claim_itm_str itm) {
rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_type, Wdata_dict_val_tid.Xto_str(itm.Val_tid()));
rv[1] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_value, String_.new_u8(itm.Val_str()));
}
public void Visit_entity(Wdata_claim_itm_entity itm) {
rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_type, Wdata_dict_val_tid.Str_entity);
rv[1] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_value, Entity_value(itm));
}
private static KeyVal[] Entity_value(Wdata_claim_itm_core itm) {
Wdata_claim_itm_entity claim_entity = (Wdata_claim_itm_entity)itm;
KeyVal[] rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Wdata_dict_value_entity.Str_entity_type, claim_entity.Entity_tid_str());
rv[1] = KeyVal_.new_(Wdata_dict_value_entity.Str_numeric_id, Int_.Xto_str(claim_entity.Entity_id()));
return rv;
}
public void Visit_monolingualtext(Wdata_claim_itm_monolingualtext itm) {
rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_type, Wdata_dict_val_tid.Str_monolingualtext);
rv[1] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_value, Monolingualtext_value(itm));
}
private static KeyVal[] Monolingualtext_value(Wdata_claim_itm_monolingualtext itm) {
KeyVal[] rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Wdata_dict_value_monolingualtext.Str_text , String_.new_u8(itm.Text()));
rv[1] = KeyVal_.new_(Wdata_dict_value_monolingualtext.Str_language , String_.new_u8(itm.Lang()));
return rv;
} public void Visit_quantity(Wdata_claim_itm_quantity itm) {
rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_type, Wdata_dict_val_tid.Str_quantity);
rv[1] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_value, Quantity_value(itm));
}
private static KeyVal[] Quantity_value(Wdata_claim_itm_quantity itm) {
KeyVal[] rv = new KeyVal[4];
rv[0] = KeyVal_.new_(Wdata_dict_value_quantity.Str_amount , String_.new_u8(itm.Amount()));
rv[1] = KeyVal_.new_(Wdata_dict_value_quantity.Str_unit , String_.new_u8(itm.Unit()));
rv[2] = KeyVal_.new_(Wdata_dict_value_quantity.Str_upperbound , String_.new_u8(itm.Ubound()));
rv[3] = KeyVal_.new_(Wdata_dict_value_quantity.Str_lowerbound , String_.new_u8(itm.Lbound()));
return rv;
}
public void Visit_time(Wdata_claim_itm_time itm) {
rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_type, Wdata_dict_val_tid.Str_time);
rv[1] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_value, Time_value(itm));
}
private static KeyVal[] Time_value(Wdata_claim_itm_time itm) {
KeyVal[] rv = new KeyVal[6];
rv[0] = KeyVal_.new_(Wdata_dict_value_time.Str_time , String_.new_a7(itm.Time()));
rv[1] = KeyVal_.new_(Wdata_dict_value_time.Str_precision , Wdata_dict_value_time.Val_precision_int); // NOTE: must return int, not str; DATE:2014-02-18
rv[2] = KeyVal_.new_(Wdata_dict_value_time.Str_before , Wdata_dict_value_time.Val_before_int);
rv[3] = KeyVal_.new_(Wdata_dict_value_time.Str_after , Wdata_dict_value_time.Val_after_int);
rv[4] = KeyVal_.new_(Wdata_dict_value_time.Str_timezone , Wdata_dict_value_time.Val_timezone_str);
rv[5] = KeyVal_.new_(Wdata_dict_value_time.Str_calendarmodel , Wdata_dict_value_time.Val_calendarmodel_str);
return rv;
}
public void Visit_globecoordinate(Wdata_claim_itm_globecoordinate itm) {
rv = new KeyVal[2];
rv[0] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_type, Wdata_dict_val_tid.Str_globecoordinate);
rv[1] = KeyVal_.new_(Scrib_lib_wikibase_srl.Key_value, Globecoordinate_value(itm));
}
private static KeyVal[] Globecoordinate_value(Wdata_claim_itm_globecoordinate itm) {
KeyVal[] rv = new KeyVal[5];
rv[0] = KeyVal_.new_(Wdata_dict_value_globecoordinate.Str_latitude , Double_.parse_(String_.new_a7(itm.Lat())));
rv[1] = KeyVal_.new_(Wdata_dict_value_globecoordinate.Str_longitude , Double_.parse_(String_.new_a7(itm.Lng())));
rv[2] = KeyVal_.new_(Wdata_dict_value_globecoordinate.Str_altitude , null);
rv[3] = KeyVal_.new_(Wdata_dict_value_globecoordinate.Str_globe , Wdata_dict_value_globecoordinate.Val_globe_dflt_str);
rv[4] = KeyVal_.new_(Wdata_dict_value_globecoordinate.Str_precision , .00001d);
return rv;
}
public void Visit_system(Wdata_claim_itm_system itm) {
rv = KeyVal_.Ary_empty;
}
}

View File

@@ -0,0 +1,63 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
import gplx.xowa.xtns.wdatas.*;
public class Scrib_lib_wikibase_tst {
@Before public void init() {
fxt.Clear_for_lib();
lib = fxt.Core().Lib_wikibase().Init();
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
@Test public void GetGlobalSiteId() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getGlobalSiteId, Object_.Ary_empty, "enwiki");
}
@Test public void GetEntityId() {
Wdata_wiki_mgr_fxt wdata_fxt = new Wdata_wiki_mgr_fxt().Init(fxt.Parser_fxt(), false);
wdata_fxt.Init_links_add("enwiki", "Earth", "q2");
fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getEntityId, Object_.Ary("Earth" ), "q2");
fxt.Test_scrib_proc_str(lib, Scrib_lib_wikibase.Invk_getEntityId, Object_.Ary("missing_page" ), "");
}
@Test public void GetEntity() {
Wdata_wiki_mgr_fxt wdata_fxt = new Wdata_wiki_mgr_fxt().Init(fxt.Parser_fxt(), false);
wdata_fxt.Init_pages_add(wdata_fxt.Wdoc_bldr("q2").Add_label("en", "b").Xto_wdoc());
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_wikibase.Invk_getEntity, Object_.Ary("q2", false), String_.Concat_lines_nl_skip_last
( "1="
, " id=q2"
, " type=item"
, " schemaVersion=2"
, " labels="
, " en="
, " language=en"
, " value=b"
));
}
@Test public void GetEntity_property() { // PURPOSE: getEntity should be able to convert "p2" to "Property:P2"; EX:es.w:Arnold_Gesell; DATE:2014-02-18
Wdata_wiki_mgr_fxt wdata_fxt = new Wdata_wiki_mgr_fxt().Init(fxt.Parser_fxt(), false);
wdata_fxt.Init_pages_add(wdata_fxt.Wdoc_bldr("Property:p2").Add_label("en", "b").Xto_wdoc());
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_wikibase.Invk_getEntity, Object_.Ary("p2", false), String_.Concat_lines_nl_skip_last
( "1="
, " id=Property:p2" // only difference from above
, " type=item"
, " schemaVersion=2"
, " labels="
, " en="
, " language=en"
, " value=b"
));
}
}

View File

@@ -0,0 +1,264 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
public class Scrib_regx_converter {
private List_adp capt_list = List_adp_.new_(), grps_parens = List_adp_.new_(); private List_adp grps_open = List_adp_.new_();
public Scrib_regx_converter() {Init();}
public String Regx() {return regx;} private String regx;
public List_adp Capt_list() {return capt_list;}
public KeyVal[] Capt_ary() {return capt_list.Count() == 0 ? null : (KeyVal[])capt_list.To_ary(KeyVal.class);}
private Bry_fmtr fmtr_balanced; private Bry_bfr bfr_balanced;
public String Parse(byte[] src, byte[] anchor) {
int len = src.length;
boolean q_flag = false;
capt_list.Clear(); grps_open.Clear(); grps_parens.Clear();
int grps_len = 0;
int bct = 0;
// bfr.Add_byte(Byte_ascii.Slash); // NOTE: do not add PHP "/" at start
for (int i = 0; i < len; i++) {
q_flag = false; // must be reset; REF.MW:UstringLibrary.php|patternToRegex; DATE:2014-02-08
byte cur = src[i];
switch (cur) {
case Byte_ascii.Pow:
q_flag = i != 0;
bfr.Add(anchor == Anchor_null || q_flag ? Bry_pow_escaped : anchor); // NOTE: must add anchor \G when using offsets; EX:cs.n:Category:1._zárí_2008; DATE:2014-05-07
break;
case Byte_ascii.Dollar:
q_flag = i < len - 1;
bfr.Add(q_flag ? Bry_dollar_escaped : Bry_dollar_literal);
break;
case Byte_ascii.Paren_bgn: {
if (i + 1 >= len) throw Exc_.new_("Unmatched open-paren at pattern character " + Int_.Xto_str(i));
boolean capt_itm = src[i + 1] == Byte_ascii.Paren_end; // current is "()"
++grps_len;
capt_list.Add(KeyVal_.int_(grps_len, capt_itm));
bfr.Add_byte(Byte_ascii.Paren_bgn);
grps_open.Add(grps_len);
grps_parens.Add(i + 1);
break;
}
case Byte_ascii.Paren_end:
if (grps_open.Count() == 0)
throw Exc_.new_("Unmatched close-paren at pattern character " + Int_.Xto_str(i));
List_adp_.DelAt_last(grps_open);
bfr.Add_byte(Byte_ascii.Paren_end);
break;
case Byte_ascii.Percent:
++i;
if (i >= len) throw Exc_.new_("malformed pattern (ends with '%')");
Object percent_obj = percent_hash.Get_by_mid(src, i, i + 1);
if (percent_obj != null) {
bfr.Add((byte[])percent_obj);
q_flag = true;
}
else {
byte nxt = src[i];
switch (nxt) {
case Byte_ascii.Ltr_b: // EX: "%b()"
i += 2;
if (i >= len) throw Exc_.new_("malformed pattern (missing arguments to \'%b\')");
byte char_0 = src[i - 1];
byte char_1 = src[i];
if (char_0 == char_1) { // same char: easier regex; REF.MW: $bfr .= "{$d1}[^$d1]*$d1";
bfr.Add(Bry_bf0_seg_0);
Regx_quote(bfr, char_0);
bfr.Add(Bry_bf0_seg_1);
Regx_quote(bfr, char_0);
bfr.Add(Bry_bf0_seg_2);
Regx_quote(bfr, char_0);
}
else { // diff char: harder regex; REF.MW: $bfr .= "(?<b$bct>$d1(?:(?>[^$d1$d2]+)|(?P>b$bct))*$d2)";
if (fmtr_balanced == null) {
fmtr_balanced = Bry_fmtr.new_("(?<b~{0}>\\~{1}(?:(?>[^\\~{1}\\~{2}]*)|\\~{1}[^\\~{1}\\~{2}]*\\~{2})*\\~{2})", "0", "1", "2"); // NOTE: complicated regex; represents 3 level depth of balanced parens; 4+ won't work; EX:(3(2(1)2)3) PAGE:en.w:Electricity_sector_in_Switzerland DATE:2015-01-23
bfr_balanced = Bry_bfr.reset_(255);
}
synchronized (fmtr_balanced) {
++bct;
fmtr_balanced.Bld_bfr(bfr_balanced, Int_.Xto_bry(bct), Byte_.Ary(char_0), Byte_.Ary(char_1));
bfr.Add(bfr_balanced.Xto_bry_and_clear());
}
}
break;
case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4:
case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9:
grps_len = nxt - Byte_ascii.Num_0;
if (grps_len == 0 || grps_len > capt_list.Count() || grps_open_Has(grps_open, grps_len))
throw Exc_.new_("invalid capture index %" + grps_len + " at pattern character " + Int_.Xto_str(i));
bfr.Add(Bry_bf2_seg_0).Add_int_variable(grps_len);//.Add(Bry_bf2_seg_1); // $bfr .= "\\g{m$grps_len}";
break;
default:
Regx_quote(bfr, nxt);
q_flag = true;
break;
}
}
break;
case Byte_ascii.Brack_bgn:
bfr.Add_byte(Byte_ascii.Brack_bgn);
++i;
if (i < len && src[i] == Byte_ascii.Pow) { // ^
bfr.Add_byte(Byte_ascii.Pow);
++i;
}
boolean stop = false;
for (; i < len; i++) {
byte tmp_b = src[i];
switch (tmp_b) {
case Byte_ascii.Brack_end:
stop = true;
break;
case Byte_ascii.Percent:
++i;
if (i >= len)
stop = true;
else {
Object brack_obj = brack_hash.Get_by_mid(src, i, i + 1);
if (brack_obj != null)
bfr.Add((byte[])brack_obj);
else
Regx_quote(bfr, src[i]);
}
break;
default:
boolean normal = true;
if (i + 2 < len) {
byte dash_1 = src[i + 1];
byte dash_2 = src[i + 2];
if (dash_1 == Byte_ascii.Dash && dash_2 != Byte_ascii.Brack_end) {
Regx_quote(bfr, tmp_b);
bfr.Add_byte(Byte_ascii.Dash);
Regx_quote(bfr, dash_2);
i += 2;
normal = false;
}
}
if (normal)
Regx_quote(bfr, src[i]);
break;
}
if (stop) break;
}
if (i >= len) throw Exc_.new_("Missing close-bracket for character set beginning at pattern character $nxt_pos");
bfr.Add_byte(Byte_ascii.Brack_end);
q_flag = true;
break;
case Byte_ascii.Brack_end: throw Exc_.new_("Unmatched close-bracket at pattern character " + Int_.Xto_str(i));
case Byte_ascii.Dot:
q_flag = true;
bfr.Add_byte(Byte_ascii.Dot);
break;
default:
q_flag = true;
Regx_quote(bfr, cur);
break;
}
if (q_flag && i + 1 < len) {
byte tmp_b = src[i + 1];
switch (tmp_b) {
case Byte_ascii.Star:
case Byte_ascii.Plus:
case Byte_ascii.Question:
bfr.Add_byte(tmp_b);
++i;
break;
case Byte_ascii.Dash:
bfr.Add(Bry_regx_dash);
++i;
break;
}
}
}
if (grps_open.Count() > 0) throw Exc_.new_("Unclosed capture beginning at pattern character " + Int_.cast_(grps_open.Get_at(0)));
// bfr.Add(Bry_regx_end); // NOTE: do not add PHP /us at end; u=PCRE_UTF8 which is not needed for Java; s=PCRE_DOTALL which will be specified elsewhere
regx = bfr.Xto_str_and_clear();
return regx;
} private Bry_bfr bfr = Bry_bfr.new_();
boolean grps_open_Has(List_adp list, int v) {
int len = list.Count();
for (int i = 0; i < len; i++) {
Object o = list.Get_at(i);
if (Int_.cast_(o) == v) return true;
}
return false;
}
private void Regx_quote(Bry_bfr bfr, byte b) {
if (Regx_char(b)) bfr.Add_byte(Byte_ascii.Backslash);
bfr.Add_byte(b);
}
private boolean Regx_char(byte b) {
switch (b) {
case Byte_ascii.Dot: case Byte_ascii.Slash: case Byte_ascii.Plus: case Byte_ascii.Star: case Byte_ascii.Question:
case Byte_ascii.Pow: case Byte_ascii.Dollar: case Byte_ascii.Eq: case Byte_ascii.Bang: case Byte_ascii.Pipe:
case Byte_ascii.Colon: case Byte_ascii.Dash:
case Byte_ascii.Brack_bgn: case Byte_ascii.Brack_end: case Byte_ascii.Paren_bgn: case Byte_ascii.Paren_end: case Byte_ascii.Curly_bgn: case Byte_ascii.Curly_end:
case Byte_ascii.Lt: case Byte_ascii.Gt:
case Byte_ascii.Backslash: // \ must be preg_quote'd; DATE:2014-01-06
return true;
default:
return false;
}
}
private static final byte[] Bry_pow_escaped = Bry_.new_a7("\\^")
, Bry_dollar_literal = Bry_.new_a7("$"), Bry_dollar_escaped = Bry_.new_a7("\\$")
, Bry_bf0_seg_0 = Bry_.new_a7("{"), Bry_bf0_seg_1 = Bry_.new_a7("}[^"), Bry_bf0_seg_2 = Bry_.new_a7("]*")
, Bry_bf2_seg_0 = Bry_.new_a7("\\")//, Bry_bf2_seg_1 = Bry_.new_a7("")
, Bry_regx_dash = Bry_.new_a7("*?") // was *?
;
public static final byte[] Anchor_null = null, Anchor_G = Bry_.new_a7("\\G"), Anchor_pow = Bry_.new_a7("^");
private void Init() {
String regx_w = "\\w"; // JRE.7: \w not support in JRE.6; PAGE:en.w:A♯_(musical_note) DATE:2015-06-10
String regx_W = "\\W"; // JRE.7: \w not support in JRE.6; PAGE:en.w:A♯_(musical_note) DATE:2015-06-10
Init_itm(Bool_.Y, "d", "\\p{Nd}");
Init_itm(Bool_.Y, "l", "\\p{Ll}");
Init_itm(Bool_.Y, "u", "\\p{Lu}");
Init_itm(Bool_.Y, "a", "\\p{L}");
Init_itm(Bool_.Y, "c", "\\p{Cc}");
Init_itm(Bool_.Y, "p", "\\p{P}");
Init_itm(Bool_.Y, "s", "\\s");
Init_itm(Bool_.Y, "w", regx_w);
Init_itm(Bool_.Y, "x", "[0-9A-Fa-f0-9A-Fa-f]");
Init_itm(Bool_.Y, "z", "\\x00");
Init_itm(Bool_.Y, "D", "\\P{Nd}");
Init_itm(Bool_.Y, "L", "\\P{Ll}");
Init_itm(Bool_.Y, "U", "\\P{Lu}");
Init_itm(Bool_.Y, "A", "\\P{L}");
Init_itm(Bool_.Y, "C", "\\P{Cc}");
Init_itm(Bool_.Y, "P", "\\P{P}");
Init_itm(Bool_.Y, "S", "\\S"); // JAVA: \P{Xps} not valid
Init_itm(Bool_.Y, "W", regx_W);
Init_itm(Bool_.Y, "X", "[^0-9A-Fa-f0-9A-Fa-f]");
Init_itm(Bool_.Y, "Z", "[^\\x00]");
Init_itm(Bool_.N, "w", regx_w);
Init_itm(Bool_.N, "x", "0-9A-Fa-f0-9A-Fa-f");
Init_itm(Bool_.N, "W", regx_W);
Init_itm(Bool_.N, "X", "\\x00-\\x2f\\x3a-\\x40\\x47-\\x60\\x67-\\x{ff0f}\\x{ff1a}-\\x{ff20}\\x{ff27}-\\x{ff40}\\x{ff47}-\\x{10ffff}");
Init_itm(Bool_.N, "Z", "\\x01-\\x{10ffff}");
}
private void Init_itm(boolean add_to_percent_hash, String lua, String php) {
byte[] lua_bry = Bry_.new_a7(lua);
byte[] php_bry = Bry_.new_a7(php);
if (add_to_percent_hash) {
percent_hash.Add_bry_obj(lua_bry, php_bry);
brack_hash.Add_bry_obj(lua_bry, php_bry); // always add to brack_hash; brack_hash = percent_hash + other characters
}
else {
brack_hash.Add_if_dupe_use_nth(lua_bry, php_bry); // replace percent_hash definitions
}
}
private final Hash_adp_bry percent_hash = Hash_adp_bry.cs_(), brack_hash = Hash_adp_bry.cs_();
}

View File

@@ -0,0 +1,67 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
import org.junit.*;
import gplx.texts.*;
public class Scrib_regx_converter_tst {
@Before public void init() {fxt.Clear();} private Scrib_regx_converter_fxt fxt = new Scrib_regx_converter_fxt();
@Test public void Basic() {fxt.Test_parse("abc012ABC" , "abc012ABC");}
@Test public void Pow_0() {fxt.Test_parse("^a" , "\\Ga");}
@Test public void Pow_1() {fxt.Test_parse("a^b" , "a\\^b");}
@Test public void Dollar_n() {fxt.Test_parse("$a" , "\\$a");}
@Test public void Dollar_last() {fxt.Test_parse("a$" , "a$");}
@Test public void Dot() {fxt.Test_parse("a." , "a.");}
// @Test public void Paren() {fxt.Test_parse("(a)" , "(?<m1>a)");}
@Test public void Percent_has() {fxt.Test_parse("%a" , "\\p{L}");}
@Test public void Percent_na() {fxt.Test_parse("%y" , "y");}
@Test public void Percent_b00() {fxt.Test_parse("%b00" , "{0}[^0]*0");}
@Test public void Percent_b01() {fxt.Test_parse("%b01" , "(?<b1>\\0(?:(?>[^\\0\\1]*)|\\0[^\\0\\1]*\\1)*\\1)");}
// @Test public void Percent_num() {fxt.Test_parse("()%1" , "(?<m1>)\\g{m1}");}
@Test public void Percent_text() {fxt.Test_parse("%e" , "e");}
@Test public void Brack_pow() {fxt.Test_parse("[^a]" , "[^a]");}
@Test public void Brack_percent_a() {fxt.Test_parse("[%a]" , "[\\p{L}]");} // NOTE: was previously [a]; DATE:2013-11-08
@Test public void Brack_dash() {fxt.Test_parse("[a-z]" , "[a-z]");}
@Test public void Brack_num() {fxt.Test_parse("[%d]" , "[\\p{Nd}]");}
@Test public void Brack_text() {fxt.Test_parse("[abc]" , "[abc]");}
@Test public void Null() {fxt.Test_parse("[%z]" , "[\\x00]");}
@Test public void Null_not() {fxt.Test_parse("%Z" , "[^\\x00]");}
@Test public void Backslash() {fxt.Test_parse("\\" , "\\\\");} // PURPOSE: make sure \ is preg_quote'd; DATE:2014-01-06
@Test public void Ex_url() {fxt.Test_parse("^%s*(.-)%s*$" , "\\G\\s*(.*?)\\s*$");}
@Test public void Balanced() {
fxt.Test_replace("a(1)c" , "%b()", "b", "abc");
fxt.Test_replace("a(2(1)2)c" , "%b()", "b", "abc");
fxt.Test_replace("a(3(2(1)2)3)c" , "%b()", "b", "a(3b3)c");
}
}
class Scrib_regx_converter_fxt {
private Scrib_regx_converter under;
public void Clear() {
if (under == null) {
under = new Scrib_regx_converter();
}
}
public void Test_parse(String raw, String expd) {
under.Parse(Bry_.new_u8(raw), Scrib_regx_converter.Anchor_G);
Tfds.Eq(expd, under.Regx());
}
public void Test_replace(String text, String find, String replace, String expd) {
String regex_str = under.Parse(Bry_.new_u8(find), Scrib_regx_converter.Anchor_G);
String actl = RegxAdp_.Replace(text, regex_str, replace);
Tfds.Eq(expd, actl);
}
}