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
2014-06-30 00:04:32 -04:00
parent 85594d3cdd
commit bae88e739c
2482 changed files with 198730 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_i18n_tst {
@Before public void init() {fxt.Clear();} private Pf_i18n_fxt fxt = new Pf_i18n_fxt();
@Test public void Casing() {fxt.lang_("de").Ini().Reg_func("fullurl", false, "VOLLSTÄNDIGE_URL").Load().Fxt().Test_parse_tmpl_str_test("{{vollstÄndige_url:a}}", "{{test}}" , "//de.wikipedia.org/wiki/A");}
@Test public void Time() {fxt.lang_("de").Ini().Reg_msg("march", "März").Load().Fxt().Test_parse_tmpl_str_test("{{#time: d F Y|1 Mar 2013}}", "{{test}}" , "01 März 2013");}
}
class Pf_i18n_fxt {
public void Clear() {}
public Xop_fxt Fxt() {return fxt;}
public Pf_i18n_fxt lang_(String v) {lang_key = v; return this;} private String lang_key;
public Pf_i18n_fxt Ini() {
if (app == null) app = Xoa_app_fxt.app_();
app.Lang_mgr().Clear(); // else lang values retained from last run
app.Free_mem(false); // else tmpl_result_cache will get reused from last run for {{test}}
lang = app.Lang_mgr().Get_by_key_or_new(Bry_.new_ascii_(lang_key));
wiki = Xoa_app_fxt.wiki_(app, lang_key + ".wikipedia.org", lang);
fxt = new Xop_fxt(app, wiki);
return this;
} private Xoa_app app; private Xop_fxt fxt; Xol_lang lang; Xow_wiki wiki;
public Pf_i18n_fxt Reg_func(String name, boolean case_match, String word) {
Io_url url = Io_url_.mem_fil_("mem/xowa/user/test_user/lang/xowa/" + lang_key + ".gfs");
String func = "keywords.load_text('" + name + "|" + (case_match ? "1" : "0") + "|" + name + "~" + word + "~');";
Io_mgr._.SaveFilStr(url, func);
return this;
}
public Pf_i18n_fxt Reg_msg(String key, String val) {
Io_url url = Io_url_.mem_fil_("mem/xowa/user/test_user/lang/xowa/" + lang_key + ".gfs");
String func = "messages.load_text('" + key + "|" + val + "');";
Io_mgr._.SaveFilStr(url, func);
return this;
}
public Pf_i18n_fxt Load() {
lang.Init_by_load();
wiki.Fragment_mgr().Evt_lang_changed(lang);
return this;
}
}

View File

