Scribunto: Trim zeroes when stringifying Doubles [#697]

staging
gnosygnu 4 years ago
parent f122a8d8a3
commit bdae819908

@ -1,6 +1,6 @@
/* /*
XOWA: the XOWA Offline Wiki Application 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, 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. 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 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/ */
package gplx; package gplx;
public class Double_ { public class Double_ {
public static final String Cls_val_name = "double"; public static final String Cls_val_name = "double";
public static final Class<?> Cls_ref_type = Double.class; public static final Class<?> Cls_ref_type = Double.class;
public static final double public static final double
MinValue = Double.MIN_VALUE MinValue = Double.MIN_VALUE
, NaN = Double.NaN , NaN = Double.NaN
, Inf_pos = Double.POSITIVE_INFINITY , Inf_pos = Double.POSITIVE_INFINITY
; ;
public static final byte[] public static final byte[]
NaN_bry = Bry_.new_a7("NaN") NaN_bry = Bry_.new_a7("NaN")
, Inf_pos_bry = Bry_.new_a7("INF") , Inf_pos_bry = Bry_.new_a7("INF")
; ;
public static boolean IsNaN(double v) {return Double.isNaN(v);} 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 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(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 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) { public static double coerce_(Object v) {
try {String s = String_.as_(v); return s == null ? Double_.cast(v) : Double_.parse(s);} 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);} catch (Exception e) {throw Err_.new_cast(e, double.class, v);}
} }
public static String To_str(double v) { public static String To_str(double v) {
int v_int = (int)v; int v_int = (int)v;
return v - v_int == 0 ? Int_.To_str(v_int) : Double.toString(v); return v - v_int == 0 ? Int_.To_str(v_int) : Double.toString(v);
} }
public static String To_str_loose(double v) { public static String To_str_loose(double v) {
int v_as_int = (int)v; int v_as_int = (int)v;
return v == v_as_int return v == v_as_int
? Int_.To_str(v_as_int) // convert to int, and call print String to eliminate any trailing decimal places ? 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 // 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;
public static int Compare(double lhs, double rhs) { : gplx.objects.primitives.Double_.ToStrByPrintF(v);
if (lhs == rhs) return CompareAble_.Same; }
else if (lhs < rhs) return CompareAble_.Less; public static int Compare(double lhs, double rhs) {
else return CompareAble_.More; if (lhs == rhs) return CompareAble_.Same;
} else if (lhs < rhs) return CompareAble_.Less;
} else return CompareAble_.More;
}
}

