From 316c6c6a585695137fcfdcadfe67f054f1dd79ab Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Thu, 3 Sep 2020 08:32:16 -0400 Subject: [PATCH] Map: Compare precision against 4 not 0 [#792] --- .../xtns/mapSources/Map_dd2dms_func_tst.java | 24 +- .../gplx/xowa/xtns/mapSources/Map_math.java | 595 +++++++++--------- .../pfuncs/Wdata_pf_property__basic__tst.java | 405 ++++++------ 3 files changed, 523 insertions(+), 501 deletions(-) diff --git a/400_xowa/src/gplx/xowa/xtns/mapSources/Map_dd2dms_func_tst.java b/400_xowa/src/gplx/xowa/xtns/mapSources/Map_dd2dms_func_tst.java index 5bfd6c744..49a868bcd 100644 --- a/400_xowa/src/gplx/xowa/xtns/mapSources/Map_dd2dms_func_tst.java +++ b/400_xowa/src/gplx/xowa/xtns/mapSources/Map_dd2dms_func_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,12 +13,16 @@ 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.mapSources; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import org.junit.*; -public class Map_dd2dms_func_tst { - @Before public void init() {fxt.Reset();} private final Xop_fxt fxt = new Xop_fxt(); - @Test public void Example() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58|precision=4}}" , "{{test}}" , "14° 34' 48"");} - @Test public void Plus() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58|precision=4|plus=pos}}" , "{{test}}" , "14° 34' 48" pos");} - @Test public void Ws() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58| precision = 4 | plus = pos }}" , "{{test}}" , "14° 34' 48" pos");} - @Test public void Nested_pfunc() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58|{{#if:2|precision=2}}}}" , "{{test|3}}" , "14° 34'");} // handle "{{#if:2|precision=2}}" -> "precision=2" -} +package gplx.xowa.xtns.mapSources; + +import gplx.xowa.Xop_fxt; +import org.junit.Before; +import org.junit.Test; + +public class Map_dd2dms_func_tst { + @Before public void init() {fxt.Reset();} private final Xop_fxt fxt = new Xop_fxt(); + @Test public void Example() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58|precision=4}}" , "{{test}}" , "14° 34' 48"");} + @Test public void Plus() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58|precision=4|plus=pos}}" , "{{test}}" , "14° 34' 48" pos");} + @Test public void Ws() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58| precision = 4 | plus = pos }}" , "{{test}}" , "14° 34' 48" pos");} + @Test public void Nested_pfunc() {fxt.Test_parse_tmpl_str_test("{{#dd2dms: 14.58|{{#if:2|precision=2}}}}" , "{{test|3}}" , "14° 35'");} // handle "{{#if:2|precision=2}}" -> "precision=2" +} diff --git a/400_xowa/src/gplx/xowa/xtns/mapSources/Map_math.java b/400_xowa/src/gplx/xowa/xtns/mapSources/Map_math.java index 53907f585..8d31898d1 100644 --- a/400_xowa/src/gplx/xowa/xtns/mapSources/Map_math.java +++ b/400_xowa/src/gplx/xowa/xtns/mapSources/Map_math.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,293 +13,306 @@ 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.mapSources; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import gplx.core.primitives.*; import gplx.core.btries.*; -import gplx.xowa.parsers.*; import gplx.xowa.parsers.tmpls.*; -class Map_math {// REF.MW:MapSources_math.php - private int word_idx_nsew; - private double[] rv = new double[4]; - private byte dir_id; - private int prec; - private int step; - public double Dec() {return dec;} private double dec; - public int Error() {return error;} private int error; - public double Coord_dec() {return coord_dec;} private double coord_dec; - public double Coord_deg() {return coord_deg;} private double coord_deg; - public double Coord_min() {return coord_min;} private double coord_min; - public double Coord_sec() {return coord_sec;} private double coord_sec; - public byte[] Coord_dir_ns() {return coord_dir_ns;} private byte[] coord_dir_ns; - public byte[] Coord_dir_ew() {return coord_dir_ew;} private byte[] coord_dir_ew; - public boolean Ctor(byte[] input, int prec, byte[] dir, int until_step) { - try { - New_coord(input, dir, prec); - if (until_step > 1 && error == 0) - Set_coord(); - } catch (Exception e) { - Err_.Noop(e); - error = -128; - } - return error == 0; - } - public void Fail(Xop_ctx ctx, byte[] src, Xot_invk self, Bry_bfr bfr, byte[] pfunc_name) { - String page_str = ctx.Page().Url().To_str(); - String pfunc_name_str = String_.new_u8(pfunc_name); - String self_str = String_.new_u8(src, self.Src_bgn(), self.Src_end()); - switch (error) { - case -1: // empty coord; EX: {{#deg2dd:|precision=6}}}} PAGE:it.v:Sami; DATE:2014-03-02 - case -2: // words > 4; EX:{{#geoLink: $1 $2 $3 $4 $5 $6|lat=51°20′00″19°55′50″}}; PAGE:pl.v:Rezerwat_przyrody_Jaksonek DATE:2014-08-14 - case -3: // invalid delim; EX:{{#geoLink: $1 $2 $3 $4 $5 $6|lat=51°31′37″|long=20°13′17'}}; PAGE:pl.v:Rezerwat_przyrody_Ciosny DATE:2014-08-14 - ctx.App().Usr_dlg().Log_many("", "", "mapSources failed: page=~{0} pfunc=~{1} err=~{2} src=~{3}", page_str, pfunc_name_str, error, self_str); // don't warn b/c there are many - break; - default: - ctx.App().Usr_dlg().Warn_many("", "", "mapSources failed: page=~{0} pfunc=~{1} err=~{2} src=~{3}", page_str, pfunc_name_str, error, self_str); - break; - } - } - private void New_coord(byte[] input, byte[] dir, int prec) { // REF.MW:newCoord - this.error = 0; this.word_idx_nsew = -1; - coord_dec = coord_deg = coord_min = coord_sec = 0; - rv[0] = rv[1] = rv[2] = 0; rv[3] = 1; - this.dir_id = Parse_dir(dir); - this.prec = Parse_precision(prec); - this.dec = 0; - this.step = 0; - Parse_input(input); - } - private void Set_coord() { // REF.MW:setCoord - if (step > 1) return; - if (prec < 9) - dec = Math_.Round(dec, prec); - int sign = dec < 0 ? -1 : 1; - double angle = Math_.Abs_double(dec); - double deg = Math_.Floor(angle); - double min = (angle - deg) * 60; - double sec = prec > 0 - ? Math_.Round((min - Math_.Floor(min)) * 60, prec - 4) - : Math_.Round((min - Math_.Floor(min)) * 60, 0) - ; - min = Math_.Floor(min); - if (sec >= 60) { - sec -= 60; - min++; - } - if (prec < 3 && sec >= 30) - min++; - if (prec < 3) - sec = 0; - if (min >= 60) { - min -= 60; - deg++; - } - if (prec < 1 && min >= 30) { - deg++; - } - if (prec < 1) - min = 0; - coord_dec = Math_.Round(dec, prec); - coord_deg = deg * sign; - coord_min = min; - coord_sec = sec; - if (sign > 0) { - coord_dir_ns = Compass_N; - coord_dir_ew = Compass_E; - } - else { - coord_dir_ns = Compass_S; - coord_dir_ew = Compass_W; - } - step = 2; - } - public byte[] Get_dms(boolean wikibase, byte[] plus, byte[] minus) { // REF.MW: getDMSString - if (step < 2) Set_coord(); - double deg = coord_deg; - if ( dec < 0 - && ( (Bry_.Len_gt_0(plus) || Bry_.Len_gt_0(minus)) - || wikibase // NOTE: wikibase will always pass in empty plus / minus; still need to suppress "-" sign because letter has already been reversed; EX:"-2 E" -> "2 W" x> "-2 W" DATE:2017-04-02 - ) - ) { - deg = Math_.Abs_double(deg); - } - tmp_bfr.Add_double(deg).Add(Bry_deg); - if (prec > 0) { - if (!wikibase) // NOTE: do not add space if wikibase, else will fail in Module:en.w:WikidataCoord; PAGE:en.w:Hulme_Arch_Bridge DATE:2017-04-02 - tmp_bfr.Add_byte_space(); - tmp_bfr.Add_double(coord_min).Add(wikibase ? Bry_apos_wb : Bry_apos_mw); - } - if (prec > 2) { - if (!wikibase) // NOTE: do not add space if wikibase, else will fail in Module:en.w:WikidataCoord; PAGE:en.w:Hulme_Arch_Bridge DATE:2017-04-02 - tmp_bfr.Add_byte_space(); - tmp_bfr.Add_double(coord_sec).Add(wikibase ? Bry_quot_wb : Bry_quot_mw); - } - byte[] letter = null; - if (dir_id == Dir_lat_id) - letter = coord_dir_ns; - if (dir_id == Dir_long_id) - letter = coord_dir_ew; - if (dec > 0 && Bry_.Len_gt_0(plus)) - letter = plus; - if (dec < 0 && Bry_.Len_gt_0(minus)) - letter = minus; - if (letter != null) { - tmp_bfr.Add_byte_space(); - tmp_bfr.Add(letter); - } - return tmp_bfr.To_bry_and_clear(); - } - private void Parse_input(byte[] src) { // REF.MW: toDec - src = Parse_input_normalize(tmp_bfr, src); - if (src == null) {error = -1; return;} - int src_len = src.length; - int word_idx = -1, word_bgn = 0, words_len = 0; - int i = 0; - while (true) { - boolean is_last = i == src_len; - byte b = is_last ? Byte_ascii.Space : src[i]; - switch (b) { - case Byte_ascii.Space: - Parse_input_word(rv, src, ++word_idx, word_bgn, i); - ++words_len; - i = Bry_find_.Find_fwd_while_space_or_tab(src, i, src_len); - word_bgn = i; - break; - } - if (is_last) break; - i++; - } - if (words_len < 1 || words_len > 4) {error = -2; return;} - if (word_idx_nsew != -1 && word_idx_nsew != words_len - 1) {error = -10; return;} - if (rv[0] >= 0) - dec = (rv[0] + rv[1] / 60 + rv[2] / 3600 ) * rv[3]; - else - dec = (rv[0] - rv[1] / 60 - rv[2] / 3600 ) * rv[3]; - this.step = 1; - } - private boolean Parse_input_word_is_compass(byte v) { - switch (v) { - case Byte_ascii.Ltr_N: - case Byte_ascii.Ltr_E: - case Byte_ascii.Ltr_S: - case Byte_ascii.Ltr_W: - return true; - default: - return false; - } - } - private void Parse_input_word(double[] rv, byte[] input, int word_idx, int word_bgn, int word_end) { - if (word_idx >= Input_units_len) return; - byte unit_dlm = Input_units[word_idx]; - int pos = Bry_find_.Find_fwd(input, unit_dlm, word_bgn, word_end); - if (pos != Bry_find_.Not_found) // remove dlms from end of bry; EX: "123'" -> "123" - word_end = pos; - if (!Parse_input_word_is_compass(input[word_bgn])) { // if ( is_numeric( $v ) ) { - double word_val = Bry_.To_double_or(input, word_bgn, word_end, Double_.NaN); - if (!Double_.IsNaN(word_val)) { - if (word_idx > 2) {error = -4; return;} - switch (word_idx) { - case 0: - if (word_val <= -180 || word_val > 180) {error = -5; return;} - rv[0] = word_val; - break; - case 1: - if (word_val < 0 || word_val >= 60) {error = -6; return;} - if (rv[0] != (int)(rv[0])) {error = -7; return;} - rv[1] = word_val; - break; - case 2: - if (word_val < 0 || word_val >= 60) {error = -8; return;} - if (rv[1] != (int)(rv[1])) {error = -9; return;} - rv[2] = word_val; - break; - } - } - else { - error = -3; - return; - } - } - else { // 'NSEW' - word_idx_nsew = word_idx; - byte word_byte = input[word_bgn]; - if (rv[0] < 0) {error = -11; return;} - if (word_end - word_bgn != 1) {error = -3; return;} - switch (dir_id) { - case Dir_long_id: - if (word_byte == Byte_ascii.Ltr_N || word_byte == Byte_ascii.Ltr_S) {error = -12; return;} - break; - case Dir_lat_id: - if (word_byte == Byte_ascii.Ltr_E || word_byte == Byte_ascii.Ltr_W) {error = -12; return;} - break; - case Dir_unknown_id: - if (word_byte == Byte_ascii.Ltr_N || word_byte == Byte_ascii.Ltr_S) this.dir_id = Dir_lat_id; - else this.dir_id = Dir_long_id; - break; - } - if (this.dir_id == Dir_lat_id) { - double rv_0 = rv[0]; - if (rv_0 < -90 || rv_0 > 90) {error = -13; return;} - } - if (word_byte == Byte_ascii.Ltr_S || word_byte == Byte_ascii.Ltr_W) - rv[3] = -1; - } - } - private static byte Parse_dir(byte[] dir) { - if (Bry_.Len_eq_0(dir)) return Dir_unknown_id; - Object dir_obj = Dir_trie.Match_bgn(dir, 0, dir.length); - return dir_obj == null ? Dir_unknown_id : ((Byte_obj_val)dir_obj).Val(); - } - private static int Parse_precision(int val) { // REF.MW: MapSourcesMath.php|newCoord - if (val > -1 && val < 10) return val; - else if (val == -1) return 9; - else return 4; - } - private Bry_bfr tmp_bfr = Bry_bfr_.Reset(32); - public static byte[] Parse_input_normalize(Bry_bfr bfr, byte[] src) { - /* - $w = str_replace( array( '‘', '’', '′' ), "'", $input ); - $w = str_replace( array( "''", '“', '”', '″' ), '"', $w ); - $w = str_replace( '−', '-', $w ); - $w = strtoupper( str_replace( array( '_', '/', "\t", "\n", "\r" ), ' ', $w ) ); - $w = str_replace( array( '°', "'", '"' ), array( '° ', "' ", '" ' ), $w ); - $w = trim( str_replace( array( 'N', 'S', 'E', 'W' ), array( ' N', ' S', ' E', ' W' ), $w ) ); - */ - int src_end = src.length; if (src_end == 0) return null; - src = Trie__normalize__apos.Replace(bfr, src, 0, src_end); // normalize apos separately, since 2 apos can go to quotes; EX: ‘’ -> "; PAGE:it.v:Morro_d'Oro DATE:2015-12-06 - src = Trie__normalize__rest.Replace(bfr, src, 0, src.length); // normalize rest; - return Bry_.Trim(src); - } - private static final byte[] - Bry_deg = Bry_.new_u8("°") - , Bry_quot_mw = Bry_.new_a7(""") - , Bry_quot_wb = Bry_.new_a7(""") // REF:en.w:Module:WikidataCoord - , Bry_apos_mw = Bry_.new_a7("'") - , Bry_apos_wb = Bry_.new_a7("'") // REF:en.w:Module:WikidataCoord - ; - private static final byte Dir_unknown_id = 0, Dir_lat_id = 1, Dir_long_id = 2; - public static final byte[] Dir_lat_bry = Bry_.new_a7("lat"), Dir_long_bry = Bry_.new_a7("long"); - private static final Btrie_slim_mgr Dir_trie = Btrie_slim_mgr.ci_a7() // NOTE:ci.ascii:MW_const.en - .Add_bry_byte(Dir_lat_bry , Dir_lat_id) - .Add_bry_byte(Dir_long_bry , Dir_long_id) - ; - private static final byte[] - Compass_N = new byte[] {Byte_ascii.Ltr_N} - , Compass_E = new byte[] {Byte_ascii.Ltr_E} - , Compass_S = new byte[] {Byte_ascii.Ltr_S} - , Compass_W = new byte[] {Byte_ascii.Ltr_W} - ; - private static final byte Input_byte_degree = Byte_ascii.Slash; // NOTE: ugly cheat to avoid using multi-byte char; note that all "/" are swapped out to " ", so any remaining "/" was added by the normalizer; EX: "123° 4/5" -> "123/ 4 5" - private static final byte[] Input_units = new byte[] {Input_byte_degree, Byte_ascii.Apos, Byte_ascii.Quote, Byte_ascii.Space}; - private static final int Input_units_len = Input_units.length; - private static final Btrie_slim_mgr Trie__normalize__apos = Btrie_slim_mgr.cs() - .Add_replace_many (Byte_ascii.Apos_bry , "‘", "’", "′"); - private static final Btrie_slim_mgr Trie__normalize__rest = Btrie_slim_mgr.cs() - .Add_replace_many ("' " , "'") - .Add_replace_many ("\" " , "\"", "''", "“", "”", "″") - .Add_replace_many (Byte_ascii.Dash_bry , "-", "−") // NOTE: emdash and endash - .Add_replace_many (Byte_ascii.Space_bry , " ", "_", "/", "\t", "\n", "\r") // NOTE: " " =   - .Add_replace_many ("/ " , "°") - .Add_replace_many (" N" , "N", "n") - .Add_replace_many (" S" , "S", "s") - .Add_replace_many (" E" , "E", "e") - .Add_replace_many (" W" , "W", "w"); - public static final Map_math Instance = new Map_math(); -} +package gplx.xowa.xtns.mapSources; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Bry_find_; +import gplx.Byte_ascii; +import gplx.Double_; +import gplx.Err_; +import gplx.Math_; +import gplx.String_; +import gplx.core.btries.Btrie_slim_mgr; +import gplx.core.primitives.Byte_obj_val; +import gplx.xowa.parsers.Xop_ctx; +import gplx.xowa.parsers.tmpls.Xot_invk; + +class Map_math {// REF.MW:MapSources_math.php + private int word_idx_nsew; + private double[] rv = new double[4]; + private byte dir_id; + private int prec; + private int step; + public double Dec() {return dec;} private double dec; + public int Error() {return error;} private int error; + public double Coord_dec() {return coord_dec;} private double coord_dec; + public double Coord_deg() {return coord_deg;} private double coord_deg; + public double Coord_min() {return coord_min;} private double coord_min; + public double Coord_sec() {return coord_sec;} private double coord_sec; + public byte[] Coord_dir_ns() {return coord_dir_ns;} private byte[] coord_dir_ns; + public byte[] Coord_dir_ew() {return coord_dir_ew;} private byte[] coord_dir_ew; + public boolean Ctor(byte[] input, int prec, byte[] dir, int until_step) { + try { + New_coord(input, dir, prec); + if (until_step > 1 && error == 0) + Set_coord(); + } catch (Exception e) { + Err_.Noop(e); + error = -128; + } + return error == 0; + } + public void Fail(Xop_ctx ctx, byte[] src, Xot_invk self, Bry_bfr bfr, byte[] pfunc_name) { + String page_str = ctx.Page().Url().To_str(); + String pfunc_name_str = String_.new_u8(pfunc_name); + String self_str = String_.new_u8(src, self.Src_bgn(), self.Src_end()); + switch (error) { + case -1: // empty coord; EX: {{#deg2dd:|precision=6}}}} PAGE:it.v:Sami; DATE:2014-03-02 + case -2: // words > 4; EX:{{#geoLink: $1 $2 $3 $4 $5 $6|lat=51°20′00″19°55′50″}}; PAGE:pl.v:Rezerwat_przyrody_Jaksonek DATE:2014-08-14 + case -3: // invalid delim; EX:{{#geoLink: $1 $2 $3 $4 $5 $6|lat=51°31′37″|long=20°13′17'}}; PAGE:pl.v:Rezerwat_przyrody_Ciosny DATE:2014-08-14 + ctx.App().Usr_dlg().Log_many("", "", "mapSources failed: page=~{0} pfunc=~{1} err=~{2} src=~{3}", page_str, pfunc_name_str, error, self_str); // don't warn b/c there are many + break; + default: + ctx.App().Usr_dlg().Warn_many("", "", "mapSources failed: page=~{0} pfunc=~{1} err=~{2} src=~{3}", page_str, pfunc_name_str, error, self_str); + break; + } + } + private void New_coord(byte[] input, byte[] dir, int prec) { // REF.MW:newCoord + this.error = 0; this.word_idx_nsew = -1; + coord_dec = coord_deg = coord_min = coord_sec = 0; + rv[0] = rv[1] = rv[2] = 0; rv[3] = 1; + this.dir_id = Parse_dir(dir); + this.prec = Parse_precision(prec); + this.dec = 0; + this.step = 0; + Parse_input(input); + } + private void Set_coord() { // REF.MW:setCoord + if (step > 1) return; + if (prec < 9) + dec = Math_.Round(dec, prec); + int sign = dec < 0 ? -1 : 1; + double angle = Math_.Abs_double(dec); + double deg = Math_.Floor(angle); + double min = (angle - deg) * 60; + double sec = prec > 4 // 2020-09-03|ISSUE#:792|precision check should be > 4 not > 0;PAGE:en.w:Huntington_Plaza + ? Math_.Round((min - Math_.Floor(min)) * 60, prec - 4) + : Math_.Round((min - Math_.Floor(min)) * 60, 0) + ; + min = Math_.Floor(min); + if (sec >= 60) { + sec -= 60; + min++; + } + if (prec < 3 && sec >= 30) + min++; + if (prec < 3) + sec = 0; + if (min >= 60) { + min -= 60; + deg++; + } + if (prec < 1 && min >= 30) { + deg++; + } + if (prec < 1) + min = 0; + coord_dec = Math_.Round(dec, prec); + coord_deg = deg * sign; + coord_min = min; + coord_sec = sec; + if (sign > 0) { + coord_dir_ns = Compass_N; + coord_dir_ew = Compass_E; + } + else { + coord_dir_ns = Compass_S; + coord_dir_ew = Compass_W; + } + step = 2; + } + public byte[] Get_dms(boolean wikibase, byte[] plus, byte[] minus) { // REF.MW: getDMSString + if (step < 2) Set_coord(); + double deg = coord_deg; + if ( dec < 0 + && ( (Bry_.Len_gt_0(plus) || Bry_.Len_gt_0(minus)) + || wikibase // NOTE: wikibase will always pass in empty plus / minus; still need to suppress "-" sign because letter has already been reversed; EX:"-2 E" -> "2 W" x> "-2 W" DATE:2017-04-02 + ) + ) { + deg = Math_.Abs_double(deg); + } + tmp_bfr.Add_double(deg).Add(Bry_deg); + if (prec > 0) { + if (!wikibase) // NOTE: do not add space if wikibase, else will fail in Module:en.w:WikidataCoord; PAGE:en.w:Hulme_Arch_Bridge DATE:2017-04-02 + tmp_bfr.Add_byte_space(); + tmp_bfr.Add_double(coord_min).Add(wikibase ? Bry_apos_wb : Bry_apos_mw); + } + if (prec > 2) { + if (!wikibase) // NOTE: do not add space if wikibase, else will fail in Module:en.w:WikidataCoord; PAGE:en.w:Hulme_Arch_Bridge DATE:2017-04-02 + tmp_bfr.Add_byte_space(); + tmp_bfr.Add_double(coord_sec).Add(wikibase ? Bry_quot_wb : Bry_quot_mw); + } + byte[] letter = null; + if (dir_id == Dir_lat_id) + letter = coord_dir_ns; + if (dir_id == Dir_long_id) + letter = coord_dir_ew; + if (dec > 0 && Bry_.Len_gt_0(plus)) + letter = plus; + if (dec < 0 && Bry_.Len_gt_0(minus)) + letter = minus; + if (letter != null) { + tmp_bfr.Add_byte_space(); + tmp_bfr.Add(letter); + } + return tmp_bfr.To_bry_and_clear(); + } + private void Parse_input(byte[] src) { // REF.MW: toDec + src = Parse_input_normalize(tmp_bfr, src); + if (src == null) {error = -1; return;} + int src_len = src.length; + int word_idx = -1, word_bgn = 0, words_len = 0; + int i = 0; + while (true) { + boolean is_last = i == src_len; + byte b = is_last ? Byte_ascii.Space : src[i]; + switch (b) { + case Byte_ascii.Space: + Parse_input_word(rv, src, ++word_idx, word_bgn, i); + ++words_len; + i = Bry_find_.Find_fwd_while_space_or_tab(src, i, src_len); + word_bgn = i; + break; + } + if (is_last) break; + i++; + } + if (words_len < 1 || words_len > 4) {error = -2; return;} + if (word_idx_nsew != -1 && word_idx_nsew != words_len - 1) {error = -10; return;} + if (rv[0] >= 0) + dec = (rv[0] + rv[1] / 60 + rv[2] / 3600 ) * rv[3]; + else + dec = (rv[0] - rv[1] / 60 - rv[2] / 3600 ) * rv[3]; + this.step = 1; + } + private boolean Parse_input_word_is_compass(byte v) { + switch (v) { + case Byte_ascii.Ltr_N: + case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_S: + case Byte_ascii.Ltr_W: + return true; + default: + return false; + } + } + private void Parse_input_word(double[] rv, byte[] input, int word_idx, int word_bgn, int word_end) { + if (word_idx >= Input_units_len) return; + byte unit_dlm = Input_units[word_idx]; + int pos = Bry_find_.Find_fwd(input, unit_dlm, word_bgn, word_end); + if (pos != Bry_find_.Not_found) // remove dlms from end of bry; EX: "123'" -> "123" + word_end = pos; + if (!Parse_input_word_is_compass(input[word_bgn])) { // if ( is_numeric( $v ) ) { + double word_val = Bry_.To_double_or(input, word_bgn, word_end, Double_.NaN); + if (!Double_.IsNaN(word_val)) { + if (word_idx > 2) {error = -4; return;} + switch (word_idx) { + case 0: + if (word_val <= -180 || word_val > 180) {error = -5; return;} + rv[0] = word_val; + break; + case 1: + if (word_val < 0 || word_val >= 60) {error = -6; return;} + if (rv[0] != (int)(rv[0])) {error = -7; return;} + rv[1] = word_val; + break; + case 2: + if (word_val < 0 || word_val >= 60) {error = -8; return;} + if (rv[1] != (int)(rv[1])) {error = -9; return;} + rv[2] = word_val; + break; + } + } + else { + error = -3; + return; + } + } + else { // 'NSEW' + word_idx_nsew = word_idx; + byte word_byte = input[word_bgn]; + if (rv[0] < 0) {error = -11; return;} + if (word_end - word_bgn != 1) {error = -3; return;} + switch (dir_id) { + case Dir_long_id: + if (word_byte == Byte_ascii.Ltr_N || word_byte == Byte_ascii.Ltr_S) {error = -12; return;} + break; + case Dir_lat_id: + if (word_byte == Byte_ascii.Ltr_E || word_byte == Byte_ascii.Ltr_W) {error = -12; return;} + break; + case Dir_unknown_id: + if (word_byte == Byte_ascii.Ltr_N || word_byte == Byte_ascii.Ltr_S) this.dir_id = Dir_lat_id; + else this.dir_id = Dir_long_id; + break; + } + if (this.dir_id == Dir_lat_id) { + double rv_0 = rv[0]; + if (rv_0 < -90 || rv_0 > 90) {error = -13; return;} + } + if (word_byte == Byte_ascii.Ltr_S || word_byte == Byte_ascii.Ltr_W) + rv[3] = -1; + } + } + private static byte Parse_dir(byte[] dir) { + if (Bry_.Len_eq_0(dir)) return Dir_unknown_id; + Object dir_obj = Dir_trie.Match_bgn(dir, 0, dir.length); + return dir_obj == null ? Dir_unknown_id : ((Byte_obj_val)dir_obj).Val(); + } + private static int Parse_precision(int val) { // REF.MW: MapSourcesMath.php|newCoord + if (val > -1 && val < 10) return val; + else if (val == -1) return 9; + else return 4; + } + private Bry_bfr tmp_bfr = Bry_bfr_.Reset(32); + public static byte[] Parse_input_normalize(Bry_bfr bfr, byte[] src) { + /* + $w = str_replace( array( '‘', '’', '′' ), "'", $input ); + $w = str_replace( array( "''", '“', '”', '″' ), '"', $w ); + $w = str_replace( '−', '-', $w ); + $w = strtoupper( str_replace( array( '_', '/', "\t", "\n", "\r" ), ' ', $w ) ); + $w = str_replace( array( '°', "'", '"' ), array( '° ', "' ", '" ' ), $w ); + $w = trim( str_replace( array( 'N', 'S', 'E', 'W' ), array( ' N', ' S', ' E', ' W' ), $w ) ); + */ + int src_end = src.length; if (src_end == 0) return null; + src = Trie__normalize__apos.Replace(bfr, src, 0, src_end); // normalize apos separately, since 2 apos can go to quotes; EX: ‘’ -> "; PAGE:it.v:Morro_d'Oro DATE:2015-12-06 + src = Trie__normalize__rest.Replace(bfr, src, 0, src.length); // normalize rest; + return Bry_.Trim(src); + } + private static final byte[] + Bry_deg = Bry_.new_u8("°") + , Bry_quot_mw = Bry_.new_a7(""") + , Bry_quot_wb = Bry_.new_a7(""") // REF:en.w:Module:WikidataCoord + , Bry_apos_mw = Bry_.new_a7("'") + , Bry_apos_wb = Bry_.new_a7("'") // REF:en.w:Module:WikidataCoord + ; + private static final byte Dir_unknown_id = 0, Dir_lat_id = 1, Dir_long_id = 2; + public static final byte[] Dir_lat_bry = Bry_.new_a7("lat"), Dir_long_bry = Bry_.new_a7("long"); + private static final Btrie_slim_mgr Dir_trie = Btrie_slim_mgr.ci_a7() // NOTE:ci.ascii:MW_const.en + .Add_bry_byte(Dir_lat_bry , Dir_lat_id) + .Add_bry_byte(Dir_long_bry , Dir_long_id) + ; + private static final byte[] + Compass_N = new byte[] {Byte_ascii.Ltr_N} + , Compass_E = new byte[] {Byte_ascii.Ltr_E} + , Compass_S = new byte[] {Byte_ascii.Ltr_S} + , Compass_W = new byte[] {Byte_ascii.Ltr_W} + ; + private static final byte Input_byte_degree = Byte_ascii.Slash; // NOTE: ugly cheat to avoid using multi-byte char; note that all "/" are swapped out to " ", so any remaining "/" was added by the normalizer; EX: "123° 4/5" -> "123/ 4 5" + private static final byte[] Input_units = new byte[] {Input_byte_degree, Byte_ascii.Apos, Byte_ascii.Quote, Byte_ascii.Space}; + private static final int Input_units_len = Input_units.length; + private static final Btrie_slim_mgr Trie__normalize__apos = Btrie_slim_mgr.cs() + .Add_replace_many (Byte_ascii.Apos_bry , "‘", "’", "′"); + private static final Btrie_slim_mgr Trie__normalize__rest = Btrie_slim_mgr.cs() + .Add_replace_many ("' " , "'") + .Add_replace_many ("\" " , "\"", "''", "“", "”", "″") + .Add_replace_many (Byte_ascii.Dash_bry , "-", "−") // NOTE: emdash and endash + .Add_replace_many (Byte_ascii.Space_bry , " ", "_", "/", "\t", "\n", "\r") // NOTE: " " =   + .Add_replace_many ("/ " , "°") + .Add_replace_many (" N" , "N", "n") + .Add_replace_many (" S" , "S", "s") + .Add_replace_many (" E" , "E", "e") + .Add_replace_many (" W" , "W", "w"); + public static final Map_math Instance = new Map_math(); +} diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/pfuncs/Wdata_pf_property__basic__tst.java b/400_xowa/src/gplx/xowa/xtns/wbases/pfuncs/Wdata_pf_property__basic__tst.java index 3da038339..600dc8910 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/pfuncs/Wdata_pf_property__basic__tst.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/pfuncs/Wdata_pf_property__basic__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,202 +13,207 @@ 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.wbases.pfuncs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.wbases.*; -import org.junit.*; import gplx.xowa.parsers.*; import gplx.xowa.parsers.tmpls.*; -public class Wdata_pf_property__basic__tst { - @Before public void init() {fxt.Init();} private final Wdata_wiki_mgr_fxt fxt = new Wdata_wiki_mgr_fxt(); - @Test public void String() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_string(1, "a")) - .Add_sitelink("enwiki", "Test_page") - ); - fxt.Test_parse("{{#property:p1}}", "a"); - fxt.Test_parse("{{#property:p2}}", ""); - } - @Test public void Entity() { - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_label("en", "b") - ); - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_entity_qid(1, 2)) - .Add_sitelink("enwiki", "Test_page") - ); - fxt.Test_parse("{{#property:p1}}", "b"); - } - @Test public void Entity_fr() { // PURPOSE: non-English wiki should default to English label if non-English label not available; DATE:2013-12-19 - // set wiki to French - fxt.Wiki().Wdata_wiki_lang_(Bry_.new_a7("fr")); - - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_entity_qid(1, 2)) - .Add_sitelink("frwiki", "Test_page") - ); - - // create wdata page Q2 with label in en (not fr) - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_label("en", "b") - ); - - // parse; should get en label - fxt.Test_parse("{{#property:p1}}", "b"); - } - @Test public void Entity_missing() { // PURPOSE: wiki may refer to entity that no longer exists; EX: {{#property:p1}} which links to Q1, but p1 links to Q2 and Q2 was deleted; DATE:2014-02-01 - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_entity_qid(1, 2)) // create wdata page Q1 with prop entity reference to Q2; note that Q2 is not created - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", ""); // parse; get "" - } - @Test public void Time() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_time(1, "2012-01-02 03:04:05")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "30405 2 Jan 2012"); // NOTE: format is missing ":" b/c test does not init messages for html_wtr; DATE:2015-08-03 - } - @Test public void Geodata() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_geo(1, "6.789", "1.2345")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "1°14'4.2"N, 6°47'20.4"E"); - } - @Test public void Quantity__plus_minus__y() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_quantity(1, "+1234", "1", "+1236", "+1232")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "1,234±2"); - } - @Test public void Quantity__plus_minus__n() { // PURPOSE:do not output ± if lbound == val == ubound; PAGE:en.w:Tintinan DATE:2015-08-02 - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_quantity(1, "+1234", "1", "+1234", "+1234")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "1,234"); - } - @Test public void Quantity__range() { // PURPOSE:do not output ± if lbound == val == ubound; PAGE:en.w:Tintinan DATE:2015-08-02 - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_quantity(1, "+1234", "1", "+1236", "+1233")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "1,233-1,236"); - } - @Test public void Quantity__long() { // PURPOSE: must cast to long for large numbers; EX:{{#property:P1082}} PAGE:en.w:Earth; DATE:2015-08-02 - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_quantity(1, "+4321000000", "1", "4321000000", "4321000000")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "4,321,000,000"); - } - @Test public void Quantity__unit__entity() {// PURPOSE: get entity name; EX:{{#invoke:Wikidata|getUnits|P2386|FETCH_WIKIDATA}} PAGE:en.w:Arecibo_Observatory; DATE:2016-10-11 - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_quantity(1, "+1234", "http://www.wikidata.org/entity/Q2", "+1236", "+1232")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_claims(fxt.Make_claim_string(2, "a")) - .Add_label("en", "meter") - ); - - fxt.Test_parse("{{#property:p1}}", "1,234±2 meter"); - } - @Test public void Quantity__decimal() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_quantity(1, "+1234.50", "1", "+1236.75", "+1232.25")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "1,234.5±2.25"); - } - @Test public void Monolingualtext() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_monolingual(1, "la", "Lorem ipsum dolor sit amet")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "Lorem ipsum dolor sit amet"); - } - @Test public void Novalue() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_novalue(1)) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "no value"); - } - @Test public void Somevalue() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_somevalue(1)) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "unknown value"); - } - @Test public void Multiple() { - fxt.Init__docs__add(fxt.Wdoc("Q1") - .Add_claims(fxt.Make_claim_string(1, "a"), fxt.Make_claim_string(1, "b")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1}}", "a"); // only take first; DATE:2015-08-02 - } - @Test public void Q() { - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_claims(fxt.Make_claim_string(1, "a")) - .Add_sitelink("enwiki", "Test_page") - ); - - fxt.Test_parse("{{#property:p1|q=Q2}}", "a"); - } - @Test public void Of() { - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_claims(fxt.Make_claim_string(1, "a")) - .Add_sitelink("enwiki", "Of_page") - ); - - fxt.Test_parse("{{#property:p1|of=Of_page}}", "a"); - } - @Test public void From() { - fxt.Init__docs__add(fxt.Wdoc("Property:P2") - .Add_claims(fxt.Make_claim_string(1, "a")) - ); - - fxt.Test_parse("{{#property:p1|from=P2}}", "a"); - fxt.Test_parse("{{#property:p1|from=}}", ""); - fxt.Test_parse("{{#property:p1| from = P2 }}", "a"); // PURPOSE: trim ws; ISSUE#:361; DATE:2019-02-11 - } - @Test public void Pid_as_name() { - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_claims(fxt.Make_claim_string(1, "a")) - .Add_sitelink("enwiki", "Test_page") - ); - fxt.Init_pids_add("en", "astronomic symbol", 1); - - fxt.Test_parse("{{#property:astronomic symbol}}", "a"); - } - @Test public void Empty_arg() { // PURPOSE: {{#property:p1|}} should not fail / warn; DATE:2013-11-15 - fxt.Init__docs__add(fxt.Wdoc("Q2") - .Add_claims(fxt.Make_claim_string(1, "a")) - .Add_sitelink("enwiki", "Test_page") - ); - fxt.Init_pids_add("en", "astronomic symbol", 1); - - fxt.Test_parse("{{#property:p1|}}", "a"); - } - @Test public void Parse_pid() { - fxt.Test_parse_pid ("p123" , 123); // basic - fxt.Test_parse_pid ("P123" , 123); // uppercase - fxt.Test_parse_pid_null ("population"); // name test - fxt.Test_parse_pid_null ("123"); // missing p - fxt.Test_parse_pid_null (""); // empty String test - } -} +package gplx.xowa.xtns.wbases.pfuncs; + +import gplx.Bry_; +import gplx.xowa.xtns.wbases.Wdata_wiki_mgr_fxt; +import org.junit.Before; +import org.junit.Test; + +public class Wdata_pf_property__basic__tst { + @Before public void init() {fxt.Init();} private final Wdata_wiki_mgr_fxt fxt = new Wdata_wiki_mgr_fxt(); + @Test public void String() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_string(1, "a")) + .Add_sitelink("enwiki", "Test_page") + ); + fxt.Test_parse("{{#property:p1}}", "a"); + fxt.Test_parse("{{#property:p2}}", ""); + } + @Test public void Entity() { + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_label("en", "b") + ); + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_entity_qid(1, 2)) + .Add_sitelink("enwiki", "Test_page") + ); + fxt.Test_parse("{{#property:p1}}", "b"); + } + @Test public void Entity_fr() { // PURPOSE: non-English wiki should default to English label if non-English label not available; DATE:2013-12-19 + // set wiki to French + fxt.Wiki().Wdata_wiki_lang_(Bry_.new_a7("fr")); + + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_entity_qid(1, 2)) + .Add_sitelink("frwiki", "Test_page") + ); + + // create wdata page Q2 with label in en (not fr) + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_label("en", "b") + ); + + // parse; should get en label + fxt.Test_parse("{{#property:p1}}", "b"); + } + @Test public void Entity_missing() { // PURPOSE: wiki may refer to entity that no longer exists; EX: {{#property:p1}} which links to Q1, but p1 links to Q2 and Q2 was deleted; DATE:2014-02-01 + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_entity_qid(1, 2)) // create wdata page Q1 with prop entity reference to Q2; note that Q2 is not created + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", ""); // parse; get "" + } + @Test public void Time() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_time(1, "2012-01-02 03:04:05")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "30405 2 Jan 2012"); // NOTE: format is missing ":" b/c test does not init messages for html_wtr; DATE:2015-08-03 + } + @Test public void Geodata() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_geo(1, "6.789", "1.2345")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "1°14'4.2"N, 6°47'20.4"E"); + } + @Test public void Quantity__plus_minus__y() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_quantity(1, "+1234", "1", "+1236", "+1232")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "1,234±2"); + } + @Test public void Quantity__plus_minus__n() { // PURPOSE:do not output ± if lbound == val == ubound; PAGE:en.w:Tintinan DATE:2015-08-02 + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_quantity(1, "+1234", "1", "+1234", "+1234")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "1,234"); + } + @Test public void Quantity__range() { // PURPOSE:do not output ± if lbound == val == ubound; PAGE:en.w:Tintinan DATE:2015-08-02 + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_quantity(1, "+1234", "1", "+1236", "+1233")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "1,233-1,236"); + } + @Test public void Quantity__long() { // PURPOSE: must cast to long for large numbers; EX:{{#property:P1082}} PAGE:en.w:Earth; DATE:2015-08-02 + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_quantity(1, "+4321000000", "1", "4321000000", "4321000000")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "4,321,000,000"); + } + @Test public void Quantity__unit__entity() {// PURPOSE: get entity name; EX:{{#invoke:Wikidata|getUnits|P2386|FETCH_WIKIDATA}} PAGE:en.w:Arecibo_Observatory; DATE:2016-10-11 + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_quantity(1, "+1234", "http://www.wikidata.org/entity/Q2", "+1236", "+1232")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_claims(fxt.Make_claim_string(2, "a")) + .Add_label("en", "meter") + ); + + fxt.Test_parse("{{#property:p1}}", "1,234±2 meter"); + } + @Test public void Quantity__decimal() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_quantity(1, "+1234.50", "1", "+1236.75", "+1232.25")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "1,234.5±2.25"); + } + @Test public void Monolingualtext() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_monolingual(1, "la", "Lorem ipsum dolor sit amet")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "Lorem ipsum dolor sit amet"); + } + @Test public void Novalue() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_novalue(1)) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "no value"); + } + @Test public void Somevalue() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_somevalue(1)) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "unknown value"); + } + @Test public void Multiple() { + fxt.Init__docs__add(fxt.Wdoc("Q1") + .Add_claims(fxt.Make_claim_string(1, "a"), fxt.Make_claim_string(1, "b")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1}}", "a"); // only take first; DATE:2015-08-02 + } + @Test public void Q() { + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_claims(fxt.Make_claim_string(1, "a")) + .Add_sitelink("enwiki", "Test_page") + ); + + fxt.Test_parse("{{#property:p1|q=Q2}}", "a"); + } + @Test public void Of() { + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_claims(fxt.Make_claim_string(1, "a")) + .Add_sitelink("enwiki", "Of_page") + ); + + fxt.Test_parse("{{#property:p1|of=Of_page}}", "a"); + } + @Test public void From() { + fxt.Init__docs__add(fxt.Wdoc("Property:P2") + .Add_claims(fxt.Make_claim_string(1, "a")) + ); + + fxt.Test_parse("{{#property:p1|from=P2}}", "a"); + fxt.Test_parse("{{#property:p1|from=}}", ""); + fxt.Test_parse("{{#property:p1| from = P2 }}", "a"); // PURPOSE: trim ws; ISSUE#:361; DATE:2019-02-11 + } + @Test public void Pid_as_name() { + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_claims(fxt.Make_claim_string(1, "a")) + .Add_sitelink("enwiki", "Test_page") + ); + fxt.Init_pids_add("en", "astronomic symbol", 1); + + fxt.Test_parse("{{#property:astronomic symbol}}", "a"); + } + @Test public void Empty_arg() { // PURPOSE: {{#property:p1|}} should not fail / warn; DATE:2013-11-15 + fxt.Init__docs__add(fxt.Wdoc("Q2") + .Add_claims(fxt.Make_claim_string(1, "a")) + .Add_sitelink("enwiki", "Test_page") + ); + fxt.Init_pids_add("en", "astronomic symbol", 1); + + fxt.Test_parse("{{#property:p1|}}", "a"); + } + @Test public void Parse_pid() { + fxt.Test_parse_pid ("p123" , 123); // basic + fxt.Test_parse_pid ("P123" , 123); // uppercase + fxt.Test_parse_pid_null ("population"); // name test + fxt.Test_parse_pid_null ("123"); // missing p + fxt.Test_parse_pid_null (""); // empty String test + } +}