@@ -0,0 +1,31 @@
/*
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; import gplx.*;
class Pf_intl_grammar extends Pf_func_base {
@Override public int Id() {return Xol_kwd_grp_.Id_i18n_grammar;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_intl_grammar().Name_(name);}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {
byte[] argx = Eval_argx(ctx, src, caller, self);
byte[] word = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, self.Args_len(), 0);
Xol_lang lang = ctx.Cur_page().Lang();
boolean pass = false;
try {pass = lang.Grammar().Grammar_eval(bfr, lang, word, argx);}
catch (Exception e) {Err_.Noop(e);}
if (!pass) Xot_invk_tkn.Print_not_found(bfr, ctx.Wiki().Ns_mgr(), this.Name());
}
}

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; import gplx.*;
import org.junit.*;
public class Pf_intl_grammar_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void English() {// for now, mark unimplemented langs by returning not_found; [[Template:grammar]]; wait for users to report
fxt.Test_parse_tmpl_str_test("{{grammar:a|b}}" , "{{test}}" , "[[:Template:grammar]]");
}
@Test public void Finnish() {
fxt.Lang_by_id_(Xol_lang_itm_.Id_fi);
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:elative|Wikiuutiset}}" , "{{test}}" , "Wikiuutisista");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:talo}}" , "{{test}}" , "");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|talo}}" , "{{test}}" , "talon");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:elative|talo}}" , "{{test}}" , "talosta");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:inessive|talo}}" , "{{test}}" , "talossa");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:partitive|talo}}" , "{{test}}" , "taloa");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:illative|talo}}" , "{{test}}" , "taloon");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|sängy}}" , "{{test}}" , "sängyn");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:elative|sängy}}" , "{{test}}" , "sängystä");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:inessive|sängy}}" , "{{test}}" , "sängyssä");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:partitive|sängy}}" , "{{test}}" , "sängyä");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:illative|sängy}}" , "{{test}}" , "sängyyn");
}
@Test public void Russian() {
fxt.Lang_by_id_(Xol_lang_itm_.Id_ru);
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:unknown}}" , "{{test}}" , "");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aвики}}" , "{{test}}" , "aвики");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aВики}}" , "{{test}}" , "aВики");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aь}}" , "{{test}}" , "");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aия}}" , "{{test}}" , "aии");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aка}}" , "{{test}}" , "aки");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aти}}" , "{{test}}" , "ей");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aды}}" , "{{test}}" , "ов");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:genitive|aник}}" , "{{test}}" , "aника");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:dative|a}}" , "{{test}}" , "a");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:accusative|a}}" , "{{test}}" , "a");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:instrumental|a}}" , "{{test}}" , "a");
fxt.Reset().Test_parse_tmpl_str_test("{{grammar:prepositional|a}}" , "{{test}}" , "a");
}
}

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; import gplx.*;
import gplx.php.*;
class Pf_intl_int extends Pf_func_base {
@Override public int Id() {return Xol_kwd_grp_.Id_i18n_int;}
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {
byte[] msg_key = Eval_argx(ctx, src, caller, self);
Xow_wiki wiki = ctx.Wiki();
Xol_lang page_lang = ctx.Cur_page().Lang();
byte[][] args_ary = Bry_.Ary_empty;
int args_len = self.Args_len();
if (args_len > 0) {
args_ary = new byte[args_len][];
for (int i = 0; i < args_len; i++)
args_ary[i] = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, self.Args_len(), i);
}
byte[] msg_val = Pf_msg_mgr.Get_msg_val(wiki, page_lang, msg_key, args_ary);
bfr.Add(msg_val);
}
@Override public Pf_func New(int id, byte[] name) {return new Pf_intl_int().Name_(name);}
}

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; import gplx.*;
import org.junit.*;
public class Pf_intl_int_tst {
@Before public void init() {fxt.Reset();} private Pf_msg_mgr_fxt fxt = new Pf_msg_mgr_fxt();
@Test public void Basic() {fxt.Test_parse_en("{{int:january}}" , "January");}
@Test public void Upper() {fxt.Test_parse_en("{{int:JANUARY}}" , "January");}
@Test public void Unknown() {fxt.Test_parse_en("{{int:unknown_msg}}" , "<unknown_msg>");}
@Test public void Fmt() {fxt.Test_parse_en("{{int:pfunc_expr_unrecognised_word|1a}}" , "Expression error: Unrecognised word \"1a\"");}
@Test public void Tmpl_txt() {
fxt.Init_msg_gfs("tst_msg", "{{#expr:1}}", false, true);
fxt.Test_parse_en("{{int:tst_msg}}", "1");
}
@Test public void Lang_current_defaults_to_en() { // PURPOSE: specifying same language as current returns same; ie: int:january/en -> int:january
fxt.Test_parse_en("{{int:january/en}}", "January");
}
@Test public void Lang_specified_by_page() {
fxt.Test_parse_lang("fr", "{{int:Lang}}", "fr"); // NOTE: "Lang" msg is added by Xol_lang; message_mgr.Itm_by_key_or_new(Bry_.new_utf8_("Lang")).Atrs_set(key_bry, false, false);
}
@Test public void Lang_missing_msg_return_en() { // PURPOSE: if key does not exist in non-english language, use English; EX: la.w:Fasciculus:HannibalFrescoCapitolinec1510.jpg; DATE:2013-09-10
fxt.Init_msg_gfs("en_only_key", "en_only_val", false, false);
fxt.Test_parse_lang("fr", "{{int:en_only_key}}", "en_only_val");
}
@Test public void Err_fmt_failed() { // PURPOSE: if no args passed to msg, return "$1", not "~{0}"
fxt.Init_msg_gfs("tst_msg", "a~{0}b", true, false);
fxt.Test_parse_en("{{int:tst_msg}}" , "a$1b");
}
@Test public void Mediawiki_overrides_gfs() {
fxt.Init_msg_gfs("mw_overrides", "gfs", false, false);
fxt.Init_msg_db("mw_overrides", "mw");
fxt.Test_parse_en("{{int:mw_overrides}}", "mw");
}
@Test public void Convert_php() {
fxt.Init_msg_db("convert_php", "a\\\\b\\$c$1e");
fxt.Test_parse_en("{{int:convert_php|d}}", "a\\b$cde");
}
@Test public void Convert_php_tilde() { // PURPOSE: tildes should be escaped, else will fail inside ByteAryBfrFmtr; DATE:2013-11-11
fxt.Init_msg_db("convert_php_tilde", "$1~u");
fxt.Test_parse_en("{{int:convert_php_tilde|a}}", "a~u");
}
@Test public void Unknown_val_returns_en() { // PURPOSE: if no "january" in "fr.gfs" and no "january/fr" in mw, use january in "en.gfs"; EX:none
fxt.Test_parse_lang("fr", "{{int:january/fr}}", "January");
}
@Test public void Unknown_lang_returns_en() { // PURPOSE: unknown lang default to english; EX:none; DATE:2014-05-09
fxt.Test_parse_en("{{int:january/unknown}}", "January");
}
@Test public void Transclude_mw_do_not_strip_lang() { // PURPOSE: if /lang matches wiki.lang, do not strip it, else stack overflow; EX:pl.d:Wikislownik:Bar/Archiwum_6; newarticletext/pl; DATE:2014-05-13
fxt.Init_msg_db("january/en", "January_en");
fxt.Test_parse_en("{{MediaWiki:january/en}}", "January_en");
}
@Test public void Transclude_gfs() { // PURPOSE: transclusion of {{MediaWiki}} pages should call {{int}} instead; EX:zh.w:Main_Page; DATE:2014-05-09
fxt.Test_parse_en("{{MediaWiki:january/en}}", "January"); // NOTE: no page exists called "MediaWiki:january/en", but returns "{{int:january/en}}" value
}
@Test public void Create_msg_in_wiki_not_lang() { // PURPOSE: if two wikis share same language and msg is missing from one, do not mark it missing in the other; EX: home/wiki/Main_Page and en.w:Main_Page; DATE:2014-05-13
Xow_wiki enwiktionary = fxt.Make_wiki("en.wiktionary.org");
fxt.Init_msg_db(enwiktionary, "wiki_only_msg", "enwiktionary_msg");
fxt.Init_msg_gfs("wiki_only_msg", "en_gfs_msg", false, false);
fxt.Test_parse_en("{{int:wiki_only_msg}}", "en_gfs_msg");
fxt.Test_parse_wiki(enwiktionary, "{{int:wiki_only_msg}}", "enwiktionary_msg");
}
}
class Pf_msg_mgr_fxt {
private Xop_fxt fxt;
private Xol_lang en_lang;
private Xow_wiki en_wiki;
public void Reset() {
fxt = new Xop_fxt(); // new fxt, else transclude tests will fail
en_wiki = fxt.Wiki();
en_lang = en_wiki.Lang();
}
public void Init_msg_gfs(String key, String val, boolean fmt, boolean tmpl) {Init_msg_gfs(en_lang, key, val, fmt, tmpl);}
public void Init_msg_gfs(Xol_lang lang, String key, String val, boolean fmt, boolean tmpl) {
Xol_msg_itm msg_itm = lang.Msg_mgr().Itm_by_key_or_new(Bry_.new_utf8_(key));
msg_itm.Atrs_set(Bry_.new_utf8_(val), fmt, tmpl);
}
public void Init_msg_db(String ttl, String val) {Init_msg_db(en_wiki, ttl, val);}
public void Init_msg_db(Xow_wiki wiki, String ttl, String val) {
fxt.Init_page_create(wiki, "MediaWiki:" + ttl, val);
}
public Xow_wiki Make_wiki(String domain) {return fxt.App().Wiki_mgr().Get_by_key_or_make(Bry_.new_utf8_(domain));}
public void Test_parse_en(String raw, String expd) {
fxt.Test_parse_tmpl_str_test(raw, "{{test}}" , expd);
}
public void Test_parse_wiki(Xow_wiki alt_wiki, String raw, String expd) {
Xop_fxt alt_fxt = new Xop_fxt(fxt.App(), alt_wiki);
alt_fxt.Test_parse_tmpl_str_test(raw, "{{test}}" , expd);
}
public void Test_parse_lang(String other_lang_id, String raw, String expd) {
Xol_lang other_lang = fxt.App().Lang_mgr().Get_by_key_or_new(Bry_.new_ascii_(other_lang_id));
other_lang.Init_by_load();
fxt.Page().Lang_(other_lang);
fxt.Test_parse_tmpl_str_test(raw, "{{test}}", expd);
fxt.Page().Lang_(en_lang); // NOTE: must reset back to en_lang, else rest of tests will look up under fr
}
}

View File

@@ -0,0 +1,34 @@
/*
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; import gplx.*;
class Pf_intl_language extends Pf_func_base {
@Override public int Id() {return Xol_kwd_grp_.Id_i18n_language;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_intl_language().Name_(name);}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {
byte[] argx = Eval_argx(ctx, src, caller, self);
Hash_adp_bry regy = Xol_lang_itm_.Regy();
if (argx.length == 0) return; // {{#language:}} should return ""; note that byte[0] will fail in MatchAtCurExact
Object o = regy.Get_by_bry(argx);
if (o == null)
bfr.Add(argx);
else {
Xol_lang_itm lang_itm = (Xol_lang_itm)o;
bfr.Add(lang_itm.Canonical_name());
}
}
}

View File

@@ -0,0 +1,30 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_intl_language_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void English() {fxt.Test_parse_tmpl_str_test("{{#language:en}}" , "{{test}}" , "English");}
@Test public void English_case() {fxt.Test_parse_tmpl_str_test("{{#language:eN}}" , "{{test}}" , "English");}
@Test public void Arabic() {fxt.Test_parse_tmpl_str_test("{{#language:ar}}" , "{{test}}" , "العربية");}
@Test public void Unknown() {fxt.Test_parse_tmpl_str_test("{{#language:unknown}}" , "{{test}}" , "unknown");}
@Test public void Foreign() {fxt.Test_parse_tmpl_str_test("{{#language:anp}}" , "{{test}}" , "अङ्गिका");}
@Test public void Foreign_2() {fxt.Test_parse_tmpl_str_test("{{#language:no}}" , "{{test}}" , "Norsk (bokmål)");} // PURPOSE: Names.php have bookend "pipes" (\xE2\x80\xAA)
@Test public void Empty() {fxt.Test_parse_tmpl_str_test("{{#language:}}" , "{{test}}" , "");}
}

View File

@@ -0,0 +1,31 @@
/*
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; import gplx.*;
class Pf_intl_plural extends Pf_func_base {
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {// REF.MW: CoreParserFunctions.php
byte[] number = Eval_argx(ctx, src, caller, self);
int self_args_len = self.Args_len();
int arg_idx = Pf_func_.Eq_(number, Ary_Num_1) ? 0 : 1;
if (arg_idx == 1 && self_args_len == 1) arg_idx = 0; // number is plural, but plural_arg not present; use singular; see test
byte[] word = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, self_args_len, arg_idx);
bfr.Add(word);
} static final byte[] Ary_Num_1 = new byte[] {Byte_ascii.Num_1};
@Override public int Id() {return Xol_kwd_grp_.Id_i18n_plural;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_intl_plural().Name_(name);}
}

View File

@@ -0,0 +1,27 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_intl_plural_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Singular() {fxt.Test_parse_tmpl_str_test("{{plural:1|wiki|wikis}}" , "{{test}}" , "wiki");}
@Test public void Plural() {fxt.Test_parse_tmpl_str_test("{{plural:2|wiki|wikis}}" , "{{test}}" , "wikis");}
@Test public void Plural_but_one_arg() {fxt.Test_parse_tmpl_str_test("{{plural:2|wiki}}" , "{{test}}" , "wiki");}
@Test public void Null() {fxt.Test_parse_tmpl_str_test("{{plural:|wiki|wikis}}" , "{{test}}" , "wikis");}
}

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; import gplx.*;
import gplx.php.*; import gplx.xowa.langs.*;
public class Pf_msg_mgr {
// public static String Get_msg_val_gui_or_null(Xol_lang lang, byte[] pre, byte[] key, byte[] suf) {
// String rv = Get_msg_val_gui_or_null(lang, pre, key, suf);
// return rv == null ? "<" + String_.new_utf8_(Bry_.Add(pre, key, suf)) + ">" : rv;
// }
public static String Get_msg_val_gui_or_empty(Xol_lang lang, byte[] pre, byte[] key, byte[] suf) { // get from lang, else get from en; does not use get_msg_val to skip db lookups; should only be used for gui; DATE:2014-05-28
String rv = Get_msg_val_gui_or_null(lang, pre, key, suf);
return rv == null ? "" : rv;
}
public static String Get_msg_val_gui_or(Xol_lang lang, byte[] pre, byte[] key, byte[] suf, String or) {
String rv = Get_msg_val_gui_or_null(lang, pre, key, suf);
return rv == null ? or : rv;
}
public static String Get_msg_val_gui_or_null(Xol_lang lang, byte[] pre, byte[] key, byte[] suf) { // get from lang, else get from en; does not use get_msg_val to skip db lookups; should only be used for gui; DATE:2014-05-28
byte[] msg_key = Bry_.Add(pre, key, suf);
Xol_msg_itm msg_itm = lang.Msg_mgr().Itm_by_key_or_null(msg_key);
if (msg_itm == null)
msg_itm = lang.App().Lang_mgr().Lang_en().Msg_mgr().Itm_by_key_or_null(msg_key);
return msg_itm == null ? null : String_.new_utf8_(msg_itm.Val());
}
public static byte[] Get_msg_val(Xow_wiki wiki, Xol_lang lang, byte[] msg_key, byte[][] fmt_args) {
Bry_bfr tmp_bfr = wiki.Utl_bry_bfr_mkr().Get_b512();
Xol_msg_itm msg_itm = Get_msg_itm(tmp_bfr, wiki, lang, msg_key);
byte[] rv = Get_msg_val(tmp_bfr, wiki, msg_itm, fmt_args);
tmp_bfr.Mkr_rls();
return rv;
} private static final byte[] Missing_bry = Bry_.new_ascii_("$"), Slash_bry = new byte[] {Byte_ascii.Slash};
public static byte[] Get_msg_val(Bry_bfr tmp_bfr, Xow_wiki wiki, Xol_msg_itm msg_itm, byte[][] fmt_args) {
byte[] msg_val = msg_itm.Val();
boolean has_fmt = msg_itm.Has_fmt_arg(), has_tmpl = msg_itm.Has_tmpl_txt();
if (!has_fmt && !has_tmpl) // no fmt or tmpl; just add val
return msg_val;
if (has_fmt) { // fmt exists; fmt first (before tmpl text); EX: Expression error: Unrecognised word "~{0}"
Bry_fmtr tmp_fmtr = Bry_fmtr.tmp_().Missing_bgn_(Missing_bry).Missing_end_(Bry_.Empty).Missing_adj_(1);
tmp_fmtr.Fmt_(msg_val);
tmp_fmtr.Bld_bfr(tmp_bfr, fmt_args);
msg_val = tmp_bfr.XtoAryAndClear();
}
if (has_tmpl) {
Xop_ctx sub_ctx = Xop_ctx.new_sub_(wiki); Xop_tkn_mkr tkn_mkr = sub_ctx.Tkn_mkr();
Xop_root_tkn sub_root = tkn_mkr.Root(msg_val);
msg_val = wiki.Parser().Parse_text_to_wtxt(sub_root, sub_ctx, tkn_mkr, msg_val);
}
return msg_val;
}
public static Xol_msg_itm Get_msg_itm(Bry_bfr tmp_bfr, Xow_wiki wiki, Xol_lang lang, byte[] msg_key) {
byte[] msg_key_sub_root = msg_key;
int slash_pos = Bry_finder.Find_bwd(msg_key, Byte_ascii.Slash);
if (slash_pos != Bry_.NotFound) { // key is of format "key/lang"; EX: "January/en"
int msg_key_len = msg_key.length;
if (slash_pos != msg_key_len) { // get text after slash; EX: "en"
Object o = Xol_lang_itm_.Regy().Get_by_mid(msg_key, slash_pos + 1, msg_key_len);
if (o != null) { // text is known lang_code;
Xol_lang_itm lang_itm = (Xol_lang_itm)o;
lang = wiki.App().Lang_mgr().Get_by_key_or_new(lang_itm.Key()); // set lang
}
msg_key_sub_root = Bry_.Mid(msg_key, 0, slash_pos); // set msg to "a" (discarding "/b")
}
}
Xol_msg_itm msg_in_wiki = wiki.Msg_mgr().Get_or_null(msg_key); // check wiki; used to be check lang, but Search_mediawiki should never be toggled on lang; DATE:2014-05-13
if (msg_in_wiki != null) return msg_in_wiki; // NOTE: all new msgs will Search_mediawiki once; EX: de.w:{{int:Autosumm-replace}}; DATE:2013-01-25
msg_in_wiki = wiki.Msg_mgr().Get_or_make(msg_key);
Xoa_page msg_page = Get_msg_itm_from_db(wiki, lang, msg_key, msg_key_sub_root);
byte[] msg_val = Bry_.Empty;
if (msg_page.Missing()) { // [[MediaWiki:key/fallback]] still not found; search "lang.gfs";
Xol_msg_itm msg_in_lang = Get_msg_itm_from_gfs(wiki, lang, msg_key_sub_root);
if (msg_in_lang == null) {
msg_val = tmp_bfr.Add_byte(Byte_ascii.Lt).Add(msg_key).Add_byte(Byte_ascii.Gt).XtoAryAndClear(); // set val to <msg_key>
msg_in_wiki.Src_(Xol_msg_itm.Src_missing);
}
else {
msg_val = msg_in_lang.Val();
msg_in_wiki.Src_(Xol_msg_itm.Src_lang);
}
}
else { // page found; dump entire contents
msg_val = gplx.xowa.apps.Xoa_gfs_php_mgr.Xto_gfs(tmp_bfr, msg_page.Data_raw()); // note that MediaWiki msg's use php arg format ($1); xowa.gfs msgs are already converted
msg_in_wiki.Src_(Xol_msg_itm.Src_wiki);
}
Xol_msg_itm_.update_val_(msg_in_wiki, msg_val);
return msg_in_wiki;
}
private static Xoa_page Get_msg_itm_from_db(Xow_wiki wiki, Xol_lang lang, byte[] msg_key, byte[] msg_key_sub_root) {
byte[] ns_bry = wiki.Ns_mgr().Ns_mediawiki().Name_db_w_colon();
Xoa_ttl ttl = Xoa_ttl.parse_(wiki, Bry_.Add(ns_bry, msg_key)); // ttl="MediaWiki:msg_key"; note that there may be "/lang"; EX:pl.d:Wikislownik:Bar/Archiwum_6 and newarticletext/pl
Xoa_page rv = ttl == null ? Xoa_page.Empty : wiki.Data_mgr().Get_page_from_msg(ttl);
if (rv.Missing()) { // [[MediaWiki:key]] not found; search for [[MediaWiki:key/fallback]]
byte[][] fallback_ary = lang.Fallback_bry_ary();
int fallback_ary_len = fallback_ary.length;
for (int i = 0; i < fallback_ary_len; i++) {
byte[] fallback = fallback_ary[i];
ttl = Xoa_ttl.parse_(wiki, Bry_.Add(ns_bry, msg_key_sub_root, Slash_bry, fallback)); // ttl="MediaWiki:msg_key/fallback"
rv = ttl == null ? Xoa_page.Empty : wiki.Data_mgr().Get_page_from_msg(ttl);
if (!rv.Missing()) break;
}
}
return rv;
}
private static Xol_msg_itm Get_msg_itm_from_gfs(Xow_wiki wiki, Xol_lang lang, byte[] msg_key_sub_root) {
Xol_msg_itm rv = lang.Msg_mgr().Itm_by_key_or_null(msg_key_sub_root); // NOTE: should always be msg_key_sub_root; EX: "msg/lang" will never be in lang.gfs
if (rv == null) { // msg not found; check fallbacks; note that this is different from MW b/c when MW constructs a lang, it automatically adds all fallback msgs to the current lang
byte[][] fallback_ary = lang.Fallback_bry_ary();
int fallback_ary_len = fallback_ary.length;
Xoa_lang_mgr lang_mgr = wiki.App().Lang_mgr();
for (int i = 0; i < fallback_ary_len; i++) {
byte[] fallback = fallback_ary[i];
Xol_lang fallback_lang = lang_mgr.Get_by_key(fallback);
if (fallback_lang == null) continue; // NOTE: en has fallback of "false"; ignore bad fallbacks;
rv = fallback_lang.Msg_mgr().Itm_by_key_or_null(msg_key_sub_root);
if (rv != null) break;
}
}
return rv;
}
}

View File

@@ -0,0 +1,39 @@
/*
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; import gplx.*;
class Pf_str_case extends Pf_func_base { // EX: {{lc:A}} -> a
public Pf_str_case(int case_type, boolean first) {this.case_type = case_type; this.first = first;}
@Override public int Id() {return Xol_kwd_grp_.Id_str_lc;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_str_case(case_type, first).Name_(name);}
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr trg) {
byte[] val_dat_ary = Eval_argx(ctx, src, caller, self); if (val_dat_ary == Bry_.Empty) return;
int val_dat_ary_len = val_dat_ary.length; if (val_dat_ary_len == 0) return; // nothing to uc / lc; just return
Xol_lang lang = ctx.Wiki().Lang();
boolean upper = case_type == Xol_lang.Tid_upper;
if (first) {
Bry_bfr tmp_bfr = ctx.App().Utl_bry_bfr_mkr().Get_b512();
val_dat_ary = lang.Case_mgr().Case_build_1st(tmp_bfr, upper, val_dat_ary, 0, val_dat_ary_len);
tmp_bfr.Mkr_rls();
}
else
val_dat_ary = lang.Case_mgr().Case_build(upper, val_dat_ary, 0, val_dat_ary_len);
trg.Add(val_dat_ary);
}
boolean first; int case_type;
}

View File

@@ -0,0 +1,46 @@
/*
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; import gplx.*;
import org.junit.*;
import gplx.xowa.langs.cases.*;
public class Pf_str_case_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Lc() {fxt.Test_parse_tmpl_str_test("{{lc:ABC}}" , "{{test}}", "abc");}
@Test public void Lc_first() {fxt.Test_parse_tmpl_str_test("{{lcfirst:ABC}}" , "{{test}}", "aBC");}
@Test public void Uc() {fxt.Test_parse_tmpl_str_test("{{uc:abc}}" , "{{test}}", "ABC");}
@Test public void Uc_first() {fxt.Test_parse_tmpl_str_test("{{ucfirst:abc}}" , "{{test}}", "Abc");}
@Test public void Multi_byte() {// NOTE: separate test b/c will sometimes fail in suite
Xol_lang lang = fxt.Wiki().Lang();
lang.Case_mgr().Clear();
lang.Case_mgr().Add_bulk(Xol_case_itm_.Universal);
fxt.Test_parse_tmpl_str_test("{{uc:ĉ}}" , "{{test}}", "Ĉ"); // upper all
}
@Test public void Multi_byte_asymmetric() {
Xol_lang lang = fxt.Wiki().Lang();
lang.Case_mgr().Clear();
lang.Case_mgr().Add_bulk(Xol_case_itm_.Universal);
fxt.Test_parse_tmpl_str_test("{{uc:ⱥ}}" , "{{test}}", "Ⱥ"); // handle multi-byte asymmetry (lc is 3 bytes; uc is 2 bytes)
}
@Test public void Multi_byte_first() {
Xol_lang lang = fxt.Wiki().Lang();
lang.Case_mgr().Clear();
lang.Case_mgr().Add_bulk(Xol_case_itm_.Universal);
fxt.Test_parse_tmpl_str_test("{{ucfirst:провинция}}" , "{{test}}", "Провинция"); // upper first; DATE:2014-02-04
}
}

View File

@@ -0,0 +1,60 @@
/*
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; import gplx.*;
class Pf_str_pad extends Pf_func_base {
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {// REF.MW: CoreParserFunctions.php|pad
int self_args_len = self.Args_len();
byte[] val = Eval_argx(ctx, src, caller, self);
int val_len = val.length;
byte[] pad_len = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, self_args_len, 0);
int pad_len_int = Bry_.X_to_int_or(pad_len, 0, pad_len.length, -1);
if (pad_len_int == -1) {bfr.Add(val); return;}// NOTE: if pad_len is non-int, add val and exit silently; EX: {{padleft: a|bad|0}}
if (pad_len_int > 500) pad_len_int = 500; // MW: force to be <= 500
byte[] pad_str = Pf_func_.Eval_arg_or(ctx, src, caller, self, self_args_len, 1, Ary_pad_dflt);
int pad_str_len = pad_str.length;
if (pad_str_len == 0) {bfr.Add(val); return;}// NOTE: pad_str is entirely empty or whitespace; add val and return; SEE:NOTE_1
int pad_idx = 0;
if (pad_dir_right) bfr.Add(val);
for (int val_idx = val_len; val_idx < pad_len_int; val_idx++) {
byte b = pad_str[pad_idx];
int b_len = gplx.intl.Utf8_.Len_of_char_by_1st_byte(b);
if (b_len == 1)
bfr.Add_byte(b);
else
bfr.Add_mid(pad_str, pad_idx, pad_idx + b_len);
pad_idx += b_len;
if (pad_idx >= pad_str_len) pad_idx = 0;
}
if (!pad_dir_right) bfr.Add(val);
}
boolean pad_dir_right; static final byte[] Ary_pad_dflt = Bry_.new_ascii_("0");
public Pf_str_pad(int id, boolean pad_dir_right) {this.id = id; this.pad_dir_right = pad_dir_right;}
@Override public int Id() {return id;} private int id;
@Override public Pf_func New(int id, byte[] name) {return new Pf_str_pad(id, pad_dir_right).Name_(name);}
}
/*
NOTE_1
difference between following:
- {{padleft:a|4}} -> 000a
Pad_arg omitted: default to 0
- {{padleft:a|4| \n\t}} -> a
Pad_arg specified, but empty: add val only
*/