@ -1,6 +1,6 @@
/* /*
XOWA: the XOWA Offline Wiki Application 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, 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. 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 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.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.*; package gplx.xowa.xtns.scribunto.libs;
import org.junit.*;
import gplx.langs.jsons.*; import gplx.Bry_;
import gplx.xowa.langs.*; import gplx.xowa.langs.msgs.*; import gplx.xowa.langs.numbers.*; import gplx.DateAdp_;
import gplx.xowa.xtns.cldrs.*; import gplx.Datetime_now;
public class Scrib_lib_language_tst { import gplx.Gfo_invk_;
@Before public void init() { import gplx.Io_mgr;
fxt.Clear_for_lib(); import gplx.Keyval;
lib = fxt.Core().Lib_language().Init(); import gplx.Keyval_;
} private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib; import gplx.Object_;
@Test public void GetContLangCode() { import gplx.String_;
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_getContLangCode, Object_.Ary_empty, "en"); import gplx.langs.jsons.Json_doc;
} import gplx.xowa.Xowe_wiki;
@Test public void IsSupportedLanguage() { import gplx.xowa.langs.Xol_lang_itm;
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("fr"), true); import gplx.xowa.langs.msgs.Xol_msg_mgr;
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("qq"), false); import gplx.xowa.xtns.cldrs.Cldr_name_loader_fxt;
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("EN"), false); import gplx.xowa.xtns.scribunto.Scrib_invoke_func_fxt;
} import gplx.xowa.xtns.scribunto.Scrib_lib;
@Test public void IsKnownLanguageTag() { import org.junit.Before;
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("fr"), true); import org.junit.Test;
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("qq"), false);
} public class Scrib_lib_language_tst {
@Test public void IsKnownLanguageTag_cldr() { @Before public void init() {
Io_mgr.Instance.InitEngine_mem(); fxt.Clear_for_lib();
Cldr_name_loader_fxt.Create_file_w_langs(fxt.Core().Lang().Key_str(), Keyval_.Ary(Keyval_.new_("goh", "goh_name"))); lib = fxt.Core().Lib_language().Init();
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("goh"), true); } private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib lib;
} @Test public void GetContLangCode() {
@Test public void IsValidCode() { fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_getContLangCode, Object_.Ary_empty, "en");
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 IsSupportedLanguage() {
} fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("fr"), true);
@Test public void IsValidBuiltInCode() { 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_isValidBuiltInCode, Object_.Ary("e-N"), true); fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isSupportedLanguage, Object_.Ary("EN"), false);
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidBuiltInCode, Object_.Ary("e n"), false); }
} @Test public void IsKnownLanguageTag() {
@Test public void FetchLanguageName() { fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("fr"), true);
Io_mgr.Instance.SaveFilStr("mem/xowa/bin/any/xowa/cfg/lang/data/names.json", Json_doc.Make_str_by_apos fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("qq"), false);
( "[" }
, " {" @Test public void IsKnownLanguageTag_cldr() {
, " 'code':'en'" Io_mgr.Instance.InitEngine_mem();
, " , 'name':'English'" Cldr_name_loader_fxt.Create_file_w_langs(fxt.Core().Lang().Key_str(), Keyval_.Ary(Keyval_.new_("goh", "goh_name")));
, " , 'note':'en_note'" fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isKnownLanguageTag, Object_.Ary("goh"), true);
, " }" }
, ", {" @Test public void IsValidCode() {
, " 'code':'fr'" fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a,b"), true);
, " , 'name':'Français'" fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isValidCode, Object_.Ary("a'b"), false);
, " , 'note':'fr_note'" }
, " }" @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);
)); }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("en"), "English"); @Test public void FetchLanguageName() {
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("fr"), "Français"); Io_mgr.Instance.SaveFilStr("mem/xowa/bin/any/xowa/cfg/lang/data/names.json", Json_doc.Make_str_by_apos
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 , " {"
} , " 'code':'en'"
@Test public void GetFallbacksFor() { , " , 'name':'English'"
Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_new(Bry_.new_a7("zh")); , " , 'note':'en_note'"
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=" , " 'code':'fr'"
, " 1=gan-hant" , " , 'name':'Français'"
, " 2=zh-hant" , " , 'note':'fr_note'"
, " 3=zh-hans" , " }"
, " 4=en" // auto-add en , "]"
));
} ));
@Test public void GetFallbacksFor_unknown() { fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("en"), "English");
fxt.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("unknown"), String_.Concat_lines_nl fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("fr"), "Français");
( "1=" fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_fetchLanguageName, Object_.Ary("qz"), "");
, " 1=en" // use "en" if unknown; REF:/languages/Language.php|getFallbacksFor; ISSUE#:340; DATE:2019-02-01 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() {
@Test public void FormatNum() { Xol_lang_itm other_lang = fxt.Core().App().Lang_mgr().Get_by_or_new(Bry_.new_a7("zh"));
// init other_lang.Fallback_bry_(Bry_.new_a7("gan-hant, zh-hant, zh-hans"));
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.Test_scrib_proc_str_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("zh"), String_.Concat_lines_nl
fxt.Parser_fxt().Init_lang_numbers_separators(other_lang, ".", ","); ( "1="
, " 1=gan-hant"
// test , " 2=zh-hant"
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("de", 1234), "1.234"); // german spr , " 3=zh-hans"
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", 1234), "1,234"); // english spr , " 4=en" // auto-add en
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234"), "1,234"); // String passed (not int) ));
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", true))) , "1234"); // noCommafy.y }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("en", "1234", Keyval_.Ary(Keyval_.new_("noCommafy", false))) , "1,234"); // noCommafy.n @Test public void GetFallbacksFor_unknown() {
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_ary(lib, Scrib_lib_language.Invk_getFallbacksFor, Object_.Ary("unknown"), String_.Concat_lines_nl
} ( "1="
@Test public void FormatDate() { , " 1=en" // use "en" if unknown; REF:/languages/Language.php|getFallbacksFor; ISSUE#:340; DATE:2019-02-01
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 FormatNum() {
@Test public void FormatDate__ymd_12() { // init
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 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 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
} fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatNum, Object_.Ary("de", 1234), "1.234"); // german spr
@Test public void FormatDate__bce() { 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_formatDate, Object_.Ary("en", "Y-m-d", "+0065-12-08T00:00:00Z", false), "0065-12-08"); // ISSUE#:500 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
@Test public void FormatDate_date_omitted() { // PURPOSE: some calls skip the date; retrieve arg_4 by int; EX: pl.w:L._Frank_Baum 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
Datetime_now.Manual_y_(); 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
Datetime_now.Manual_(DateAdp_.new_(2013, 12, 19, 1, 2, 3, 4)); 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
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:สถานีรถไฟตรัง @Test public void FormatDate() {
Datetime_now.Manual_n_(); 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 Lc() { }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lc, Object_.Ary("en", "ABC"), "abc"); @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 Uc() { }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC"); @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 LcFirst() { }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lcfirst, Object_.Ary("en", "ABC"), "aBC"); @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 UcFirst() { }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC"); @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_();
@Test public void ParseFormattedNumber() { Datetime_now.Manual_(DateAdp_.new_(2013, 12, 19, 1, 2, 3, 4));
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_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_parseFormattedNumber, Object_.Ary("en", "1234") , "1234"); // String 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:สถานีรถไฟตรัง
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234) , "1234"); // int Datetime_now.Manual_n_();
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 Lc() {
} fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lc, Object_.Ary("en", "ABC"), "abc");
@Test public void ConvertGrammar() { }
fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertGrammar, Object_.Ary("fi", "talo", "elative"), "talosta"); @Test public void Uc() {
} fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC");
@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 @Test public void LcFirst() {
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 fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_lcfirst, Object_.Ary("en", "ABC"), "aBC");
} }
@Test public void IsRTL() { @Test public void UcFirst() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("en"), false); fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_uc, Object_.Ary("en", "abc"), "ABC");
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); @Test public void ParseFormattedNumber() {
fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("ar"), true); 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
@Test public void Format_duration() { fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_parseFormattedNumber, Object_.Ary("en", 1234) , "1234"); // int
Init_lang_durations(fxt.Core().Wiki()); 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_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_parseFormattedNumber, Object_.Ary("en"), Scrib_invoke_func_fxt.Null_rslt); // PURPOSE: missing arg should not fail; EX: ru.w:Туйон DATE:2014-01-06
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 ConvertGrammar() {
} fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_convertGrammar, Object_.Ary("fi", "talo", "elative"), "talosta");
@Test public void Get_duration_intervals() { }
Init_lang_durations(fxt.Core().Wiki()); @Test public void ConvertPlural() {
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 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
( "1=" 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
, " hours=1" }
, " minutes=2" @Test public void IsRTL() {
, " seconds=3" 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);
public static Keyval[] Kv_ary_(String... ary) { fxt.Test_scrib_proc_bool(lib, Scrib_lib_language.Invk_isRTL, Object_.Ary("ar"), true);
int ary_len = ary.length; }
Keyval[] rv = new Keyval[ary_len]; @Test public void Format_duration() {
for (int i = 0; i < ary_len; i++) { Init_lang_durations(fxt.Core().Wiki());
rv[i] = Keyval_.int_(i, ary[i]); 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
return rv; fxt.Test_scrib_proc_str(lib, Scrib_lib_language.Invk_formatDuration, Object_.Ary("en", 123d, Kv_ary_("hours")) , "0 hours"); // handle fractional duration
} }
public static void Init_lang_durations(Xowe_wiki wiki) { @Test public void Get_duration_intervals() {
Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr(); Init_lang_durations(fxt.Core().Wiki());
msg_mgr.Clear(); 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
msg_mgr.Itm_by_key_or_new("duration-millenia" , "~{0} {{PLURAL:~{0}\u007Cmillennium\u007Cmillennia}}"); ( "1="
msg_mgr.Itm_by_key_or_new("duration-centuries" , "~{0} {{PLURAL:~{0}\u007Ccentury\u007Ccenturies}}"); , " hours=1"
msg_mgr.Itm_by_key_or_new("duration-decades" , "~{0} {{PLURAL:~{0}\u007Cdecade\u007Cdecades}}"); , " minutes=2"
msg_mgr.Itm_by_key_or_new("duration-years" , "~{0} {{PLURAL:~{0}\u007Cyear\u007Cyears}}"); , " seconds=3"
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}}"); public static Keyval[] Kv_ary_(String... ary) {
msg_mgr.Itm_by_key_or_new("duration-minutes" , "~{0} {{PLURAL:~{0}\u007Cminute\u007Cminutes}}"); int ary_len = ary.length;
msg_mgr.Itm_by_key_or_new("duration-seconds" , "~{0} {{PLURAL:~{0}\u007Csecond\u007Cseconds}}"); Keyval[] rv = new Keyval[ary_len];
msg_mgr.Itm_by_key_or_new("and" , "&#32;and"); for (int i = 0; i < ary_len; i++) {
msg_mgr.Itm_by_key_or_new("word-separator" , "&#32;"); rv[i] = Keyval_.int_(i, ary[i]);
msg_mgr.Itm_by_key_or_new("comma-separator" , ",&#32;"); }
} return rv;
} }
public static void Init_lang_durations(Xowe_wiki wiki) {
Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr();
msg_mgr.Clear();
msg_mgr.Itm_by_key_or_new("duration-millenia" , "~{0} {{PLURAL:~{0}\u007Cmillennium\u007Cmillennia}}");
msg_mgr.Itm_by_key_or_new("duration-centuries" , "~{0} {{PLURAL:~{0}\u007Ccentury\u007Ccenturies}}");
msg_mgr.Itm_by_key_or_new("duration-decades" , "~{0} {{PLURAL:~{0}\u007Cdecade\u007Cdecades}}");
msg_mgr.Itm_by_key_or_new("duration-years" , "~{0} {{PLURAL:~{0}\u007Cyear\u007Cyears}}");
msg_mgr.Itm_by_key_or_new("duration-weeks" , "~{0} {{PLURAL:~{0}\u007Cweek\u007Cweeks}}");
msg_mgr.Itm_by_key_or_new("duration-days" , "~{0} {{PLURAL:~{0}\u007Cday\u007Cdays}}");
msg_mgr.Itm_by_key_or_new("duration-hours" , "~{0} {{PLURAL:~{0}\u007Chour\u007Chours}}");
msg_mgr.Itm_by_key_or_new("duration-minutes" , "~{0} {{PLURAL:~{0}\u007Cminute\u007Cminutes}}");
msg_mgr.Itm_by_key_or_new("duration-seconds" , "~{0} {{PLURAL:~{0}\u007Csecond\u007Cseconds}}");
msg_mgr.Itm_by_key_or_new("and" , "&#32;and");
msg_mgr.Itm_by_key_or_new("word-separator" , "&#32;");
msg_mgr.Itm_by_key_or_new("comma-separator" , ",&#32;");
}
}

@ -1,6 +1,6 @@
/* /*
XOWA: the XOWA Offline Wiki Application 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, 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. 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 GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt
Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/ */
package gplx.objects.primitives; import gplx.*; import gplx.objects.*; package gplx.objects.primitives;
public class Double_ {
public static final String Cls_val_name = "double"; public class Double_ {
public static final Class<?> Cls_ref_type = Double.class; 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;
}
}

@ -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));
}
}
Loading…
Cancel
Save