diff --git a/100_core/src/gplx/Double_.java b/100_core/src/gplx/Double_.java index 18f12e217..841ccf272 100644 --- a/100_core/src/gplx/Double_.java +++ b/100_core/src/gplx/Double_.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,40 +13,43 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx; +package gplx; + public class Double_ { - public static final String Cls_val_name = "double"; - public static final Class Cls_ref_type = Double.class; - public static final double - MinValue = Double.MIN_VALUE - , NaN = Double.NaN - , Inf_pos = Double.POSITIVE_INFINITY - ; - public static final byte[] - NaN_bry = Bry_.new_a7("NaN") - , Inf_pos_bry = Bry_.new_a7("INF") - ; - public static boolean IsNaN(double v) {return Double.isNaN(v);} - public static double cast(Object o) {try {return (Double)o;} catch(Exception e) {throw Err_.new_type_mismatch_w_exc(e, double.class, o);}} - public static double parse(String raw) {try {return Double.parseDouble(raw);} catch(Exception e) {throw Err_.new_parse_exc(e, double.class, raw);}} - public static double parse_or(String raw, double v) {try {return Double.parseDouble(raw);} catch(Exception e) {Err_.Noop(e); return v;}} - public static double coerce_(Object v) { - try {String s = String_.as_(v); return s == null ? Double_.cast(v) : Double_.parse(s);} - catch (Exception e) {throw Err_.new_cast(e, double.class, v);} - } - public static String To_str(double v) { - int v_int = (int)v; - return v - v_int == 0 ? Int_.To_str(v_int) : Double.toString(v); - } - public static String To_str_loose(double v) { - int v_as_int = (int)v; - return v == v_as_int - ? Int_.To_str(v_as_int) // convert to int, and call print String to eliminate any trailing decimal places - : Float_.To_str((float)v); // calling ((float)v).toString is better at removing trailing 0s than String.format("%g", v). note that .net .toString() handles it better; EX:2449.600000000000d; DATE:2014-07-29 - } - public static int Compare(double lhs, double rhs) { - if (lhs == rhs) return CompareAble_.Same; - else if (lhs < rhs) return CompareAble_.Less; - else return CompareAble_.More; - } -} + public static final String Cls_val_name = "double"; + public static final Class Cls_ref_type = Double.class; + public static final double + MinValue = Double.MIN_VALUE + , NaN = Double.NaN + , Inf_pos = Double.POSITIVE_INFINITY + ; + public static final byte[] + NaN_bry = Bry_.new_a7("NaN") + , Inf_pos_bry = Bry_.new_a7("INF") + ; + public static boolean IsNaN(double v) {return Double.isNaN(v);} + public static double cast(Object o) {try {return (Double)o;} catch(Exception e) {throw Err_.new_type_mismatch_w_exc(e, double.class, o);}} + public static double parse(String raw) {try {return Double.parseDouble(raw);} catch(Exception e) {throw Err_.new_parse_exc(e, double.class, raw);}} + public static double parse_or(String raw, double v) {try {return Double.parseDouble(raw);} catch(Exception e) {Err_.Noop(e); return v;}} + public static double coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Double_.cast(v) : Double_.parse(s);} + catch (Exception e) {throw Err_.new_cast(e, double.class, v);} + } + public static String To_str(double v) { + int v_int = (int)v; + return v - v_int == 0 ? Int_.To_str(v_int) : Double.toString(v); + } + public static String To_str_loose(double v) { + int v_as_int = (int)v; + return v == v_as_int + ? Int_.To_str(v_as_int) // convert to int, and call print String to eliminate any trailing decimal places + // DATE:2014-07-29; calling ((float)v).toString is better at removing trailing 0s than String.format("%g", v). note that .net .toString() handles it better; EX:2449.600000000000d + // DATE:2020-08-12; calling ToStrByPrintF b/c better at removing trailing 0s; ISSUE#:697; + : gplx.objects.primitives.Double_.ToStrByPrintF(v); + } + public static int Compare(double lhs, double rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_language_tst.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_language_tst.java index 3069fc023..1f06cdd37 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_language_tst.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_language_tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,181 +13,198 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; -import org.junit.*; -import gplx.langs.jsons.*; -import gplx.xowa.langs.*; import gplx.xowa.langs.msgs.*; import gplx.xowa.langs.numbers.*; -import gplx.xowa.xtns.cldrs.*; -public class Scrib_lib_language_tst { - @Before public void init() { - fxt.Clear_for_lib(); - lib = fxt.Core().Lib_language().Init(); - } private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib; - @Test public void GetContLangCode() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_getContLangCode, Object_.Ary_empty, "en"); - } - @Test public void IsSupportedLanguage() { - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("fr"), true); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("qq"), false); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("EN"), false); - } - @Test public void IsKnownLanguageTag() { - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("fr"), true); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("qq"), false); - } - @Test public void IsKnownLanguageTag_cldr() { - Io_mgr.Instance.InitEngine_mem(); - Cldr_name_loader_fxt.Create_file_w_langs(fxt.Core().Lang().Key_str(), Keyval_.Ary(Keyval_.new_("goh", "goh_name"))); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("goh"), true); - } - @Test public void IsValidCode() { - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a,b"), true); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a'b"), false); - } - @Test public void IsValidBuiltInCode() { - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e-N"), true); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e n"), false); - } - @Test public void FetchLanguageName() { - Io_mgr.Instance.SaveFilStr("mem/xowa/bin/any/xowa/cfg/lang/data/names.json", Json_doc.Make_str_by_apos - ( "[" - , " {" - , " 'code':'en'" - , " , 'name':'English'" - , " , 'note':'en_note'" - , " }" - , ", {" - , " 'code':'fr'" - , " , 'name':'Français'" - , " , 'note':'fr_note'" - , " }" - , "]" - - )); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("en"), "English"); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("fr"), "Français"); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("qz"), ""); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("459"), ""); // PAGE:en.w:United_States_Strategic_Bombing_Survey; DATE:2018-07-01 - } - @Test public void GetFallbacksFor() { - Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_new(Bry_.new_a7("zh")); - other_lang.Fallback_bry_(Bry_.new_a7("gan-hant, zh-hant, zh-hans")); - fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("zh"), String_.Concat_lines_nl - ( "1=" - , " 1=gan-hant" - , " 2=zh-hant" - , " 3=zh-hans" - , " 4=en" // auto-add en - )); - } - @Test public void GetFallbacksFor_unknown() { - fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("unknown"), String_.Concat_lines_nl - ( "1=" - , " 1=en" // use "en" if unknown; REF:/languages/Language.php|getFallbacksFor; ISSUE#:340; DATE:2019-02-01 - )); - } - @Test public void FormatNum() { - // init - Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_load(Bry_.new_a7("de")); // NOTE: must call load now, else load will be called by scrib and sprs below will get overwritten during load; - fxt.Parser_fxt().Init_lang_numbers_separators(other_lang, ".", ","); - - // test - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("de", 1234), "1.234"); // german spr - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", 1234), "1,234"); // english spr - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234"), "1,234"); // String passed (not int) - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", true))) , "1234"); // noCommafy.y - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", false))) , "1,234"); // noCommafy.n - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", "y"))) , "1234"); // noCommafy."y"; ISSUE#:372 DATE:2019-03-02 - } - @Test public void FormatDate() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "2013-03-17", false), "2013-03-17"); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d"), Datetime_now.Get().XtoStr_fmt_yyyy_MM_dd()); // empty date should default to today; - } - @Test public void FormatDate__ymd_12() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "201603160102", false), "2016-03-16"); // handle long numeric date (12 digits); PAGE:en.w:Boron; DATE:2015-07-29 - } - @Test public void FormatDate__utc() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "+00000002010-05-01T00:00:00Z", false), "2010-05-01"); // handle Wikidata style dates; PAGE:en.w:Mountain_Province; DATE:2015-07-29 - } - @Test public void FormatDate__bce() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "+0065-12-08T00:00:00Z", false), "0065-12-08"); // ISSUE#:500 - } - @Test public void FormatDate_date_omitted() { // PURPOSE: some calls skip the date; retrieve arg_4 by int; EX: pl.w:L._Frank_Baum - Datetime_now.Manual_y_(); - Datetime_now.Manual_(DateAdp_.new_(2013, 12, 19, 1, 2, 3, 4)); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Keyval_.Ary(Keyval_.int_(1, "en"), Keyval_.int_(2, "Y-m-d"), Keyval_.int_(4, false)), "2013-12-19"); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", ""), "2013-12-19");// PURPOSE: '' should return today, not fail; EX: th.w:สถานีรถไฟตรัง - Datetime_now.Manual_n_(); - } - @Test public void Lc() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lc, Object_.Ary("en", "ABC"), "abc"); - } - @Test public void Uc() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC"); - } - @Test public void LcFirst() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lcfirst, Object_.Ary("en", "ABC"), "aBC"); - } - @Test public void UcFirst() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC"); - } - @Test public void ParseFormattedNumber() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", "1,234.56") , "1234.56"); // formatted String - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", "1234") , "1234"); // String - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234) , "1234"); // int - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234.56) , "1234.56"); // double - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en"), Scrib_invoke_func_fxt.Null_rslt); // PURPOSE: missing arg should not fail; EX: ru.w:Туйон DATE:2014-01-06 - } - @Test public void ConvertGrammar() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertGrammar, Object_.Ary("fi", "talo", "elative"), "talosta"); - } - @Test public void ConvertPlural() { - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertPlural, Object_.Ary("ru", 5, Kv_ary_("a", "b", "c")), "c"); // forms in kv_ary - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertPlural, Object_.Ary("ru", 5, "a", "b", "c"), "c"); // forms as rest of ary; PAGE:ru.w:Ленин,_Владимир_Ильич DATE:2014-07-01 - } - @Test public void IsRTL() { - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("en"), false); - Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_new(Bry_.new_a7("ar")); - Gfo_invk_.Invk_by_val(other_lang, Xol_lang_itm.Invk_dir_rtl_, true); - fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("ar"), true); - } - @Test public void Format_duration() { - Init_lang_durations(fxt.Core().Wiki()); - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 3723d, Kv_ary_("hours", "minutes", "seconds")), "1 hour, 2 minutes and 3 seconds"); // basic - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours", "minutes", "seconds")), "2 minutes and 3 seconds"); // omit hour since < 1 - fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours")) , "0 hours"); // handle fractional duration - } - @Test public void Get_duration_intervals() { - Init_lang_durations(fxt.Core().Wiki()); - fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getDurationIntervals, Object_.Ary("en", 3723d, Kv_ary_("hours", "minutes", "seconds")), String_.Concat_lines_nl_skip_last - ( "1=" - , " hours=1" - , " minutes=2" - , " seconds=3" - )); - } - public static Keyval[] Kv_ary_(String... ary) { - int ary_len = ary.length; - Keyval[] rv = new Keyval[ary_len]; - for (int i = 0; i < ary_len; i++) { - rv[i] = Keyval_.int_(i, ary[i]); - } - return rv; - } - public static void Init_lang_durations(Xowe_wiki wiki) { - Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr(); - msg_mgr.Clear(); - msg_mgr.Itm_by_key_or_new("duration-millenia" , "~{0} {{PLURAL:~{0}\u007Cmillennium\u007Cmillennia}}"); - msg_mgr.Itm_by_key_or_new("duration-centuries" , "~{0} {{PLURAL:~{0}\u007Ccentury\u007Ccenturies}}"); - msg_mgr.Itm_by_key_or_new("duration-decades" , "~{0} {{PLURAL:~{0}\u007Cdecade\u007Cdecades}}"); - msg_mgr.Itm_by_key_or_new("duration-years" , "~{0} {{PLURAL:~{0}\u007Cyear\u007Cyears}}"); - msg_mgr.Itm_by_key_or_new("duration-weeks" , "~{0} {{PLURAL:~{0}\u007Cweek\u007Cweeks}}"); - msg_mgr.Itm_by_key_or_new("duration-days" , "~{0} {{PLURAL:~{0}\u007Cday\u007Cdays}}"); - msg_mgr.Itm_by_key_or_new("duration-hours" , "~{0} {{PLURAL:~{0}\u007Chour\u007Chours}}"); - msg_mgr.Itm_by_key_or_new("duration-minutes" , "~{0} {{PLURAL:~{0}\u007Cminute\u007Cminutes}}"); - msg_mgr.Itm_by_key_or_new("duration-seconds" , "~{0} {{PLURAL:~{0}\u007Csecond\u007Cseconds}}"); - msg_mgr.Itm_by_key_or_new("and" , " and"); - msg_mgr.Itm_by_key_or_new("word-separator" , " "); - msg_mgr.Itm_by_key_or_new("comma-separator" , ", "); - } -} +package gplx.xowa.xtns.scribunto.libs; + +import gplx.Bry_; +import gplx.DateAdp_; +import gplx.Datetime_now; +import gplx.Gfo_invk_; +import gplx.Io_mgr; +import gplx.Keyval; +import gplx.Keyval_; +import gplx.Object_; +import gplx.String_; +import gplx.langs.jsons.Json_doc; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.langs.Xol_lang_itm; +import gplx.xowa.langs.msgs.Xol_msg_mgr; +import gplx.xowa.xtns.cldrs.Cldr_name_loader_fxt; +import gplx.xowa.xtns.scribunto.Scrib_invoke_func_fxt; +import gplx.xowa.xtns.scribunto.Scrib_lib; +import org.junit.Before; +import org.junit.Test; + +public class Scrib_lib_language_tst { + @Before public void init() { + fxt.Clear_for_lib(); + lib = fxt.Core().Lib_language().Init(); + } private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib; + @Test public void GetContLangCode() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_getContLangCode, Object_.Ary_empty, "en"); + } + @Test public void IsSupportedLanguage() { + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("fr"), true); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("qq"), false); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("EN"), false); + } + @Test public void IsKnownLanguageTag() { + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("fr"), true); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("qq"), false); + } + @Test public void IsKnownLanguageTag_cldr() { + Io_mgr.Instance.InitEngine_mem(); + Cldr_name_loader_fxt.Create_file_w_langs(fxt.Core().Lang().Key_str(), Keyval_.Ary(Keyval_.new_("goh", "goh_name"))); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("goh"), true); + } + @Test public void IsValidCode() { + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a,b"), true); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a'b"), false); + } + @Test public void IsValidBuiltInCode() { + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e-N"), true); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e n"), false); + } + @Test public void FetchLanguageName() { + Io_mgr.Instance.SaveFilStr("mem/xowa/bin/any/xowa/cfg/lang/data/names.json", Json_doc.Make_str_by_apos + ( "[" + , " {" + , " 'code':'en'" + , " , 'name':'English'" + , " , 'note':'en_note'" + , " }" + , ", {" + , " 'code':'fr'" + , " , 'name':'Français'" + , " , 'note':'fr_note'" + , " }" + , "]" + + )); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("en"), "English"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("fr"), "Français"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("qz"), ""); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("459"), ""); // PAGE:en.w:United_States_Strategic_Bombing_Survey; DATE:2018-07-01 + } + @Test public void GetFallbacksFor() { + Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_new(Bry_.new_a7("zh")); + other_lang.Fallback_bry_(Bry_.new_a7("gan-hant, zh-hant, zh-hans")); + fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("zh"), String_.Concat_lines_nl + ( "1=" + , " 1=gan-hant" + , " 2=zh-hant" + , " 3=zh-hans" + , " 4=en" // auto-add en + )); + } + @Test public void GetFallbacksFor_unknown() { + fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("unknown"), String_.Concat_lines_nl + ( "1=" + , " 1=en" // use "en" if unknown; REF:/languages/Language.php|getFallbacksFor; ISSUE#:340; DATE:2019-02-01 + )); + } + @Test public void FormatNum() { + // init + Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_load(Bry_.new_a7("de")); // NOTE: must call load now, else load will be called by scrib and sprs below will get overwritten during load; + fxt.Parser_fxt().Init_lang_numbers_separators(other_lang, ".", ","); + + // test + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("de", 1234), "1.234"); // german spr + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", 1234), "1,234"); // english spr + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234"), "1,234"); // String passed (not int) + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", true))) , "1234"); // noCommafy.y + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", false))) , "1,234"); // noCommafy.n + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", "y"))) , "1234"); // noCommafy."y"; ISSUE#:372 DATE:2019-03-02 + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", 768d / 10000000d) , "7.68E-5"); // ensure "e-05" -> "E-5" ISSUE#:697; DATE:2020-08-12 + } + @Test public void FormatDate() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "2013-03-17", false), "2013-03-17"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d"), Datetime_now.Get().XtoStr_fmt_yyyy_MM_dd()); // empty date should default to today; + } + @Test public void FormatDate__ymd_12() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "201603160102", false), "2016-03-16"); // handle long numeric date (12 digits); PAGE:en.w:Boron; DATE:2015-07-29 + } + @Test public void FormatDate__utc() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "+00000002010-05-01T00:00:00Z", false), "2010-05-01"); // handle Wikidata style dates; PAGE:en.w:Mountain_Province; DATE:2015-07-29 + } + @Test public void FormatDate__bce() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", "+0065-12-08T00:00:00Z", false), "0065-12-08"); // ISSUE#:500 + } + @Test public void FormatDate_date_omitted() { // PURPOSE: some calls skip the date; retrieve arg_4 by int; EX: pl.w:L._Frank_Baum + Datetime_now.Manual_y_(); + Datetime_now.Manual_(DateAdp_.new_(2013, 12, 19, 1, 2, 3, 4)); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Keyval_.Ary(Keyval_.int_(1, "en"), Keyval_.int_(2, "Y-m-d"), Keyval_.int_(4, false)), "2013-12-19"); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDate, Object_.Ary("en", "Y-m-d", ""), "2013-12-19");// PURPOSE: '' should return today, not fail; EX: th.w:สถานีรถไฟตรัง + Datetime_now.Manual_n_(); + } + @Test public void Lc() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lc, Object_.Ary("en", "ABC"), "abc"); + } + @Test public void Uc() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC"); + } + @Test public void LcFirst() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lcfirst, Object_.Ary("en", "ABC"), "aBC"); + } + @Test public void UcFirst() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC"); + } + @Test public void ParseFormattedNumber() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", "1,234.56") , "1234.56"); // formatted String + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", "1234") , "1234"); // String + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234) , "1234"); // int + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234.56) , "1234.56"); // double + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en"), Scrib_invoke_func_fxt.Null_rslt); // PURPOSE: missing arg should not fail; EX: ru.w:Туйон DATE:2014-01-06 + } + @Test public void ConvertGrammar() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertGrammar, Object_.Ary("fi", "talo", "elative"), "talosta"); + } + @Test public void ConvertPlural() { + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertPlural, Object_.Ary("ru", 5, Kv_ary_("a", "b", "c")), "c"); // forms in kv_ary + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertPlural, Object_.Ary("ru", 5, "a", "b", "c"), "c"); // forms as rest of ary; PAGE:ru.w:Ленин,_Владимир_Ильич DATE:2014-07-01 + } + @Test public void IsRTL() { + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("en"), false); + Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_new(Bry_.new_a7("ar")); + Gfo_invk_.Invk_by_val(other_lang, Xol_lang_itm.Invk_dir_rtl_, true); + fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("ar"), true); + } + @Test public void Format_duration() { + Init_lang_durations(fxt.Core().Wiki()); + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 3723d, Kv_ary_("hours", "minutes", "seconds")), "1 hour, 2 minutes and 3 seconds"); // basic + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours", "minutes", "seconds")), "2 minutes and 3 seconds"); // omit hour since < 1 + fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours")) , "0 hours"); // handle fractional duration + } + @Test public void Get_duration_intervals() { + Init_lang_durations(fxt.Core().Wiki()); + fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getDurationIntervals, Object_.Ary("en", 3723d, Kv_ary_("hours", "minutes", "seconds")), String_.Concat_lines_nl_skip_last + ( "1=" + , " hours=1" + , " minutes=2" + , " seconds=3" + )); + } + public static Keyval[] Kv_ary_(String... ary) { + int ary_len = ary.length; + Keyval[] rv = new Keyval[ary_len]; + for (int i = 0; i < ary_len; i++) { + rv[i] = Keyval_.int_(i, ary[i]); + } + return rv; + } + public static void Init_lang_durations(Xowe_wiki wiki) { + Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr(); + msg_mgr.Clear(); + msg_mgr.Itm_by_key_or_new("duration-millenia" , "~{0} {{PLURAL:~{0}\u007Cmillennium\u007Cmillennia}}"); + msg_mgr.Itm_by_key_or_new("duration-centuries" , "~{0} {{PLURAL:~{0}\u007Ccentury\u007Ccenturies}}"); + msg_mgr.Itm_by_key_or_new("duration-decades" , "~{0} {{PLURAL:~{0}\u007Cdecade\u007Cdecades}}"); + msg_mgr.Itm_by_key_or_new("duration-years" , "~{0} {{PLURAL:~{0}\u007Cyear\u007Cyears}}"); + msg_mgr.Itm_by_key_or_new("duration-weeks" , "~{0} {{PLURAL:~{0}\u007Cweek\u007Cweeks}}"); + msg_mgr.Itm_by_key_or_new("duration-days" , "~{0} {{PLURAL:~{0}\u007Cday\u007Cdays}}"); + msg_mgr.Itm_by_key_or_new("duration-hours" , "~{0} {{PLURAL:~{0}\u007Chour\u007Chours}}"); + msg_mgr.Itm_by_key_or_new("duration-minutes" , "~{0} {{PLURAL:~{0}\u007Cminute\u007Cminutes}}"); + msg_mgr.Itm_by_key_or_new("duration-seconds" , "~{0} {{PLURAL:~{0}\u007Csecond\u007Cseconds}}"); + msg_mgr.Itm_by_key_or_new("and" , " and"); + msg_mgr.Itm_by_key_or_new("word-separator" , " "); + msg_mgr.Itm_by_key_or_new("comma-separator" , ", "); + } +} diff --git a/baselib/src/gplx/objects/primitives/Double_.java b/baselib/src/gplx/objects/primitives/Double_.java index fe2c36a2c..6dd778d80 100644 --- a/baselib/src/gplx/objects/primitives/Double_.java +++ b/baselib/src/gplx/objects/primitives/Double_.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,8 +13,100 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.objects.primitives; import gplx.*; import gplx.objects.*; -public class Double_ { - public static final String Cls_val_name = "double"; - public static final Class Cls_ref_type = Double.class; -} +package gplx.objects.primitives; + +public class Double_ { + public static final String Cls_val_name = "double"; + public static final Class Cls_ref_type = Double.class; + + public static String ToStrByPrintF(double val) { + // call sprintf-like format; EX:"sprintf((s), "%.14g", (n));" + return TrimZeroes(String.format("%.14g", val)); + } + public static String TrimZeroes(String valStr) { // TEST + // NOTE:this function was originally in LuaDouble; it was refactored for clarity, and also to handle trimming zeroes from exponent; EX:"e-05" ISSUE#:697; DATE:2020-08-12 + int valStrLen = valStr.length(); + boolean startTrimming = true; // will be set to true at end of string (12.00), or before "e" (1.00e5) + boolean trimmingZeroes = false; + int trimIdx = -1; + int expIdx = -1; + + // read backwards from end of string + for (int i = valStrLen - 1; i > -1; i--) { + switch (valStr.charAt(i)) { + case '0': + // if startTrimming is true, then enable trimmingZeroes; should only occur twice; (1) end of string ("12.00"); (2) before "e" ("1.00e5") + if (startTrimming) { + startTrimming = false; + trimmingZeroes = true; + } + + // if trimmingZeroes, set trimIdx just before this `0` + if (trimmingZeroes) + trimIdx = i; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + // disable trimmingZeroes; EX: "12.30" + trimmingZeroes = false; + startTrimming = false; // mark startTrimming false else will gobble up zeroes before numbers; EX: "0.00001" x> "0.1"; ISSUE#:697; DATE:2020-08-11 + break; + case '-': // ignore scientific notation or negative sign; EX: "5.0e-17", "-1" + break; + case 'e': // scientific notation; reset startTrimming; EX: 5.0000000000000e-17 + expIdx = i; + startTrimming = true; + break; + case '.': + // if still trimmingZeroes, and reached decimalPoint, then update trimIdx to truncate decimalPoint also; EX: "123.0000" -> "123" x> "123." + if (trimmingZeroes) + trimIdx = i; + + // trimIdx has been set + if (trimIdx != -1) { + if (expIdx == -1) { // decimal; EX: "12.00" + valStr = valStr.substring(0, trimIdx); + } + else { // exponent; "1.00e5" + // get exponent portion; EX: "e-05" + String expStr = valStr.substring(expIdx, valStrLen); + int expStrLen = expStr.length(); + + // set expSymIdx to be after "e" or "e-" + int expSymIdx = 1; // skip the "e" + if (expSymIdx < expStrLen && !Character.isDigit(expStr.charAt(expSymIdx))) { // skip "-" if it's there + expSymIdx++; + } + + // exponent can have 0s; skip them; EX: 1.1e-05 + int expNumIdx = expSymIdx; + for (int j = expNumIdx; j < expStrLen; j++) { + if (expStr.charAt(j) == '0') { // skip zeroes + expNumIdx++; + } + else { + break; + } + } + + // now stitch it together + valStr = valStr.substring(0, trimIdx) + + expStr.substring(0, expSymIdx) // add "e-" + + expStr.substring(expNumIdx); // add numbers with any leading zeroes trimmed + } + } + + // NOTE: "e" needs to be uppercased to "E"; PAGE:en.w:List_of_countries_and_dependencies_by_population + if (expIdx != -1) { + valStr = valStr.toUpperCase(); + } + i = -1; // no more trimming needed before decimalPoint; stop looping + break; + default: // anything else; stop looping + i = -1; + break; + } + } + return valStr; + } +} diff --git a/baselib/tst/gplx/objects/primitives/Double_Test.java b/baselib/tst/gplx/objects/primitives/Double_Test.java new file mode 100644 index 000000000..91dd977f5 --- /dev/null +++ b/baselib/tst/gplx/objects/primitives/Double_Test.java @@ -0,0 +1,51 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +You may use XOWA according to either of these licenses as is most appropriate +for your project on a case-by-case basis. + +The terms of each license can be found in the source code repository: + +GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt +Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt +*/ +package gplx.objects.primitives; + +import gplx.tests.Gftest_fxt; +import org.junit.Test; + +public class Double_Test { + private final Double_Tstr tstr = new Double_Tstr(); + + @Test + public void TrimZeroes() { + tstr.Test_TrimZeroes("12.100" , "12.1"); + tstr.Test_TrimZeroes("12.000" , "12"); + tstr.Test_TrimZeroes("12.001" , "12.001"); + tstr.Test_TrimZeroes("1020.00" , "1020"); + tstr.Test_TrimZeroes("1020.00" , "1020"); + tstr.Test_TrimZeroes("1.200e5" , "1.2E5"); + tstr.Test_TrimZeroes("1.200e-05" , "1.2E-5"); + } + + @Test + public void ToStrByPrintF() { + tstr.Test_ToStrByPrintF(1d / 2d , "0.5"); // fails with 0.50000000000000 + tstr.Test_ToStrByPrintF(5d / 100000000000000000d, "5E-17"); // fails with 5.0000000000000e-17 + tstr.Test_ToStrByPrintF(7538000d / 7773352000d , "0.00096972322879499"); // fails with 0; ISSUE#:697; DATE:2020-08-11 + tstr.Test_ToStrByPrintF(56225d / 7776747000d , "7.2298867379895E-06"); // fails with 0; ISSUE#:697; DATE:2020-08-11 + tstr.Test_ToStrByPrintF(35746d / 7805411000d , "4.5796435319037E-06"); // fails with 0; ISSUE#:697; DATE:2020-08-11 + } +} +class Double_Tstr { + public void Test_ToStrByPrintF(double v, String expd) { + Gftest_fxt.Eq__str(expd, Double_.ToStrByPrintF(v)); + } + public void Test_TrimZeroes(String val, String expd) { + Gftest_fxt.Eq__str(expd, Double_.TrimZeroes(val)); + } +} diff --git a/res/bin/any/java/luaj/luaj_xowa.jar b/res/bin/any/java/luaj/luaj_xowa.jar index f2299edfd..35f4c696d 100644 Binary files a/res/bin/any/java/luaj/luaj_xowa.jar and b/res/bin/any/java/luaj/luaj_xowa.jar differ