View File

@@ -0,0 +1,36 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_str_pad_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void L_len_3() {fxt.Test_parse_tmpl_str_test("{{padleft: a|4|0}}" , "{{test}}" , "000a");}
@Test public void L_str_ab() {fxt.Test_parse_tmpl_str_test("{{padleft: a|4|01}}" , "{{test}}" , "010a");}
@Test public void L_len_neg1() {fxt.Test_parse_tmpl_str_test("{{padleft: a|-1|01}}" , "{{test}}" , "a");}
@Test public void L_val_null() {fxt.Test_parse_tmpl_str_test("{{padleft: |4|0}}" , "{{test}}" , "0000");}
@Test public void L_word_3() {fxt.Test_parse_tmpl_str_test("{{padleft: abc|4}}" , "{{test}}" , "0abc");}
@Test public void L_exc_len_bad1() {fxt.Test_parse_tmpl_str_test("{{padleft:a|bad|01}}" , "{{test}}" , "a");}
@Test public void L_exc_pad_ws() {fxt.Test_parse_tmpl_str_test("{{padleft:a|4|\n \t}}" , "{{test}}" , "a");}
@Test public void R_len_3() {fxt.Test_parse_tmpl_str_test("{{padright:a|4|0}}" , "{{test}}" , "a000");}
@Test public void R_str_ab() {fxt.Test_parse_tmpl_str_test("{{padright:a|4|01}}" , "{{test}}" , "a010");}
@Test public void R_str_intl() {fxt.Test_parse_tmpl_str_test("{{padright:|6|devanā}}" , "{{test}}" , "devanā");}
}
/*
{{padleft: a|4|0}}
*/

View File

@@ -0,0 +1,127 @@
/*
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; import gplx.*;
import gplx.xowa.html.*;
public class Pf_tag extends Pf_func_base {
@Override public int Id() {return Xol_kwd_grp_.Id_misc_tag;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_tag().Name_(name);}
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {
byte[] tag_name = Eval_argx(ctx, src, caller, self); if (tag_name.length == 0) return;
int args_len = self.Args_len();
Xoa_app app = ctx.App();
Bry_bfr tmp = app.Utl_bry_bfr_mkr().Get_b512();
try {
int tag_idx = ctx.Cur_page().Ref_mgr().Tag__next_id();
Xop_xnde_tag tag = (Xop_xnde_tag)app.Xnde_tag_regy().XndeNames(ctx.Xnde_names_tid()).MatchAtCurExact(tag_name, 0, tag_name.length);
boolean tag_is_ref = tag != null && tag.Id() == Xop_xnde_tag_.Tid_ref;
if (tag_is_ref) // <ref>; add <xtag_bgn> to handle nested refs; PAGE:en.w:Battle_of_Midway; DATE:2014-06-27
tmp.Add(Xtag_bgn_lhs).Add_int_pad_bgn(Byte_ascii.Num_0, 10, tag_idx).Add(Xtag_rhs);
tmp.Add_byte(Byte_ascii.Lt).Add(tag_name);
if (args_len > 1) {
for (int i = 1; i < args_len; i++) {
byte[] arg = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, args_len, i);
if (arg.length == 0) continue; // if atr is empty, skip; see test
tmp.Add_byte(Byte_ascii.Space);
if (!AddHtmlArg(arg, tmp)) {
ctx.Msg_log().Add_itm_none(Xop_tag_log.Invalid, arg, 0, arg.length);
tmp.Clear();
}
}
}
tmp.Add_byte(Byte_ascii.Gt);
if (args_len > 0) // TODO: trim should not be called on content; WHEN: adding src[] back to tmpl_eval
tmp.Add(Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, args_len, 0));
tmp.Add_byte(Byte_ascii.Lt).Add_byte(Byte_ascii.Slash).Add(tag_name).Add_byte(Byte_ascii.Gt);
if (tag_is_ref) // <ref>; add <xtag_end> to handle nested refs; PAGE:en.w:Battle_of_Midway; DATE:2014-06-27
tmp.Add(Xtag_end_lhs).Add_int_pad_bgn(Byte_ascii.Num_0, 10, tag_idx).Add(Xtag_rhs);
bfr.Add_bfr_and_clear(tmp);
}
finally {tmp.Mkr_rls();}
}
private boolean AddHtmlArg(byte[] src, Bry_bfr tmp) {
ParseKeyVal(src, kv_bldr);
//if (!kv_bldr.Valid()) return false;
if (kv_bldr.Key_bgn() != -1)
tmp.Add(Bry_.Mid(src, kv_bldr.Key_bgn(), kv_bldr.KeyEnd()));
if (kv_bldr.Val_bgn() != -1) {
if (kv_bldr.Key_bgn() != -1) {
tmp.Add_byte(Byte_ascii.Eq);
}
tmp.Add_byte(Byte_ascii.Quote);
tmp.Add(Bry_.Mid(src, kv_bldr.Val_bgn(), kv_bldr.Val_end()));
tmp.Add_byte(Byte_ascii.Quote);
}
kv_bldr.Clear();
return true;
} private KeyValBldr kv_bldr = new KeyValBldr();
private static void ParseKeyVal(byte[] src, KeyValBldr kv_bldr) {
int itm_bgn = -1, itm_end = -1, src_len = src.length;
boolean mode_is_key = true;
for (int i = 0; i < src_len; i++) {
byte b = src[i];
switch (b) {
case Byte_ascii.Eq:
if (mode_is_key) {
mode_is_key = false;
if (itm_end == -1) itm_end = i;
kv_bldr.Key_rng_(itm_bgn, itm_end);
itm_bgn = itm_end = -1;
}
break;
case Byte_ascii.Quote:
case Byte_ascii.Apos: // NOTE: quotes cannot be escaped; regx takes first two quotes; REF:MW:CoreParserFunctions.php|tagObj
if (itm_bgn == -1)
itm_bgn = i + 1;
else if (itm_end == -1)
itm_end = i;
break;
case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine:// NOTE: do not need to handle ws, b/c argBldr will trim it EX: {{#tag|a| b = c }}; " b " and " c " are automatically trimmed
break;
default:
if (itm_bgn == -1) itm_bgn = i;
break;
}
}
if (itm_end == -1) itm_end = src_len;
kv_bldr.Val_rng_(itm_bgn, itm_end);
}
public static final int
Xtag_len = 27 // <xtag_bgn id='1234567890'/>
, Xtag_bgn = 14 // <xtag_bgn id='
;
public static final byte[]
Xtag_bgn_lhs = Bry_.new_ascii_("<xtag_bgn id='")
, Xtag_end_lhs = Bry_.new_ascii_("<xtag_end id='")
, Xtag_rhs = Bry_.new_ascii_("'/>")
;
}
class KeyValBldr {
public int Key_bgn() {return key_bgn;} private int key_bgn;
public int KeyEnd() {return key_end;} private int key_end;
public KeyValBldr Key_rng_(int bgn, int end) {key_bgn = bgn; key_end = end; return this;}
public int Val_bgn() {return val_bgn;} private int val_bgn;
public int Val_end() {return val_end;} private int val_end;
public KeyValBldr Val_rng_(int bgn, int end) {val_bgn = bgn; val_end = end; return this;}
public boolean Valid() {
return key_bgn != -1 && key_end != -1 && val_bgn != -1 && val_end != -1 && key_bgn <= key_end && val_bgn <= val_end;
}
public void Clear() {
key_bgn = key_end = val_bgn = val_end = -1;
}
}

View File

@@ -0,0 +1,39 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_tag_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Basic() {fxt.Test_html_full_str("{{#tag:pre|a|id=b|style=c}}" , "<pre id=\"b\" style=\"c\">a</pre>");}
// @Test public void Missing_val() {fxt.ini_Msg(Mwl_tag_rsc._.Invalid).Test_parse_tmpl_str_test("{{#tag:pre|a|id=}}" , "{{test}}" , "");} // see {{Reflist|colwidth=30em}} -> <ref group=a>a</ref>{{#tag:references||group=}} -> ""
@Test public void Atr2_empty() {fxt.Test_html_full_str("{{#tag:pre|a|id=b|}}" , "<pre id=\"b\">a</pre>");} // see {{Reflist|colwidth=30em}} -> <ref group=a>a</ref>{{#tag:references||group=a|}} -> "<references group=a/>"
@Test public void Val_apos() {fxt.Test_html_full_str("{{#tag:pre|a|id='b'}}" , "<pre id=\"b\">a</pre>");}
@Test public void Val_quote() {fxt.Test_html_full_str("{{#tag:pre|a|id=\"b\"}}" , "<pre id=\"b\">a</pre>");}
@Test public void Ws_all() {fxt.Test_html_full_str("{{#tag:pre|a| id = b }}" , "<pre id=\"b\">a</pre>");}
@Test public void Ws_quoted() {fxt.Test_html_full_str("{{#tag:pre|a| id = ' b ' }}" , "<pre id=\"_b_\">a</pre>");}
@Test public void Err_bad_key() {fxt.Test_html_full_str("{{#tag:pre|a|id=val|b}}" , "<pre id=\"val\">a</pre>");} // PURPOSE: b was failing b/c id was larger and key_end set to 4 (whereas b was len=1)
// @Test public void Exc() {
// fxt.Test_parse_tmpl_str_test("{{#tag:ref|George Robertson announced in January 2003 that he would be stepping down in December.<ref> {{cite news|title =NATO Secretary General to Leave His Post in December After 4 Years |first = Craig | last = Smith | work = The New York Times | date = January 23, 2003| url = http://www.nytimes.com/2003/01/23/world/nato-secretary-general-to-leave-his-post-in-december-after-4-years.html?scp=2&sq=lord+robertson&st=nyt|accessdate = 2009-03-29}}</ref> Jaap de Hoop Scheffer was selected as his successor, but could not assume the office until January 2004 because of his commitment in the Dutch Parliament.<ref> {{cite news|title = Jaap de Hoop Scheffer | work = Newsmakers | issue = 1 | publisher = Thomson Gale | date = January 1, 2005}}</ref> Robertson was asked to extend his term until Scheffer was ready, but declined, so Minuto-Rizzo, the Deputy Secretary General, took over in the interim.<ref name =\"ncsd\" /> |group=N|}}"
// , "{{test}}" , "<pre id=\" b \">a</pre>");}
@Test public void Nested_tmpl() { // PURPOSE: nested template must get re-evaluated; EX:de.wikipedia.org/wiki/Freiburg_im_Breisgau; DATE:2013-12-18;
fxt.Init_page_create("Template:!", "|");
fxt.Init_page_create("Template:A", "{{#ifeq:{{{1}}}|expd|pass|fail}}");
fxt.Test_html_full_frag("{{#tag:ref|{{A{{!}}expd}}}}<references/>", "<span class=\"reference-text\">pass</span>");
}
}

View File

@@ -0,0 +1,103 @@
/*
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; import gplx.*;
import gplx.xowa.parsers.amps.*; import gplx.xowa.parsers.lnkes.*;
public class Pf_url_anchorencode extends Pf_func_base { // EX: {{anchorencode:a b}} -> a+b
@Override public boolean Func_require_colon_arg() {return true;}
@Override public int Id() {return Xol_kwd_grp_.Id_url_anchorencode;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_url_anchorencode().Name_(name);}
public static void Func_init(Xop_ctx ctx) {
if (anchor_ctx != null) return;// NOTE: called by Scrib_uri
encode_trie.Add(Byte_ascii.Colon, Bry_fmtr_arg_.byt_(Byte_ascii.Colon));
encode_trie.Add(Byte_ascii.Space, Bry_fmtr_arg_.byt_(Byte_ascii.Underline));
anchor_ctx = Xop_ctx.new_sub_(ctx.Wiki());
anchor_ctx.Para().Enabled_n_();
anchor_tkn_mkr = anchor_ctx.Tkn_mkr();
anchor_parser = ctx.Wiki().Utl_mgr().Anchor_encode_parser();
}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bfr) {
if (anchor_ctx == null) Func_init(ctx);
byte[] val_ary = Eval_argx(ctx, src, caller, self); if (val_ary == Bry_.Empty) return;
Anchor_encode(val_ary, bfr, ctx.App().Utl_bry_bfr_mkr().Get_b512().Mkr_rls());
}
public static void Anchor_encode(byte[] src, Bry_bfr bfr, Bry_bfr tmp_bfr) {
Xop_root_tkn root = anchor_ctx.Tkn_mkr().Root(src);
anchor_parser.Parse_wtxt_to_wdom(root, anchor_ctx, anchor_tkn_mkr, src, Xop_parser_.Doc_bgn_bos);
// anchor_parser.Parse_page_tmpl(root, anchor_ctx, anchor_tkn_mkr, src);
int subs_len = root.Subs_len();
for (int i = 0; i < subs_len; i++) {
Xop_tkn_itm sub = root.Subs_get(i);
Tkn(src, sub, root, i, tmp_bfr);
}
byte[] unencoded = tmp_bfr.XtoAryAndClear();
encoder.Encode(tmp_bfr, unencoded);
bfr.Add_bfr_and_clear(tmp_bfr);
}
private static Url_encoder encoder = Url_encoder.new_html_id_();
private static void Tkn(byte[] src, Xop_tkn_itm sub, Xop_tkn_grp grp, int sub_idx, Bry_bfr tmp_bfr) {
switch (sub.Tkn_tid()) {
case Xop_tkn_itm_.Tid_lnke: Lnke(src, (Xop_lnke_tkn)sub, tmp_bfr); break; // FUTURE: need to move number to lnke_tkn so that number will be correct/consistent?
case Xop_tkn_itm_.Tid_lnki: Lnki(src, (Xop_lnki_tkn)sub, tmp_bfr); break;
case Xop_tkn_itm_.Tid_apos: break; // noop
case Xop_tkn_itm_.Tid_xnde: Xnde(src, (Xop_xnde_tkn)sub, tmp_bfr); break;
case Xop_tkn_itm_.Tid_html_ncr: tmp_bfr.Add_utf8_int(((Xop_amp_tkn_num)sub).Val()); break;
case Xop_tkn_itm_.Tid_html_ref: tmp_bfr.Add_utf8_int(((Xop_amp_tkn_txt)sub).Char_int()); break;
case Xop_tkn_itm_.Tid_tmpl_invk:
Xot_invk_tkn invk_tkn = (Xot_invk_tkn)sub;
Arg_itm_tkn name_tkn = invk_tkn.Name_tkn().Key_tkn();
int name_ary_bgn = name_tkn.Src_bgn() + 1, name_ary_end = name_tkn.Src_end();
byte[] name_ary = Bry_.Mid(src, name_ary_bgn, name_ary_end); // + 1 to skip :
int name_ary_len = name_ary_end - name_ary_bgn;
if (name_ary_len > 0 && name_ary[0] == Byte_ascii.Colon) // has initial colon; EX: {{:a}
tmp_bfr.Add_mid(name_ary, 1, name_ary_len); // 1 to skip initial colon
else // regular tmpl; EX: {{a}}
tmp_bfr.Add(anchor_ctx.Wiki().Ns_mgr().Ns_template().Gen_ttl(name_ary));
break;
default: tmp_bfr.Add_mid(src, sub.Src_bgn_grp(grp, sub_idx), sub.Src_end_grp(grp, sub_idx)); break;
}
}
private static void Lnke(byte[] src, Xop_lnke_tkn lnke, Bry_bfr tmp_bfr) {
int subs_len = lnke.Subs_len();
for (int i = 0; i < subs_len; i++) {
Xop_tkn_itm lnke_sub = lnke.Subs_get(i);
tmp_bfr.Add_mid(src, lnke_sub.Src_bgn_grp(lnke, i), lnke_sub.Src_end_grp(lnke, i));
}
}
private static void Lnki(byte[] src, Xop_lnki_tkn lnki, Bry_bfr tmp_bfr) {
int src_end = lnki.Src_end();
int trg_end = lnki.Trg_tkn().Src_end();
if (trg_end == src_end - Xop_tkn_.Lnki_end_len) { // only trg
int trg_bgn = lnki.Trg_tkn().Src_bgn();
if (lnki.Ttl().ForceLiteralLink()) ++trg_bgn; // literal link; skip colon; EX: [[:a]] -> a
tmp_bfr.Add_mid(src, trg_bgn, trg_end);
}
else {
tmp_bfr.Add_mid(src, trg_end + 1, src_end - Xop_tkn_.Lnki_end_len); //+1 is len of pipe
}
}
private static void Xnde(byte[] src, Xop_xnde_tkn xnde, Bry_bfr tmp_bfr) {
int subs_len = xnde.Subs_len();
for (int i = 0; i < subs_len; i++) {
Tkn(src, xnde.Subs_get(i), xnde, i, tmp_bfr);
}
}
private static ByteTrieMgr_fast encode_trie = ByteTrieMgr_fast.cs_();
private static Xop_ctx anchor_ctx; static Xop_tkn_mkr anchor_tkn_mkr;
private static Xop_parser anchor_parser;
}

View File

@@ -0,0 +1,35 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_url_anchorencode_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Lnke() {fxt.Test_parse_tmpl_str_test("{{anchorencode:[irc://a b c]}}" , "{{test}}" , "b_c");}
@Test public void Apos_bold() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a ''b'' c}}" , "{{test}}" , "a_b_c");}
@Test public void Apos_1() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a 'b c}}" , "{{test}}" , "a_.27b_c");}
@Test public void Lnki_trg() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a [[b]] c}}" , "{{test}}" , "a_b_c");}
@Test public void Lnki_caption() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a [[b|c]] c}}" , "{{test}}" , "a_c_c");}
@Test public void Lnki_file() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a [[Image:b|thumb|c]] d}}" , "{{test}}" , "a_thumb.7Cc_d");}
@Test public void Xnde() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a <i>b</i> c}}" , "{{test}}" , "a_b_c");}
@Test public void Html_ncr() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a &#34; b}}" , "{{test}}" , "a_.22_b");}
@Test public void Html_ref() {fxt.Test_parse_tmpl_str_test("{{anchorencode:a &quot; b}}" , "{{test}}" , "a_.22_b");}
@Test public void Tmpl_missing_basic() {fxt.Test_parse_tmpl_str_test("{{anchorencode:{{a}}}}" , "{{test}}" , "Template:a");}
@Test public void Tmpl_missing_colon() {fxt.Test_parse_tmpl_str_test("{{anchorencode:{{:a}}}}" , "{{test}}" , "Template:A");}
@Test public void Lnki_literal() {fxt.Test_parse_tmpl_str_test("{{anchorencode:[[:a]]}}" , "{{test}}" , "a");}
}

View File

@@ -0,0 +1,52 @@
/*
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; import gplx.*;
import gplx.xowa.files.*;
public class Pf_url_filepath extends Pf_func_base {
@Override public boolean Func_require_colon_arg() {return true;}
@Override public int Id() {return Xol_kwd_grp_.Id_url_filepath;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_url_filepath().Name_(name);}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bb) {
byte[] val_ary = Eval_argx(ctx, src, caller, self); if (val_ary == Bry_.Empty) return;
Xow_wiki wiki = ctx.Wiki();
val_ary = Bry_.Add(Bry_file, val_ary);
Xoa_ttl ttl = Xoa_ttl.new_(wiki, ctx.App().Msg_log_null(), val_ary, val_ary, 0, val_ary.length); if (ttl == null) return; // text is not valid ttl; return;
Xoa_page page = Load_page(wiki, ttl);
if (page.Missing()) return; // page not found in commons; exit;
byte[] ttl_bry = page.Ttl().Page_url();
Xofw_file_finder_rslt tmp_rslt = wiki.File_mgr().Repo_mgr().Page_finder_locate(ttl_bry);
if (tmp_rslt .Repo_idx() == Byte_.MaxValue_127) return;
Xof_repo_itm trg_repo = wiki.File_mgr().Repo_mgr().Repos_get_at(tmp_rslt.Repo_idx()).Trg();
xfer_itm.Atrs_by_ttl(ttl_bry, Bry_.Empty); // redirect is empty b/c Get_page does all redirect lookups
byte[] url = url_bldr.Set_trg_html_(Xof_repo_itm.Mode_orig, trg_repo, ttl_bry, xfer_itm.Lnki_md5(), xfer_itm.Lnki_ext(), Xof_img_size.Size_null_deprecated, Xof_doc_thumb.Null, Xof_doc_page.Null).Xto_bry();
bb.Add(url);
} private static final byte[] Bry_file = Bry_.new_ascii_("File:");
private static final Xof_xfer_itm xfer_itm = new Xof_xfer_itm();
private static final Xof_url_bldr url_bldr = new Xof_url_bldr();
public static Xoa_page Load_page(Xow_wiki wiki, Xoa_ttl ttl) {
Xoa_page page = wiki.Data_mgr().Get_page(ttl, false);
if (page.Missing()) { // file not found in current wiki; try commons;
Xow_wiki commons_wiki = wiki.App().Wiki_mgr().Get_by_key_or_null(wiki.Commons_wiki_key());
if (commons_wiki != null) {; // commons_wiki not installed; exit; DATE:2013-06-08
if (!Env_.Mode_testing()) commons_wiki.Init_assert();// must assert load else page_zip never detected; TODO: move to Xoa_wiki_mgr.New_wiki; DATE:2013-03-10
page = commons_wiki.Data_mgr().Get_page(ttl, false);
}
}
return page;
}
}

View File

@@ -0,0 +1,54 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_url_filepath_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {
fxt.Reset();
Io_mgr._.InitEngine_mem();
Xoa_app app = fxt.App(); en_wiki = fxt.Wiki();
mock_wkr.Clear_commons(); // assume all files are in repo 0
en_wiki.File_mgr().Repo_mgr().Page_finder_(mock_wkr);
commons_wiki = Xoa_app_fxt.wiki_(app, Xow_wiki_.Domain_commons_str);
commons_wiki.Db_mgr().Load_mgr().Clear();
en_wiki.Db_mgr().Load_mgr().Clear();
app.Wiki_mgr().Add(commons_wiki);
app.File_mgr().Repo_mgr().Set("src_commons", "mem/xowa/file/commons/src/", commons_wiki.Domain_str()).Ext_rules_(Xoft_rule_grp.Grp_app_default);
app.File_mgr().Repo_mgr().Set("trg_commons", "mem/xowa/file/commons/trg/", commons_wiki.Domain_str()).Ext_rules_(Xoft_rule_grp.Grp_app_default);
en_wiki.File_mgr().Repo_mgr().Add_repo(Bry_.new_utf8_("src_commons"), Bry_.new_utf8_("trg_commons"));
Io_mgr._.CreateDir(Io_url_.new_dir_("mem/xowa/wiki/commons.wikimedia.org/ns/000/page/")); // HACK: create page_dir so Scan_dirs_zip will not identify commons as zipped; FIX: remove; WHEN: after redoing commons.css download logic
} private Xow_wiki en_wiki, commons_wiki; Xofw_wiki_wkr_mock mock_wkr = new Xofw_wiki_wkr_mock();
@Test public void Wiki_is_local() {
fxt.Init_page_create(en_wiki, "File:A.png", "");
mock_wkr.Redirect_("A.png", "A.png").Repo_idx_(0);
fxt.Test_parse_tmpl_str_test("{{filepath:A.png}}", "{{test}}", "file:///mem/wiki/repo/trg/orig/7/0/A.png");
}
@Test public void Wiki_is_commons() {
fxt.Init_page_create(commons_wiki, "File:A.png", "");
commons_wiki.Fsys_mgr().Dir_regy()[Xow_dir_info_.Tid_page].Ext_tid_(gplx.ios.Io_stream_.Tid_file);
mock_wkr.Redirect_("A.png", "A.png").Repo_idx_(1);
fxt.Test_parse_tmpl_str_test("{{filepath:A.png}}", "{{test}}", "file:///mem/xowa/file/commons/trg/orig/7/0/1/c/A.png");
}
@Test public void Not_found() {
fxt.Test_parse_tmpl_str_test("{{filepath:B.png}}", "{{test}}", "");
}
@Test public void Invalid() { // PURPOSE: handle invalid ttls; EX:w:Germicidin
fxt.Test_parse_tmpl_str_test("{{filepath:{{{ImageFile}}}}}", "{{test}}", "");
}
}

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; import gplx.*;
class Pf_url_ns extends Pf_func_base { // EX: {{ns:6}} -> File
public Pf_url_ns(boolean encode) {this.encode = encode; if (canonical == null) canonical_();}
@Override public int Id() {return Xol_kwd_grp_.Id_url_ns;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_url_ns(encode).Name_(name);}
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bb) {
byte[] val_dat_ary = Eval_argx(ctx, src, caller, self); if (val_dat_ary == Bry_.Empty) return;
int val_dat_ary_len = val_dat_ary.length;
int ns_id = Bry_.X_to_int_or(val_dat_ary, 0, val_dat_ary_len, -1);
if (ns_id == -1) {
Object o = ctx.Wiki().Ns_mgr().Names_get_or_null(val_dat_ary, 0, val_dat_ary_len);
if (o == null
&& !Bry_.Eq(ctx.Lang().Key_bry(), Xol_lang_.Key_en)) // foreign language; english canonical names are still valid; REF.MW: Language.php|getNsIndex
o = canonical.Get_by_mid(val_dat_ary, 0, val_dat_ary_len);
if (o != null) {
Xow_ns itm = (Xow_ns)o;
if (itm.Id() == Xow_ns_.Id_file) itm = ctx.Wiki().Ns_mgr().Ns_file(); // handles "Image" -> "File"
bb.Add(encode ? itm.Name_enc() : itm.Name_txt());
}
}
else {
Xow_ns itm = (Xow_ns)ctx.Wiki().Ns_mgr().Ids_get_or_null(ns_id);
if (itm == null) return; // occurs when ns_id is not known; EX: {{ns:999}}; SEE: Wiktionary:Grease pit archive/2007/October; "{{ns:114}}"
bb.Add(encode ? itm.Name_enc() : itm.Name_txt());
}
}
boolean encode;
private static Hash_adp_bry canonical;
private static void canonical_add(int ns_id, byte[] ns_name) {
Xow_ns ns = new Xow_ns(ns_id, Xow_ns_case_.Id_all, ns_name, false);
canonical.Add(ns_name, ns);
}
private static void canonical_() {
canonical = Hash_adp_bry.ci_();
for (Xow_ns ns : Xow_ns_.Canonical)
canonical_add(ns.Id(), ns.Name_bry());
}
}

View File

@@ -0,0 +1,37 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_url_ns_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Ns_0() {fxt.Test_parse_tmpl_str_test("{{ns:0}}" , "{{test}}", "");}
@Test public void Ns_10() {fxt.Test_parse_tmpl_str_test("{{ns:10}}" , "{{test}}", "Template");}
@Test public void Ns_11() {fxt.Test_parse_tmpl_str_test("{{ns:11}}" , "{{test}}", "Template talk");}
@Test public void Ns_11_ws() {fxt.Test_parse_tmpl_str_test("{{ns: 11 }}" , "{{test}}", "Template talk");}
@Test public void Ns_Template() {fxt.Test_parse_tmpl_str_test("{{ns:Template}}" , "{{test}}", "Template");}
@Test public void Ns_invalid() {fxt.Test_parse_tmpl_str_test("{{ns:252}}" , "{{test}}", "");}
@Test public void Nse_10() {fxt.Test_parse_tmpl_str_test("{{nse:10}}" , "{{test}}", "Template");}
@Test public void Nse_11() {fxt.Test_parse_tmpl_str_test("{{nse:11}}" , "{{test}}", "Template_talk");}
@Test public void Ns_Image() {fxt.Test_parse_tmpl_str_test("{{ns:Image}}" , "{{test}}", "File");}
@Test public void Ns_Templatex() {fxt.Test_parse_tmpl_str_test("{{ns:Templatex}}" , "{{test}}", "");}
@Test public void Ns_Talk() { // PURPOSE: non-English wikis may have parameterized Project Talk ($1 talk); swap out with ns:4; REF.MW: Language.php!fixVariableInNamespace
fxt.Wiki().Ns_mgr_(new Xow_ns_mgr().Add_new(4, "wiki").Add_new(5, "$1 talk").Add_new(10, "Template").Init());
fxt.Test_parse_tmpl_str_test("{{ns:5}}" , "{{test}}", "wiki talk");
}
}

View File

@@ -0,0 +1,29 @@
/*
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; import gplx.*;
class Pf_url_urlencode extends Pf_func_base { // EX: {{urlencode:a b}} -> a+b
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bb) {
byte[] val_ary = Eval_argx(ctx, src, caller, self); if (val_ary == Bry_.Empty) return;
ctx.App().Url_converter_url().Encode(urlEncodeBfr, val_ary);
bb.Add_bfr(urlEncodeBfr);
urlEncodeBfr.Clear();
} private Bry_bfr urlEncodeBfr = Bry_bfr.new_(128);
@Override public int Id() {return Xol_kwd_grp_.Id_url_urlencode;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_url_urlencode().Name_(name);}
}

View File

@@ -0,0 +1,30 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_url_urlencode_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Numbers() {fxt.Test_parse_tmpl_str_test("{{urlencode:0123456789}}" , "{{test}}", "0123456789");}
@Test public void Ltrs_lower() {fxt.Test_parse_tmpl_str_test("{{urlencode:abcdefghijklmnopqrstuvwxyz}}" , "{{test}}", "abcdefghijklmnopqrstuvwxyz");}
@Test public void Ltrs_upper() {fxt.Test_parse_tmpl_str_test("{{urlencode:ABCDEFGHIJKLMNOPQRSTUVWXYZ}}" , "{{test}}", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");}
@Test public void Syms_allowed() {fxt.Test_parse_tmpl_str_test("{{urlencode:-_.}}" , "{{test}}", "-_.");}
@Test public void Space() {fxt.Test_parse_tmpl_str_test("{{urlencode:a b}}" , "{{test}}", "a+b");}
@Test public void Syms() {fxt.Test_parse_tmpl_str_test("{{urlencode:!?^~:}}" , "{{test}}", "%21%3F%5E%7E%3A");}
@Test public void Extended() {fxt.Test_parse_tmpl_str_test("{{urlencode:aéb}}" , "{{test}}", "a%C3%A9b");}
}

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; import gplx.*;
public class Pf_url_urlfunc extends Pf_func_base { // EX: {{lc:A}} -> a
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bb) {
byte[] val_dat_ary = Eval_argx(ctx, src, caller, self); if (val_dat_ary.length == 0) return;
byte[] qry_arg = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, self.Args_len(), 0);
UrlString(ctx, tid, encode, val_dat_ary, bb, qry_arg);
}
public static void UrlString(Xop_ctx ctx, byte tid, boolean encode, byte[] src, Bry_bfr trg, byte[] qry_arg) {
Xow_wiki wiki = ctx.Wiki();
Xoa_ttl ttl = Xoa_ttl.new_(wiki, ctx.Msg_log(), src, src, 0, src.length);
if (ttl == null) return; // NOTE: guard against null ttl; EX: {{fullurl:{{transclude|{{{1}}}}}|action=edit}} -> {{fullurl:Template:{{{1}}}|action=edit}} -> Template:{{{1}}} will be a null ttl
byte[] ttl_ary = ttl.Full_url();// NOTE: Full_url does encoding; don't encode again
Xow_xwiki_itm xwiki = ttl.Wik_itm();
if (xwiki != null) { // xwiki exists; add as //commons.wikimedia.org/wiki/A#b?c=d
if (tid == Tid_canonical)
trg.Add(Xoh_href_parser.Href_http_bry); // "http://"
else
trg.Add(Xoa_consts.Url_relative_prefix); // "//"
trg.Add(xwiki.Domain()) // "commons.wikimedia.org"
.Add(Xoh_href_parser.Href_wiki_bry) // "/wiki/"
.Add_mid(ttl_ary, xwiki.Key().length + 1, ttl_ary.length); // "A#b?c=d"; +1 for colon after "commons:"; NOTE: ugly way of getting rest of url, but ttl currently does not have Full_wo_wiki
}
else {
Bry_bfr tmp_bfr = ctx.App().Utl_bry_bfr_mkr().Get_b512().Mkr_rls();
switch (tid) {
case Tid_local: tmp_bfr.Add(ctx.Wiki().Props().ArticlePath());break;
case Tid_full: tmp_bfr.Add(Bry_relative_url).Add(ctx.Wiki().Props().ServerName()).Add(ctx.Wiki().Props().ArticlePath()); break;
case Tid_canonical: tmp_bfr.Add(ctx.Wiki().Props().Server()).Add(ctx.Wiki().Props().ArticlePath()); break;
default: throw Err_.unhandled(tid);
}
tmp_bfr.Add(ttl_ary);
trg.Add_bfr_and_clear(tmp_bfr);
}
if (qry_arg != Bry_.Empty) trg.Add_byte(Byte_ascii.Question).Add(qry_arg);
}
public Pf_url_urlfunc(int id, byte tid, boolean encode) {this.id = id; this.tid = tid; this.encode = encode;} private byte tid; boolean encode;
@Override public int Id() {return id;} private int id;
@Override public Pf_func New(int id, byte[] name) {return new Pf_url_urlfunc(id, tid, encode).Name_(name);}
public static final byte Tid_local = 0, Tid_full = 1, Tid_canonical = 2;
public static final byte[] Bry_relative_url = Bry_.new_ascii_("//");
}
/*
NOTE: Both fullurle: and localurle: performed additional character escaping on the resulting link, but no example is known where that still has any additional effect.
http://meta.wikimedia.org/wiki/Help:Parser_function
*/

View File

@@ -0,0 +1,42 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_url_urlfunc_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Localurl() {fxt.Test_parse_tmpl_str_test("{{localurl:a&b! c}}" , "{{test}}", "/wiki/A%26b!_c");}
@Test public void Fullurl() {fxt.Test_parse_tmpl_str_test("{{fullurl:a&b! c}}" , "{{test}}", "//en.wikipedia.org/wiki/A%26b!_c");}
@Test public void Canonicalurl() {fxt.Test_parse_tmpl_str_test("{{canonicalurl:a&b! c}}" , "{{test}}", "http://en.wikipedia.org/wiki/A%26b!_c");}
@Test public void Canonicalurl_case() {fxt.Test_parse_tmpl_str_test("{{CANONICALURL:a&b! c}}" , "{{test}}", "http://en.wikipedia.org/wiki/A%26b!_c");}
@Test public void Localurle() {fxt.Test_parse_tmpl_str_test("{{localurle:a&b! c}}" , "{{test}}", "/wiki/A%26b!_c");}
@Test public void Fullurle() {fxt.Test_parse_tmpl_str_test("{{fullurle:a&b! c}}" , "{{test}}", "//en.wikipedia.org/wiki/A%26b!_c");}
@Test public void Canonicalurle() {fxt.Test_parse_tmpl_str_test("{{canonicalurle:a&b! c}}" , "{{test}}", "http://en.wikipedia.org/wiki/A%26b!_c");}
@Test public void Fullurl_arg() {fxt.Test_parse_tmpl_str_test("{{fullurle:a&b! c|action=edit}}" , "{{test}}", "//en.wikipedia.org/wiki/A%26b!_c?action=edit");}
@Test public void Random() {fxt.Test_parse_tmpl_str_test("{{fullurle:a&b! c|action=edit}}" , "{{test|a|b|c}}", "//en.wikipedia.org/wiki/A%26b!_c?action=edit");}
@Test public void Xwiki() {
fxt.Wiki().Xwiki_mgr().Add_full("commons", "commons.wikimedia.org");
fxt.Reset().Test_parse_tmpl_str_test("{{localurl:commons:A}}" , "{{test}}", "//commons.wikimedia.org/wiki/A");
fxt.Reset().Test_parse_tmpl_str_test("{{fullurl:commons:A}}" , "{{test}}", "//commons.wikimedia.org/wiki/A");
fxt.Reset().Test_parse_tmpl_str_test("{{canonicalurl:commons:A}}" , "{{test}}", "http://commons.wikimedia.org/wiki/A");
}
@Test public void Xwiki_qarg_fix() {
fxt.Wiki().Xwiki_mgr().Add_full("commons", "commons.wikimedia.org");
fxt.Reset().Test_parse_tmpl_str_test("{{fullurl:commons:A|key=val}}" , "{{test}}", "//commons.wikimedia.org/wiki/A?key=val");
}
}

View File

@@ -0,0 +1,182 @@
/*
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; import gplx.*;
public class Pf_xtn_rel2abs extends Pf_func_base {
@Override public boolean Func_require_colon_arg() {return true;}
private static final byte[] Ary_dot_slash = Bry_.new_ascii_("./"), Ary_dot_dot = Bry_.new_ascii_(".."), Ary_dot_dot_slash = Bry_.new_ascii_("../");
private static void qry_bgns_with_init() {
qry_bgns_with = ByteTrieMgr_fast.cs_();
qry_bgns_with.Add(Byte_ascii.Slash, Int_obj_ref.new_(Id_slash));
qry_bgns_with.Add(Byte_ascii.Dot, Int_obj_ref.new_(Id_dot));
qry_bgns_with.Add(Ary_dot_slash, Int_obj_ref.new_(Id_dot_slash));
qry_bgns_with.Add(Ary_dot_dot, Int_obj_ref.new_(Id_dot_dot));
qry_bgns_with.Add(Ary_dot_dot_slash, Int_obj_ref.new_(Id_dot_dot_slash));
} static ByteTrieMgr_fast qry_bgns_with;
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bb) {// REF.MW:ParserFunctions_body.php
byte[] qry = Eval_argx(ctx, src, caller, self);
byte[] orig = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, self.Args_len(), 0);
if (orig.length == 0) orig = ctx.Cur_page().Ttl().Full_txt();
bb.Add(Rel2abs(ctx.App().Utl_bry_bfr_mkr().Get_b512().Mkr_rls(), qry, orig));
}
public static boolean Rel2abs_ttl(byte[] ttl, int bgn, int end) {
int last = end - 1;
if (end - bgn > Xoa_ttl.Max_len) return false; // NOTE: some tmpls have long #if statements; quicker to fail here than wait for invalid char below
boolean rv = false;
for (int i = bgn; i < end; ++i) {
switch (ttl[i]) {
case Byte_ascii.Gt: case Byte_ascii.Pipe: // simplified version of Xoa_ttl parse; note that Xoa_ttl accepts these if anchor is seen; this proc assumes that anything with anchor and invalid char is an invalid rel2abs; EX: "A../b#c[d" is not valid; DATE:2013-03-31
case Byte_ascii.Brack_bgn: case Byte_ascii.Brack_end: case Byte_ascii.Curly_bgn: case Byte_ascii.Curly_end:
return false;
case Byte_ascii.Slash:
if ( !rv &&
( (i == bgn) // bgns with "/"
|| (i > bgn && ttl[i - 1] == Byte_ascii.Dot) // "./"
|| (i < last && ttl[i + 1] == Byte_ascii.Dot) // "/."
)
) rv = true;
break;
}
}
return rv;
}
private static final Int_obj_ref ignore_rel2abs_tid = Int_obj_ref.zero_();
public static byte[] Rel2abs(Bry_bfr tmp_bfr, byte[] qry, byte[] src) {return Rel2abs(tmp_bfr, qry, src, ignore_rel2abs_tid);}
public static byte[] Rel2abs(Bry_bfr tmp_bfr, byte[] qry, byte[] src, Int_obj_ref rel2abs_tid) {
if (qry_bgns_with == null) qry_bgns_with_init();
int qry_len = qry.length, src_len = src.length;
// qry_len = RTrim(qry, Byte_ascii.Slash, qry_bgn, qry_len); // not needed, but test anyway
if (qry_len == 0) return src;// no qry; return src; EX:{{#rel2abs:|a/b}} -> a/b
byte[] tmp = src;
int tmp_adj = 0, i = 0, prv_slash_end = 0, tmp_len = src_len, seg_pos = 0;
boolean tmp_is_1st = true;
Object o = qry_bgns_with.MatchAtCur(qry, 0, qry_len); // check if qry begins with ".", "/", "./", "../"; if it doesn't return;
if (o != null) {
int id = ((Int_obj_ref)o).Val();
rel2abs_tid.Val_(id);
switch (id) {
case Id_dot: // "."
break;
case Id_slash: // "/"
case Id_dot_slash: // "./"
case Id_dot_dot_slash: // "../"
break; // qry is relative to src; noop
case Id_dot_dot: // ".."
int match_end = qry_bgns_with.Match_pos();
if (match_end < qry_len && qry[match_end] == Byte_ascii.Dot) // NOTE: handles "..."; if "...*" then treat as invalid and return; needed for en.wiktionary.org/wiki/Wiktionary:Requests for cleanup/archive/2006
return qry;
break;
default:
return qry; // NOTE: lnki parsing currently has fuzzy logic to try to detect rel2abs; for now, if false match, then return original
}
}
else { // qry is not relative to src; src is ignored; EX:{{#rel2abs:c|a/b}} -> c
src_len = 0;
tmp = qry;
tmp_len = qry_len;
tmp_is_1st = false;
}
// create segs; see NOTE_1 for approach
byte b = Byte_.Zero;
boolean loop = true, dot_mode = true;
while (loop) {
if (i == tmp_len) { // finished an ary (either src or qry)
if (tmp_is_1st) { // finished src; EX: A/b
tmp_is_1st = false;
tmp = qry;
tmp_adj = src_len + 1;
tmp_len = src_len + 1 + qry_len;
}
else // finished qry; EX: ../c
loop = false;
b = Byte_ascii.Slash; // fake a slash between ary and src
}
else // inside ary
b = tmp[i - tmp_adj];
switch (b) {
case Byte_ascii.Dot: // "."; ignore; note that dot_mode defaults to true
break;
default: // something else besides dot or slash; reset dot_mode
dot_mode = false;
break;
case Byte_ascii.Slash: // "/"; seg finished
int seg_len = i - prv_slash_end; // EX: "a/b/c" prv_slash_end = 2; i = 3; len = 1
boolean create_seg = false;
switch (seg_len) {
case 0: // "//"; ignore
break;
case 1: // "/?/"
if (dot_mode) {} // "/./"; current seg; ignore;
else create_seg = true; // something else (EX: /A/); create seg;
break;
case 2:
if (dot_mode) { // "/../"; pop seg_ary
seg_pos -= 2;
if (seg_pos < 0) return Bry_.Empty; // FUTURE: return MediaWiki error
}
else create_seg = true; // something else; create seg
break;
default: // something else; create seg
create_seg = true;
break;
}
if (create_seg) {
seg_ary[seg_pos++] = prv_slash_end;
seg_ary[seg_pos++] = i;
}
prv_slash_end = i + 1; // +1: place after slash
dot_mode = true; // reset dot_mode
break;
}
i++;
}
// write segs
tmp = src; tmp_adj = 0; tmp_is_1st = true; tmp_len = src_len;
if (src_len == 0) {
tmp = qry;
tmp_len = qry_len;
tmp_is_1st = false;
}
for (int j = 0; j < seg_pos; j += 2) {
if (j != 0) tmp_bfr.Add_byte(Byte_ascii.Slash);
if (seg_ary[j] >= tmp_len) {
tmp = qry;
tmp_adj = src_len + 1;
}
tmp_bfr.Add_mid(tmp, seg_ary[j] - tmp_adj, seg_ary[j+1] - tmp_adj);
}
return tmp_bfr.XtoAryAndClear();
}
private static int[] seg_ary = new int[Xoa_ttl.Max_len];
@Override public int Id() {return Xol_kwd_grp_.Id_xtn_rel2abs;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_xtn_rel2abs().Name_(name);}
public static final int Id_null = 0, Id_slash = 1, Id_dot = 2, Id_dot_slash = 3, Id_dot_dot = 4, Id_dot_dot_slash = 5;
}
/*
NOTE_1:approach (easiest explained with an example)
given qry = "../C/./D" and src = "A/B"
. combine two into a pseudo-array: "A/B/../C/./D"
. iterate over every slash to create "segs_ary"
A -> [A] add
B -> [A, B] add
.. -> [A] pop
C -> [A, C] add
. -> [A, C] noop
D -> [A, C, D] add
*/

View File

@@ -0,0 +1,68 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_xtn_rel2abs_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Slash_lvl3() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/d|a/b/c}}" , "{{test}}" , "a/b/c/d");}
@Test public void Cur_lvl1() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:./d|a}}" , "{{test}}" , "a/d");}
@Test public void Cur_lvl2() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:./d|a/b}}" , "{{test}}" , "a/b/d");}
@Test public void Cur_lvl3() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:./d|a/b/c}}" , "{{test}}" , "a/b/c/d");}
@Test public void Owner_lvl3() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:../d|a/b/c}}" , "{{test}}" , "a/b/d");}
@Test public void Owner_cur_lvl3() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:../.|a/b/c}}" , "{{test}}" , "a/b");}
@Test public void Text_lvl3() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:d|a/b/c}}" , "{{test}}" , "d");}
@Test public void Slash_mult() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/d//e|a/b/c}}" , "{{test}}" , "a/b/c/d/e");}
@Test public void Slash_cur_mult() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/d/./e|a/b/c}}" , "{{test}}" , "a/b/c/d/e");}
@Test public void Qry_ends_w_slash() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/d/|a/b/c}}" , "{{test}}" , "a/b/c/d");}
@Test public void Qry_is_empty() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:|a/b/c}}" , "{{test}}" , "a/b/c");}
@Test public void Qry_is_dot() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:.|a/b/c}}" , "{{test}}" , "a/b/c");}
@Test public void DotDot_mult2() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/../..|a/b/c}}" , "{{test}}" , "a");}
@Test public void DotDot_mult3() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/../../..|a/b/c}}" , "{{test}}" , "");}
@Test public void Src_is_empty() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/d|}}" , "{{test}}" , "Test page/d");}
@Test public void Err_owner() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:..}}" , "{{test}}" , "");} // PURPOSE.fix: should not fail
@Test public void Err_owner_2() {fxt.Test_parse_tmpl_str_test("{{#rel2abs:/../../b|a}}" , "{{test}}" , "");} // PURPOSE.fix: should not fail
@Test public void Ns_should_be_included_for_cur_page() {// PURPOSE.fix: current title was not returning ns; EX: de.wikipedia.org/wiki/Hilfe:Vorlagenprogrammierung#Funktion_rel2abs
fxt.Page_ttl_("Help:A"); // set page to title with namespace
fxt.Test_parse_tmpl_str_test("{{#rel2abs:.}}" , "{{test}}" , "Help:A");
}
@Test public void Owner_lvl0() {// PURPOSE.fix: old rel2abs was producing "/c"; EX: de.wikipedia.org/wiki/Hilfe:Vorlagenprogrammierung#Funktion_rel2abs
fxt.Test_parse_tmpl_str_test("{{#rel2abs:../c|a}}" , "{{test}}" , "c");
}
@Test public void Rel2abs_slash() {
fxt.Page_ttl_("Page_1");
fxt.Init_defn_clear();
fxt.Init_defn_add("test" , "{{/B}}");
fxt.Init_page_create("Page_1/B" , "Page_1/B text");
fxt.Test_parse_tmpl_str("{{test}}" , "Page_1/B text");
fxt.Init_defn_clear();
}
@Test public void Rel2abs_dot() {
fxt.Page_ttl_("Page_1/A");
fxt.Init_defn_clear();
fxt.Init_defn_add("test" , "{{../C}}");
fxt.Init_page_create("Page_1/C" , "Page_1/C text");
fxt.Test_parse_tmpl_str("{{test}}" , "Page_1/C text");
fxt.Init_defn_clear();
}
@Test public void Rel2abs_ttl() {
Tst_rel2abs_ttl("a../b", true);
Tst_rel2abs_ttl("a../[b", false);
}
private void Tst_rel2abs_ttl(String raw, boolean expd) {Tfds.Eq(expd, Pf_xtn_rel2abs.Rel2abs_ttl(Bry_.new_ascii_(raw), 0, String_.Len(raw)));}
}

View File

@@ -0,0 +1,80 @@
/*
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; import gplx.*;
import gplx.core.ints.*; import gplx.xowa.xtns.pfuncs.times.*;
class Pf_xtn_titleparts extends Pf_func_base {
@Override public boolean Func_require_colon_arg() {return true;}
@Override public void Func_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Xot_invk self, Bry_bfr bb) {// REF.MW:ParserFunctions_body.php
// get argx
int args_len = self.Args_len();
byte[] argx = Eval_argx(ctx, src, caller, self); if (argx == null) return; // no argx; return empty
Xoa_ttl argx_as_ttl = Xoa_ttl.new_(ctx.Wiki(), ctx.App().Msg_log_null(), argx, argx, 0, argx.length); // transform to title in order to upper first, replace _, etc..
if (argx_as_ttl == null) {bb.Add(argx); return;} // NOTE: argx_as_ttl will be null if invalid, such as [[a|b]]; EX.WP:owl and {{taxobox/showtaxon|Dinosauria}}
else argx = argx_as_ttl.Full_txt();
// get parts_len
byte[] parts_len_ary = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, args_len, 0);
int parts_len = parts_len_ary == Bry_.Empty ? Int_.MinValue : Bry_.X_to_int_or(parts_len_ary, Int_.MaxValue);
if (parts_len == Int_.MaxValue) {// len is not an int; EX: "a";
ctx.Msg_log().Add_itm_none(Pf_xtn_titleparts_log.Len_is_invalid, src, caller.Src_bgn(), caller.Src_end());
bb.Add(argx);
return;
}
// get parts_bgn
byte[] parts_bgn_arg = Pf_func_.Eval_arg_or_empty(ctx, src, caller, self, args_len, 1);
int parts_bgn = parts_bgn_arg == Bry_.Empty ? 0 : Bry_.X_to_int_or(parts_bgn_arg, Int_.MinValue);
if (parts_bgn == Int_.MinValue) {// parts_bgn is not an int; EX: "a"
ctx.Msg_log().Add_itm_none(Pf_xtn_titleparts_log.Bgn_is_invalid, src, caller.Src_bgn(), caller.Src_end());
parts_bgn = 0; // NOTE: do not return
}
else if (parts_bgn > 0) parts_bgn -= ListAdp_.Base1; // adjust for base1
bb.Add(TitleParts(argx, parts_len, parts_bgn));
}
private byte[] TitleParts(byte[] src, int parts_len, int parts_bgn) {
// find dlm positions; EX: ab/cde/f/ will have -1,2,6,8
int src_len = src.length; int dlms_ary_len = 1; // 1 b/c dlms_ary[0] will always be -1
for (int i = 0; i < src_len; i++) {
if (src[i] == Byte_ascii.Slash) dlms_ary[dlms_ary_len++] = i;
}
dlms_ary[dlms_ary_len] = src_len; // put src_len into last dlms_ary; makes dlms_ary[] logic easier
// calc bgn_idx; must occur before adjust parts_len
int bgn_idx = parts_bgn > -1 ? parts_bgn : parts_bgn + dlms_ary_len; // negative parts_bgn means calc from end of dlms_ary_len; EX a/b/c|1|-1 means start from 2
if ( bgn_idx < 0 // bgn_idx can be negative when parts_len is negative and greater than array; EX: {{#titleparts:a/b|-1|-2}} results in dlms_ary_len of 1 and parts_bgn of -2 which will be parts_bgn of -1
|| bgn_idx > dlms_ary_len) return Bry_.Empty; // if bgn > len, return ""; EX: {{#titleparts:a/b|1|3}} should return ""
// adjust parts_len for negative/null
if (parts_len == Int_.MinValue) parts_len = dlms_ary_len; // no parts_len; default to dlms_ary_len
else if (parts_len < 0) { // neg parts_len; shorten parts now and default to rest of String; EX: a/b/c|-1 -> makes String a/b/c and get 2 parts
dlms_ary_len += parts_len;
parts_len = dlms_ary_len;
if (parts_len < 1) return Bry_.Empty; // NOTE: if zerod'd b/c of neg length, return empty; contrast with line below; EX: a/b/c|-4
}
if (parts_len == 0) return src; // if no dlms, return orig
// calc idxs for extraction
int bgn_pos = dlms_ary[bgn_idx] + 1; // +1 to start after slash
int end_idx = bgn_idx + parts_len;
int end_pos = end_idx > dlms_ary_len ? dlms_ary[dlms_ary_len] : dlms_ary[end_idx];
if (end_pos < bgn_pos) return Bry_.Empty;
return Bry_.Mid(src, bgn_pos, end_pos);
} static int[] dlms_ary = new Int_ary_bldr(255).Set(0, -1).Xto_int_ary(); // set pos0 to -1; makes +1 logic easier
@Override public int Id() {return Xol_kwd_grp_.Id_xtn_titleparts;}
@Override public Pf_func New(int id, byte[] name) {return new Pf_xtn_titleparts().Name_(name);}
}

View File

@@ -0,0 +1,81 @@
/*
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; import gplx.*;
import org.junit.*;
public class Pf_xtn_titleparts_tst {
private Xop_fxt fxt = new Xop_fxt();
@Before public void init() {fxt.Reset();}
@Test public void Len1() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|1}}" , "{{test}}", "A");}
@Test public void Len2() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|2}}" , "{{test}}", "A/b");}
@Test public void Len2_Bgn2() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|2|2}}" , "{{test}}", "b/c");}
@Test public void LenNeg1() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|-1}}" , "{{test}}", "A/b/c");}
@Test public void LenNeg1Bgn2() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|-1|2}}" , "{{test}}", "b/c");}
@Test public void Exc_len0() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|0}}" , "{{test}}", "A/b/c/d");}
@Test public void Exc_lenNeg4() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|-4}}" , "{{test}}", "");}
@Test public void Exc_lenNeg5() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|-5}}" , "{{test}}", "");}
@Test public void Exc_no_dlm() {fxt.Test_parse_tmpl_str_test("{{#titleparts:abcd|1}}" , "{{test}}", "Abcd");}
@Test public void Exc_parts0() {fxt.Test_parse_tmpl_str_test("{{#titleparts:abcd|0}}" , "{{test}}", "Abcd");}
@Test public void Exc_many_segs() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b|1|4}}" , "{{test}}", "");} // PURPOSE: bgn=4, len=1 requested; only bgn=1,2 available
@Test public void Exc_bad_len() {fxt.Init_log_(Pf_xtn_titleparts_log.Len_is_invalid).Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|bad}}" , "{{test}}", "A/b/c/d");}
@Test public void Exc_bad_bgn() {fxt.Init_log_(Pf_xtn_titleparts_log.Bgn_is_invalid).Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d|1|bad}}" , "{{test}}", "A");}
@Test public void Exc_1() {fxt.Test_parse_tmpl_str_test("{{#titleparts:50|1|1}}" , "{{test}}", "50");}
@Test public void Exc_2() {fxt.Test_parse_tmpl_str_test("{{#titleparts:50|1|2}}" , "{{test}}", "");}
@Test public void Lnki() {fxt.Test_parse_tmpl_str_test("{{#titleparts:[[a|b]]|1}}" , "{{test}}", "[[a|b]]");} // PURPOSE: invalid title was not being rendered
@Test public void Invalid_table() {// PURPOSE: Template:Taxobox/showtaxon calls #titleparts on table fragments; do not remove new lines; EX.WP:Owl
fxt.Init_defn_clear();
fxt.Init_defn_add("!", "|");
fxt.Init_defn_add("!!", "||");
fxt.Test_parse_tmpl_str_test
( String_.Concat_lines_nl_skip_last
( "{|"
, "|-"
, "{{#titleparts:"
, "{{!}} Order: {{!!}} '''Strigiformes'''"
, "{{!}}-|1}}"
, "|}"
)
, "{{test}}"
, String_.Concat_lines_nl_skip_last
( "" // NOTE: don't know why this new line is necessary, but hopefully it is benign
, "{|"
, "|-"
, "| Order: || '''Strigiformes'''"
, "|-"
, "|}"
)
);
}
@Test public void Invalid_xml() {// PURPOSE: handle calls like {{#titleparts:a<span id='b'>c</span>d|1}}; EX.WP:Triceratops
fxt.Test_parse_tmpl_str_test
( "{{#titleparts:a<span id='b'>c</span>d/e|1}}"
, "{{test}}"
, "a<span id='b'>c</span>d/e"
);
}
@Test public void Bgn_neg_1() {fxt.Test_parse_tmpl_str_test("{{#titleparts:a/b/c/d||-1}}" , "{{test}}", "d");}
@Test public void Bgn_neg_2() { // PURPOSE: EX: Wikisource:Requests for comment/Annotations and derivative works; DATE:2013-12-19
fxt.Test_parse_tmpl_str_test
( "{{#titleparts:a/b|-1|-2}}"
, "{{test}}"
, "A"
);
}
}