diff --git a/100_core/src/gplx/Int_.java b/100_core/src/gplx/Int_.java index d2380050b..d6ea40a62 100644 --- a/100_core/src/gplx/Int_.java +++ b/100_core/src/gplx/Int_.java @@ -28,6 +28,7 @@ public class Int_ { , Null = Int_.Min_value , Base1 = 1 // for super 1 lists / arrays; EX: PHP; [a, b, c]; [1] => a , Offset_1 = 1 // common symbol for + 1 after current pos; EX: String_.Mid(lhs + Offset_1, rhs) + , Zero = 0 ; public static int Cast(Object obj) { diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java index 5b42241bf..e5ace16c5 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray.java @@ -33,6 +33,7 @@ public class XophpArray implements Bry_bfr_able { int len = hash.Len(); return len == 0 ? null : ((XophpArrayItm)hash.Get_at(len - 1)).Val(); } + public boolean Eq_to_new() {return hash.Count() == 0;}// same as "array === []" public void unset(int key) {unset(Int_.To_str(key));} public void unset(String key) { hash.Del(key); @@ -103,9 +104,17 @@ public class XophpArray implements Bry_bfr_able { } return this; } + public void Concat_str(int i, String s) { + this.Set(i, this.Get_at_str(i) + s); + } + public XophpArray Get_at_ary_or_null(int i) { + Object rv = Get_at(i); + return Type_.Eq_by_obj(rv, XophpArray.class) ? (XophpArray)rv : null; + } public XophpArray Get_at_ary(int i) {return (XophpArray)Get_at(i);} - public String Get_at_str(int i) {return (String)Get_at(i);} + public boolean Get_at_bool(int i) {return Bool_.Cast(Get_at(i));} public int Get_at_int(int i) {return Int_.Cast(Get_at(i));} + public String Get_at_str(int i) {return (String)Get_at(i);} public Object Get_at(int i) { if (i < 0 || i >= hash.Len()) return null; XophpArrayItm itm = (XophpArrayItm)hash.Get_at(i); @@ -123,10 +132,15 @@ public class XophpArray implements Bry_bfr_able { } public Object Get_by_obj(Object key) {return Get_by(Object_.Xto_str_strict_or_null(key));} public Object Get_by(int key) {return Get_by(Int_.To_str(key));} + public boolean Get_by_bool_or(String key, boolean or) {Object rv = this.Get_by(key); return rv == null ? or : Bool_.Cast(rv);} public boolean Get_by_bool(String key) {return Bool_.Cast(this.Get_by(key));} + public int Get_by_int_or(String key, int or) {Object rv = this.Get_by(key); return rv == null ? or : Int_.Cast(rv);} public int Get_by_int(String key) {return Int_.Cast(this.Get_by(key));} + public XophpArray Get_by_ary_or(String key, XophpArray or) {Object rv = this.Get_by(key); return rv == null ? or : (XophpArray)rv;} public XophpArray Get_by_ary(String key) {return (XophpArray)this.Get_by(key);} public String Get_by_str(char key) {return (String)this.Get_by(Char_.To_str(key));} + public String Get_by_str(int key) {return (String)this.Get_by(Int_.To_str(key));} + public String Get_by_str_or(String key, String or) {Object rv = this.Get_by(key); return rv == null ? or : (String)rv;} public String Get_by_str(String key) {return (String)this.Get_by(key);} public Object Get_by(String key) { XophpArrayItm itm = (XophpArrayItm)hash.Get_by(key); @@ -180,6 +194,35 @@ public class XophpArray implements Bry_bfr_able { } return rv; } + @Override public boolean equals(Object obj) { + if (obj == null) return false; + if (!Type_.Eq_by_obj(obj, XophpArray.class)) return false; + XophpArray comp = (XophpArray)obj; + + // compare lens + int this_len = this.Len(); + int comp_len = comp.Len(); + if (this_len != comp_len) return false; + + // loop items + for (int i = 0; i < this_len; i++) { + XophpArrayItm this_itm = this.Get_at_itm(i); + XophpArrayItm comp_itm = comp.Get_at_itm(i); + if (!Object_.Eq(this_itm, comp_itm)) + return false; + } + return true; + } + @Override public int hashCode() { + int rv = 0; + int len = this.Len(); + for (int i = 0; i < len; i++) { + XophpArrayItm itm = this.Get_at_itm(i); + rv = (31 * rv) + itm.hashCode(); + } + return rv; + } + public static XophpArray New(Object... vals) { XophpArray rv = new XophpArray(); for (Object val : vals) diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java index 9ee058afe..1d2afa237 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArrayItm.java @@ -39,6 +39,36 @@ public class XophpArrayItm implements Bry_bfr_able { bfr.Add_obj(val).Add_byte_space(); } } + @Override public boolean equals(Object obj) { + if (obj == null) return false; + XophpArrayItm comp = (XophpArrayItm)obj; + + // compare key + if (key_is_int) { + if (this.key_as_int != comp.key_as_int) + return false; + } + else { + if (!String_.Eq(this.key, comp.key)) + return false; + } + + // compare val + if (this.val == null && comp.val != null) + return false; + else if (this.val != null && comp.val == null) + return false; + else if (this.val == null && comp.val == null) + return true; + else + return Object_.Eq(this.val, comp.val); + } + @Override public int hashCode() { + int rv = 0; + rv = (31 * rv) + (key_is_int ? key_as_int : key.hashCode()); + rv = (31 * rv) + val.hashCode(); + return rv; + } public static XophpArrayItm New_int(int key, Object val) {return new XophpArrayItm(Bool_.Y, key, Int_.To_str(key), val);} public static XophpArrayItm New_str(String key, Object val) {return new XophpArrayItm(Bool_.N, -1, key , val);} diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java index 4d00dee05..495c01680 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java @@ -152,6 +152,11 @@ public class XophpArray_ { public static boolean array_is_empty(Ordered_hash array) { return array.Len() == 0; } + + public static boolean array_key_exists(String key, XophpArray array) {return array.Has(key);} + public static boolean array_key_exists(int key, XophpArray array) {return array.Has(Int_.To_str(key));} + + public static void unset(XophpArray array, int i) {array.unset(i);} public static void unset(Ordered_hash array, Object key) { array.Del(key); } @@ -164,12 +169,6 @@ public class XophpArray_ { rv[i - 1] = ary[i]; return rv; } - public static boolean in_array(String needle, String[] haystack) { - for (String hay : haystack) - if (String_.Eq(hay, needle)) - return true; - return false; - } // REF.PHP:https://www.php.net/manual/en/function.array-map.php public static XophpArray array_map(XophpCallbackOwner callback_owner, String method, XophpArray array) { @@ -204,7 +203,47 @@ public class XophpArray_ { return sb.To_str_and_clear(); } - public static int count(XophpArray array) { - return array.count(); + public static int count(XophpArray array) {return array.count();} + public static boolean count_bool(XophpArray array) {return array.count_bool();} + public static Object array_pop(XophpArray array) {return array.pop();} + public static boolean isset(XophpArray array, int key) {return XophpObject_.isset_obj(array.Get_at(key));} + public static boolean isset(XophpArray array, String key) {return XophpObject_.isset_obj(array.Get_by(key));} + public static boolean is_array(XophpArray array) {return array != null;} + + // REF.PHP: https://www.php.net/manual/en/function.in-array.php + public static boolean in_array(Object needle, XophpArray haystack) {return in_array(needle, haystack, false);} + public static boolean in_array(Object needle, XophpArray haystack, boolean strict) { + // if strict, cache needleType + Class> needleType = null; + if (strict && needle != null) { + needleType = Type_.Type_by_obj(needle); + } + + // loop haystack to find match + int haystack_len = haystack.Len(); + for (int i = 0; i < haystack_len; i++) { + Object val = haystack.Get_at(i); + + // if strict, compare types + if (strict) { + if (needle != null && val == null) { + return false; + } + else if (needle == null && val != null) { + return false; + } + else if (needle != null && val != null) { + if (!Type_.Eq_by_obj(val, needleType)) { + return false; + } + } + } + + // compare objects + if (Object_.Eq(needle, val)) { + return true; + } + } + return false; } } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java index 631bb8e84..b3c9c221a 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java @@ -257,6 +257,25 @@ public class XophpArray__tst { // REF:https://www.php.net/manual/en/function.arr , XophpArray_.implode(" ", orig) ); } + @Test public void in_array() { + // PHP samples + XophpArray array; + // Example #1 + array = XophpArray.New("Mac", "NT", "Irix", "Linux"); + Gftest.Eq__bool(Bool_.Y, XophpArray_.in_array("Irix", array)); + Gftest.Eq__bool(Bool_.N, XophpArray_.in_array("mac" , array)); + + // Example #2 + array = XophpArray.New(12.4d, 1.13d); + Gftest.Eq__bool(Bool_.N, XophpArray_.in_array("12.4", array, true)); + Gftest.Eq__bool(Bool_.Y, XophpArray_.in_array( 1.13d, array, true)); + + // Example #3 + array = XophpArray.New(XophpArray.New('p', 'h'), XophpArray.New('p', 'r'), 'o'); + Gftest.Eq__bool(Bool_.Y, XophpArray_.in_array(XophpArray.New('p', 'h'), array)); + Gftest.Eq__bool(Bool_.N, XophpArray_.in_array(XophpArray.New('f', 'i'), array)); + Gftest.Eq__bool(Bool_.Y, XophpArray_.in_array('o', array)); + } } class XophpArray__fxt { public XophpArray Make() {return new XophpArray();} diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpBool_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpBool_.java index 1f51dd3ca..de98c0490 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpBool_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpBool_.java @@ -15,6 +15,7 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; public class XophpBool_ { + public static final boolean Null = false; public static boolean is_true(byte[] val) {return val != null && is_true(String_.new_u8(val));} public static boolean is_true(String val) { // REF: https://www.php.net/manual/en/language.types.boolean.php#language.types.boolean.casting diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpDouble_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpDouble_.java new file mode 100644 index 000000000..83206e743 --- /dev/null +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpDouble_.java @@ -0,0 +1,20 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2017 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.xowa.mediawiki; import gplx.*; import gplx.xowa.*; +public class XophpDouble_ { + public static int intval(double val) {return (int)val;} +} + diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpInt_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpInt_.java index d983d02a8..dce2666eb 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpInt_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpInt_.java @@ -16,7 +16,8 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; public class XophpInt_ { public static final int False = 0; // REF.PHP:https://www.php.net/manual/en/language.types.boolean.php - public static boolean is_true(int val) {return val != -1;} // handles code like "if ($var === false)" where var is an Object; + public static boolean is_true(int val) {return val != 0;} // handles code like "if ($var)" where var is an Object; + public static boolean is_false(int val) {return val < 0;} // handles XophpInt_.False as well as strpos.notFound (-1) public static String strval(int number) { return Int_.To_str(number); } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java index a1f6627f4..ba7e83cd9 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpObject_.java @@ -17,6 +17,7 @@ package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; public class XophpObject_ { public static final Object False = null; // handles code like "if ($var === false)" where var is an Object; public static boolean is_true(Object val) {return val != null;} + public static boolean is_false(Object val) {return val == null;} public static boolean is_null(Object val) {return val == null;} // REF.PHP:http://php.net/manual/en/function.empty.php diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpRegex_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpRegex_.java index 96fd05966..2b5a3fc7e 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpRegex_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpRegex_.java @@ -17,8 +17,25 @@ package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; import gplx.langs.regxs.*; import gplx.core.strings.*; import gplx.core.primitives.*; import gplx.core.bits.*; public class XophpRegex_ { + // REF.PHP: https://www.php.net/manual/en/function.preg-quote.php + // The special regular expression characters are: + private static final Hash_adp preg_quote_hash = Hash_adp_.New().Add_many_as_key_and_val + ('.', '\\', '+', '*', '?', '[', '^', ']', '$', '(', ')', '{', '}', '=', '!', '<', '>', '|', ':', '-', '#'); + public static String preg_quote(String str, String delimiter) {// NOTE: "String delimiter" not used b/c Java / XO does not allow symbolic quotes; EX: "/abc/i" + String_bldr sb = String_bldr_.new_(); + int len = String_.Len(str); + for (int i = 0; i < len; i++) { + char c = String_.CharAt(str, i); + if (preg_quote_hash.Has(c)) { + sb.Add("\\"); + } + sb.Add(c); + } + return sb.To_str_and_clear(); + } public static boolean preg_match_bool(Regx_adp pattern, int modifier, String subject) {return preg_match_bool(pattern, modifier, subject, null, 0, 0);} public static boolean preg_match_bool(Regx_adp pattern, String subject, XophpArray matches, int flags, int offset) {return preg_match(pattern, MODIFIER_NONE, subject, matches, flags, offset) == FOUND;} + public static boolean preg_match_bool(String pattern, int modifier, String subject, XophpArray matches, int flags, int offset) {return preg_match(Regx_adp_.new_(pattern), modifier, subject, matches, flags, offset) == FOUND;} public static boolean preg_match_bool(Regx_adp pattern, int modifier, String subject, XophpArray matches, int flags, int offset) {return preg_match(pattern, modifier, subject, matches, flags, offset) == FOUND;} public static int preg_match(Regx_adp pattern, String subject) {return preg_match(pattern, MODIFIER_NONE, subject, null, 0, 0);} public static int preg_match(Regx_adp pattern, int modifier, String subject) {return preg_match(pattern, modifier, subject, null, 0, 0);} @@ -61,8 +78,8 @@ public class XophpRegex_ { private static void preg_match_fill(String subject, XophpArray matches, int flags, Regx_match match, String match_str, Regx_group[] grps, int grps_len) { for (int i = 0; i < grps_len; i++) { Regx_group grp = grps[i]; - if (!grp.Rslt()) continue; // ignore non matches in group; EX: "1" and "^-?(([0-9]+)(?:\\.([0-9]+))?)" returns a match=false for group(2) - String grp_match = grp.Val(); + // TOMBSTONE: if (!grp.Rslt()) continue; // ignore non matches in group; EX: "1" and "^-?(([0-9]+)(?:\\.([0-9]+))?)" returns a match=false for group(2) + String grp_match = grp.Rslt() ? grp.Val() : ""; if (flags == PREG_OFFSET_CAPTURE) { matches.Add(XophpArray.New(grp_match, grp.Bgn())); } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpRegex__tst.java b/400_xowa/src/gplx/xowa/mediawiki/XophpRegex__tst.java index 7fbf58d13..54b6c1a59 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpRegex__tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpRegex__tst.java @@ -51,6 +51,10 @@ public class XophpRegex__tst { .Add("baz") ); } + @Test public void preg_quote() { + Gftest.Eq__str("abc", XophpRegex_.preg_quote("abc", "/")); + Gftest.Eq__str("\\.\\\\\\+\\*\\?\\[\\^\\]\\$\\(\\)\\{\\}\\=\\!\\<\\>\\|\\:\\-\\#", XophpRegex_.preg_quote(".\\+*?[^]$(){}=!<>|:-#", "/")); + } } class XophpRegex__fxt { private String_bldr print_php = null;//String_bldr_.new_(); diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpStringRef.java b/400_xowa/src/gplx/xowa/mediawiki/XophpStringRef.java new file mode 100644 index 000000000..11f6dd46b --- /dev/null +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpStringRef.java @@ -0,0 +1,21 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2017 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.xowa.mediawiki; import gplx.*; import gplx.xowa.*; +public class XophpStringRef { + private String val; + public XophpStringRef(String val) {this.val = val;} + public String Get() {return val;} +} diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java index 66892c6d9..0bc04fca1 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java @@ -18,9 +18,13 @@ import gplx.core.btries.*; import gplx.core.intls.*; import gplx.objects.strings.unicodes.*; import gplx.core.primitives.*; +import gplx.objects.strings.bfrs.*; public class XophpString_ implements XophpCallbackOwner { public static final String False = null; - public static boolean is_true(String s) {return s != null;} // handles code like "if ($var)" where var is an Object; + public static boolean is_true (String s) {return s != null;} // handles code like "if ($var)" where var is an Object; + public static boolean is_false(String s) {return s == null;} + public static boolean eq(String lhs, String rhs) {return String_.Eq(lhs, rhs);} + public static boolean eq_not(String lhs, String rhs) {return !String_.Eq(lhs, rhs);} // REF.PHP: https://www.php.net/manual/en/function.strpos.php public static int strpos(String haystack, String needle) {return strpos(haystack, needle, 0);} @@ -80,16 +84,36 @@ public class XophpString_ implements XophpCallbackOwner { } return rv; } - public static int strspn(String subject, Hash_adp mask, int start) {return strspn(subject, mask, start, Int_.Null);} + public static int strspn(String subject, Hash_adp mask, int start) {return strspn(subject, mask, start, Int_.Zero);} public static int strspn(String subject, Hash_adp mask, int start, int length) { int subject_len = String_.Len(subject); - - // get subject_end + start = strspn__start(start, subject_len); + int subject_end = strspn__subject_end(start, length, subject_len); + return strspn__rslt(Bool_.Y, subject, mask, start, subject_end); + } + // REF.PHP:https://www.php.net/manual/en/function.strcspn.php + public static int strcspn(String subject, Hash_adp mask) {return strcspn(subject, mask, Int_.Zero, Int_.Zero);} + public static int strcspn(String subject, Hash_adp mask, int start) {return strcspn(subject, mask, start, Int_.Zero);} + public static int strcspn(String subject, Hash_adp mask, int start, int length) { + int subject_len = String_.Len(subject); + start = strspn__start(start, subject_len); + int subject_end = strspn__subject_end(start, length, subject_len); + return strspn__rslt(Bool_.N, subject, mask, start, subject_end); + } + private static int strspn__start(int start, int subject_len) { + if (start < 0) { // adjust start if -1 + start = subject_len + start; + if (start < 0) + start = 0; + } + return start; + } + private static int strspn__subject_end(int start, int length, int subject_len) { int subject_end = 0; - if (length == Int_.Null) { + if (length == Int_.Zero) { subject_end = subject_len; } - else if (length < 0) { + else if (length < Int_.Zero) { subject_end = subject_len + length; // If length is given and is negative, then subject will be examined from the starting position up to length characters from the end of subject. if (subject_end < start) subject_end = start; @@ -99,9 +123,11 @@ public class XophpString_ implements XophpCallbackOwner { if (subject_end > subject_len) subject_end = subject_len; } - + return subject_end; + } + private static int strspn__rslt(boolean is_strspn, String subject, Hash_adp mask, int start, int subject_end) { // loop subject until encountering character not in mask - int rv = 0; + int strspn_rv = 0; int i = start; while (i < subject_end) { char subject_char = String_.CharAt(subject, i); @@ -117,14 +143,21 @@ public class XophpString_ implements XophpCallbackOwner { } if (mask.Has(mask_key)) { - rv++; + if (is_strspn) { + strspn_rv++; + } + else { + break; + } } else { - break; + if (is_strspn) { + break; + } } i++; } - return rv; + return is_strspn ? strspn_rv : i - start; } public static int strspn_fwd__ary(byte[] src, boolean[] find, int bgn, int max, int src_len) { if (max == -1) max = src_len; @@ -389,17 +422,15 @@ public class XophpString_ implements XophpCallbackOwner { rv[rv_idx] = trim_pos; } } - public static String str_repeat(String val, int count) { - int val_len = String_.Len(val); - int chry_len = val_len * count; - char[] chry = new char[chry_len]; - for (int i = 0; i < count; i++) { - for (int j = 0; j < val_len; j++) { - chry[(i * val_len) + j] = String_.CharAt(val, j); - } + // REF.PHP: https://www.php.net/manual/en/function.str-repeat.php + public static String str_repeat(String input, int multiplier) { + String_bfr sb = new String_bfr(); + for (int i = 0; i < multiplier; i++) { + sb.Add(input); } - return String_.new_charAry_(chry, 0, chry_len); + return sb.To_str_and_clear(); } + public static boolean is_string(Object o) { return String_.as_(o) != null; } @@ -512,6 +543,26 @@ public class XophpString_ implements XophpCallbackOwner { return b >= 128 && b <= 255; } } + + // REF.PHP: https://www.php.net/manual/en/function.strrev.php + public static String strrev(String src) { + String_bfr sb = new String_bfr(); + Ustring usrc = Ustring_.New_codepoints(src); + int usrc_len = usrc.Len_in_data(); + for (int i = usrc_len - 1; i > -1; i--) { + int c = usrc.Get_data(i); + sb.Add_char_by_code(c); + } + return sb.To_str_and_clear(); + } + + public static String Char_as_str(String s, int idx) { + return Char_.To_str(String_.CharAt(s, idx)); + } + public static boolean Char_eq(String s, int idx, String comp) { + return String_.Eq(Char_as_str(s, idx), comp); + } + public Object Callback(String method, Object... args) { if (String_.Eq(method, "strtoupper")) { String val = (String)args[0]; diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java b/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java index 72ff9fd0e..db7de50d6 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java @@ -60,12 +60,6 @@ public class XophpString__tst { fxt.Test__strpos("aba", "a", 1, 2); fxt.Test__strpos("aba", "a", -2, 2); } - @Test public void strspn() { - fxt.Test__strspn("42 is the answer to the 128th question", fxt.Init__strspn_hash("1234567890"), 0, Int_.Min_value, 2); - fxt.Test__strspn("foo", fxt.Init__strspn_hash("o"), 0, Int_.Min_value, 0); - fxt.Test__strspn("foo", fxt.Init__strspn_hash("o"), 1, 2, 2); - fxt.Test__strspn("foo", fxt.Init__strspn_hash("o"), 1, 1, 1); - } @Test public void rtrim() { // pad is 0, 1 char fxt.Test__rtrim("0100", "", "0100"); // empty pad returns String @@ -151,6 +145,36 @@ public class XophpString__tst { fxt.Test__Fmt("a$0b", "a$0b", "z"); // invalid identifier fxt.Test__Fmt("a0", "a$xyz0b", "0"); // long identifier } + @Test public void strrev() { + fxt.Test__strrev("", ""); + fxt.Test__strrev("Hello world!", "!dlrow olleH"); + fxt.Test__strrev("☆❤world", "dlrow❤☆"); + fxt.Test__strrev("a¢€𤭢b", "b𤭢€¢a"); + } + @Test public void str_repeat() { + fxt.Test__str_repeat("-=", 10, "-=-=-=-=-=-=-=-=-=-="); + } + @Test public void strspn() { + fxt.Test__strspn(1, "<-", "abc", "abc", -1); + + // PHP samples + fxt.Test__strspn(2, "<-", "42 is the answer to the 128th question", "1234567890"); + fxt.Test__strspn(0, "<-", "foo", "o"); + fxt.Test__strspn(2, "<-", "foo", "o", 1, 2); + fxt.Test__strspn(1, "<-", "foo", "o", 1, 1); + } + @Test public void strcspn() { + fxt.Test__strcspn(3, "abc", "x", 0); + fxt.Test__strcspn(3, "abc", "x", -5); + + // PHP samples + fxt.Test__strcspn(0, "abcd", "apple"); + fxt.Test__strcspn(0, "abcd", "banana"); + fxt.Test__strcspn(2, "hello", "l"); + fxt.Test__strcspn(2, "hello", "world"); + fxt.Test__strcspn(5, "abcdhelloabcd", "abcd", -9); + fxt.Test__strcspn(4, "abcdhelloabcd", "abcd", -9, -5); + } } class XophpString__fxt { public void Test_strspn_fwd__byte(String src_str, byte find, int bgn, int max, int expd) { @@ -191,8 +215,10 @@ class XophpString__fxt { Gftest.Eq__int(expd, XophpString_.strpos(haystack, needle, offset)); } public Hash_adp Init__strspn_hash(String mask) {return XophpString_.strspn_hash(mask);} - public void Test__strspn(String subject, Hash_adp mask, int start, int length, int expd) { - int actl = XophpString_.strspn(subject, mask, start, length); + public void Test__strspn(int expd, String ignore, String subject, String mask) {Test__strspn(expd, ignore, subject, mask, Int_.Zero, Int_.Zero);} + public void Test__strspn(int expd, String ignore, String subject, String mask, int start) {Test__strspn(expd, ignore, subject, mask, start, Int_.Zero);} + public void Test__strspn(int expd, String ignore, String subject, String mask, int start, int length) { + int actl = XophpString_.strspn(subject, XophpString_.strspn_hash(mask), start, length); Gftest.Eq__int(expd, actl); } public void Test__rtrim(String str, String character_mask, String expd) { @@ -222,4 +248,15 @@ class XophpString__fxt { public void Test__Fmt(String expd, String fmt, Object... args) { Gftest.Eq__str(expd, XophpString_.Fmt(fmt, args)); } + public void Test__strrev(String src, String expd) { + Gftest.Eq__str(expd, XophpString_.strrev(src)); + } + public void Test__str_repeat(String input, int multiplier, String expd) { + Gftest.Eq__str(expd, XophpString_.str_repeat(input, multiplier)); + } + public void Test__strcspn(int expd, String subject, String mask) {Test__strcspn(expd, subject, mask, Int_.Zero, Int_.Zero);} + public void Test__strcspn(int expd, String subject, String mask, int start) {Test__strcspn(expd, subject, mask, start, Int_.Zero);} + public void Test__strcspn(int expd, String subject, String mask, int start, int length) { + Gftest.Eq__int(expd, XophpString_.strcspn(subject, XophpString_.strspn_hash(mask), start, length)); + } } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpType_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpType_.java index 42a809b68..b0b10af55 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpType_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpType_.java @@ -49,6 +49,6 @@ public class XophpType_ { return Type_.Eq_by_obj(o, XophpArray.class); } public static boolean instance_of(Object o, Class> t) { - return Type_.Eq_by_obj(o, t); + return Type_.Eq_by_obj(o, t) || Type_.Is_assignable_from_by_obj(o, t); } } diff --git a/400_xowa/src/gplx/xowa/mediawiki/extensions/JsonConfig/includes/JCContentHandler.java b/400_xowa/src/gplx/xowa/mediawiki/extensions/JsonConfig/includes/JCContentHandler.java index 416d30a2c..e3073df88 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/extensions/JsonConfig/includes/JCContentHandler.java +++ b/400_xowa/src/gplx/xowa/mediawiki/extensions/JsonConfig/includes/JCContentHandler.java @@ -25,7 +25,7 @@ class JCContentHandler extends TextContentHandler { /** * @param String $modelId */ public void __construct(String modelId, JCSingleton singleton) { - super.__construct(modelId, XomwDefines.CONTENT_FORMAT_JSON, CONTENT_FORMAT_JSON_PRETTY); + super.__construct(modelId, XophpArray.New(XomwDefines.CONTENT_FORMAT_JSON, CONTENT_FORMAT_JSON_PRETTY)); this.singleton = singleton; } diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwDefaultSettings.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwDefaultSettings.java index 04fa053a0..de0b677a8 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwDefaultSettings.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwDefaultSettings.java @@ -3036,7 +3036,7 @@ public class XomwDefaultSettings { // /** // * Whether to enable language variant conversion. // */ -// $wgDisableLangConversion = false; + public static boolean wgDisableLangConversion = false; // // /** // * Whether to enable language variant conversion for links. @@ -4146,19 +4146,19 @@ public class XomwDefaultSettings { // * When the limit is exceeded, an exception is thrown. // */ // $wgMaxGeneratedPPNodeCount = 1000000; -// -// /** -// * Maximum recursion depth for templates within templates. -// * The current parser adds two levels to the PHP call stack for each template, -// * and xdebug limits the call stack to 100 by default. So this should hopefully -// * stop the parser before it hits the xdebug limit. -// */ -// $wgMaxTemplateDepth = 40; + + /** + * Maximum recursion depth for templates within templates. + * The current parser adds two levels to the PHP call stack for each template, + * and xdebug limits the call stack to 100 by default. So this should hopefully + * stop the parser before it hits the xdebug limit. + */ + public static final int wgMaxTemplateDepth = 40; /** * @see $wgMaxTemplateDepth */ - public static int wgMaxPPExpandDepth = 40; + public static final int wgMaxPPExpandDepth = 40; // /** // * URL schemes that should be recognized as valid by wfParseUrl(). diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitle.java b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitle.java index 0b7be2160..8884fde3e 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitle.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/XomwTitle.java @@ -252,7 +252,8 @@ public class XomwTitle { * @throws InvalidArgumentException * @return Title|null Title or null on an error. */ - public static XomwTitle newFromText(XomwEnv env, byte[] text) {return newFromText(env, text, XomwDefines.NS_MAIN);} + public static XomwTitle newFromText(XomwEnv env, byte[] text) {return newFromText(env, text, XomwDefines.NS_MAIN);} + public static XomwTitle newFromText(XomwEnv env, String text, int defaultNamespace) {return newFromText(env, Bry_.new_u8(text));} private static XomwTitle newFromText(XomwEnv env, byte[] text, int defaultNamespace) { // DWIM: Integers can be passed in here when page titles are used as array keys. // XO.MW.SKIP:STRONGCAST @@ -1454,6 +1455,7 @@ public class XomwTitle { } return this.mPrefixedText; } + public String getPrefixedTextStr() {return String_.new_u8(getPrefixedText());} // /** // * Return a String representation of this title diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/content/ContentHandler.java b/400_xowa/src/gplx/xowa/mediawiki/includes/content/ContentHandler.java index b613800db..eb753a809 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/content/ContentHandler.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/content/ContentHandler.java @@ -358,7 +358,7 @@ public abstract class ContentHandler { /** * @var String[] */ - protected String[] mSupportedFormats; + protected XophpArray mSupportedFormats; /** * Constructor, initializing the ContentHandler instance with its model ID @@ -369,7 +369,7 @@ public abstract class ContentHandler { * @param String[] $formats List for supported serialization formats * (typically as MIME types) */ - @gplx.Virtual public void __construct(String modelId, String... formats) { + @gplx.Virtual public void __construct(String modelId, XophpArray formats) { this.mModelID = modelId; this.mSupportedFormats = formats; } @@ -495,7 +495,7 @@ public abstract class ContentHandler { * * @return String[] List of serialization formats as MIME type like strings */ - public String[] getSupportedFormats() { + public XophpArray getSupportedFormats() { return this.mSupportedFormats; } diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/content/TextContentHandler.java b/400_xowa/src/gplx/xowa/mediawiki/includes/content/TextContentHandler.java index 0cd14f6e4..d3c16c6f9 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/content/TextContentHandler.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/content/TextContentHandler.java @@ -14,13 +14,11 @@ 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.mediawiki.includes.content; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; -public class TextContentHandler extends ContentHandler { // @codingStandardsIgnoreStart bug 57585 - public void __construct() {this.__construct(XomwDefines.CONTENT_MODEL_TEXT, XomwDefines.CONTENT_FORMAT_TEXT);} - @Override public void __construct(String modelId, String... formats) { +public class TextContentHandler extends ContentHandler { public void __construct() {this.__construct(XomwDefines.CONTENT_MODEL_TEXT, XophpArray.New(XomwDefines.CONTENT_FORMAT_TEXT));} + @Override public void __construct(String modelId, XophpArray formats) { super.__construct(modelId, formats); } - // @codingStandardsIgnoreEnd -// + // /** // * Returns the content's text as-is. // * diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java index a5afd893f..bbc502b7b 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/XomwParser.java @@ -143,7 +143,7 @@ public class XomwParser implements XomwParserIface { // public mFunctionHooks = []; // public mFunctionSynonyms = [ 0 => [], 1 => [] ]; // public mFunctionTagHooks = []; -// public mStripList = []; + public XophpArray mStripList = XophpArray.New(); // public mDefaultStripList = []; // public mVarCache = []; // public mImageParams = []; @@ -295,7 +295,7 @@ public class XomwParser implements XomwParserIface { // XomwParser.EXT_LINK_ADDR . // XomwParser.EXT_LINK_URL_CLASS . '*)\p{Zs}*([^\]\\x00-\\x08\\x0a-\\x1F]*?)\]/Su'; - this.mPreprocessorClass = XomwPreprocessor_DOM.Instance; + this.mPreprocessorClass = XomwPreprocessor_Hash.Instance; // if (isset($conf['preprocessorClass'])) { // this.mPreprocessorClass = $conf['preprocessorClass']; // } elseif (defined('HPHP_VERSION')) { @@ -1096,15 +1096,15 @@ public class XomwParser implements XomwParserIface { // } // return $stripped; // } -// -// /** -// * Get a list of strippable XML-like elements -// * -// * @return array -// */ -// public function getStripList() { -// return this.mStripList; -// } + + /** + * Get a list of strippable XML-like elements + * + * @return array + */ + public XophpArray getStripList() { + return this.mStripList; + } /** * Add an item to the strip state @@ -1147,8 +1147,8 @@ public class XomwParser implements XomwParserIface { * @return String */ // isMain=tru - public void internalParse(XomwParserBfr pbfr, XomwParserCtx pctx, byte[] text) {internalParse(pbfr, pctx, text, true, false);} - public void internalParse(XomwParserBfr pbfr, XomwParserCtx pctx, byte[] text, boolean isMain, boolean frame) { + public void internalParse(XomwParserBfr pbfr, XomwParserCtx pctx, byte[] text) {internalParse(pbfr, pctx, text, true, null);} + public void internalParse(XomwParserBfr pbfr, XomwParserCtx pctx, byte[] text, boolean isMain, XomwPPFrame frame) { pbfr.Init(text); // $origText = text; @@ -1206,6 +1206,69 @@ public class XomwParser implements XomwParserIface { // $text = $this->formatHeadings($text, $origText, $isMain); } + public String internalParse(String text, boolean isMain, XomwPPFrame frame) { // isMain=true; frame=false; + // String origText = text; + + // Avoid PHP 7.1 warning from passing this by reference + // XomwParser parser = this; + + // Hook to suspend the parser in this state +// if (!Hooks::run("ParserBeforeInternalParse", [ &parser, &text, &this.mStripState ])) { +// return text; +// } + + // if frame is provided, then use frame for replacing any variables + int flag = 0; + if (XophpObject_.is_true(frame)) { + // use frame depth to infer how include/noinclude tags should be handled + // depth=0 means this is the top-level document; otherwise it's an included document + if (!XophpInt_.is_true(frame.depth)) { + flag = 0; + } else { + flag = XomwParser.PTD_FOR_INCLUSION; + } + XomwPPNode dom = this.preprocessToDom(text, flag); + text = frame.expand(dom); + } else { + // if frame is not provided, then use old-style replaceVariables +// text = this.replaceVariables(text); + } + +// Hooks::run("InternalParseBeforeSanitize", [ &parser, &text, &this.mStripState ]); +// text = Sanitizer::removeHTMLtags( +// text, +// [ this, "attributeStripCallback" ], +// false, +// array_keys(this.mTransparentTagHooks), +// [], +// [ this, "addTrackingCategory" ] +// ); +// Hooks::run("InternalParseBeforeLinks", [ &parser, &text, &this.mStripState ]); + + // Tables need to come after variable replacement for things to work + // properly; putting them before other transformations should keep + // exciting things like link expansions from showing up in surprising + // places. +// text = this.doTableStuff(text); +// +// text = preg_replace("/(^|\n)-----* /", "\\1
bc", "
bc", "
bc", "
ba<pre>b</pre c"); + } + @Test public void Ext__dangling() { // COVERS: "Let it run out to the end of the text." + fxt.Test__parse("a
bc", "a<pre>bc "); + } + @Test public void Ext__dangling__many() { // COVERS: "Cache results, otherwise we have O(N^2) performance for input like..." + fxt.Test__parse("a bc", "a<pre><pre><pre>bc "); + } + @Test public void Ext__unclosed() { // COVERS: "Infinite backtrack" + fxt.Test__parse("aa<pre bcd"); + } + @Test public void Ext__noinclude() { // COVERS: "and just become tags" + fxt.Init__for_inclusion_(Bool_.N); + fxt.Test__parse("a b e", "c da "); + } + @Test public void Heading() { + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a" + , "== b1 ==" + , "z" + ), String_.Concat_lines_nl_skip_last + ( "<includeonly>b<noinclude>c</noinclude>d</includeonly> ea" + , " " + )); + } + @Test public void Heading__eos__no_nl() { + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a" + , "== b1 ==" + ), String_.Concat_lines_nl_skip_last + ( "== b1 == " + , "za" + , " " + )); + } + @Test public void Heading__bos__implied_nl() { // COVERS: "Is this the start of a heading?" + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "== b1 ==" + , "z" + ), String_.Concat_lines_nl_skip_last + ( "== b1 == " + )); + } + @Test public void Heading__dwim__y() { // COVERS: "DWIM: This looks kind of like a name/value separator." + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a{{b|" + , "=c=" + , "}}d" + ), String_.Concat_lines_nl_skip_last + ( " == b1 == " + , "za " + )); + } + @Test public void Heading__dwim__n() { // COVERS: "DWIM: This looks kind of like a name/value separator." + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a{{b|" + , "==c==" + , "}}d" + ), String_.Concat_lines_nl_skip_last + ( "b d " + , " =c=" + , " a " + )); + } + @Test public void Heading__comment() { // COVERS: "Comment found at line end" + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a" + , "==b== " + , "" + ), String_.Concat_lines_nl_skip_last + ( "b d " + , " ==c== " // NOTE: verified against MW:1.29; possible-h line hit + , "a" + , " " + )); + } + @Test public void Heading__consecutive__5() { // COVERS: "This is just a single String of equals signs on its own line" + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a" + , "=====" + , "" + ), String_.Concat_lines_nl_skip_last + ( "==b== " + , "<!--c--> a" + , " " + )); + } + @Test public void Heading__consecutive__1() { // COVERS: "Single equals sign on its own line, count=0" + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a" + , "=" + , "" + ), String_.Concat_lines_nl_skip_last + ( "===== " + , "a" + , "=" + , " " + )); + } + @Test public void Heading__unclosed() { // COVERS: "No match, no, just pass down the inner src" + fxt.Test__parse(String_.Concat_lines_nl_skip_last + ( "a" + , "===b" + , "" + ), String_.Concat_lines_nl_skip_last + ( " a" + , "===b" + , " " + )); + } + @Test public void Inclusion__n() { + fxt.Init__for_inclusion_(Bool_.N); + fxt.Test__parse("ab c", "a "); + } + @Test public void Inclusion__y() { + fxt.Init__for_inclusion_(Bool_.Y); + fxt.Test__parse("a<onlyinclude> b</onlyinclude> cb c", ""); + } + @Test public void Ignored__noinclude() { // COVERS: "Handle ignored tags" + fxt.Init__for_inclusion_(Bool_.N); + fxt.Test__parse("a a<onlyinclude> b</onlyinclude>c b c", "a "); + } +} +class XomwPreprocessor__fxt { + private boolean hash_enabled = Bool_.Y; + private boolean for_inclusion = false; + public XomwPreprocessor__fxt() { + } + public void Clear() { + hash_enabled = true; + for_inclusion = false; + } + public void Init__for_inclusion_(boolean v) {for_inclusion = v;} + public XomwPreprocessor__fxt Init__hash_y() {hash_enabled = Bool_.Y; return this;} + public void Test__parse(String src_str, String expd) { + List_adp list = List_adp_.New(); + if (hash_enabled) { + XomwParser parser = new XomwParser(XomwEnv_fxt.NewTest()); + XomwPreprocessor_Hash wkr_hash = new XomwPreprocessor_Hash(parser); + parser.mStripList.Add("pre"); + list.Add(wkr_hash); + } + + for (int i = 0; i < list.Len(); i++) { + XomwPreprocessor wkr = (XomwPreprocessor)list.Get_at(i); + String actl = wkr.preprocessToDbg(src_str, for_inclusion); + Tfds.Eq_str_lines(expd, actl, src_str); + } + } +} diff --git a/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage.java b/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage.java index 32874b1d2..dabe6faae 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage.java +++ b/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage.java @@ -4585,7 +4585,7 @@ public class XomwLanguage { // For unknown languages, fallbackSequence returns an empty array, // hardcode fallback to 'en' in that case. Object rv = XomwLanguage.getLocalisationCache().getItem(code, "fallbackSequence"); - return rv == null ? XophpArray.New().Add("en") : (XophpArray)rv; + return rv == null ? XophpArray.New("en") : (XophpArray)rv; } // /** diff --git a/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage_tst.java b/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage_tst.java index beda9ac0b..19b86815b 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage_tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/languages/XomwLanguage_tst.java @@ -114,8 +114,8 @@ public class XomwLanguage_tst { fxt.Test_commafy("-1234567890" , "-1,23,45,67,890"); } @Test public void handleExplicitPluralForms() { - fxt.Test__handleExplicitPluralForms__string("1", XophpArray.New().Add("1=one"), "one"); - fxt.Test__handleExplicitPluralForms__array("1", XophpArray.New().Add("no_match"), XophpArray.New().Add(0, "no_match")); + fxt.Test__handleExplicitPluralForms__string("1", XophpArray.New("1=one"), "one"); + fxt.Test__handleExplicitPluralForms__array("1", XophpArray.New("no_match"), XophpArray.New().Add(0, "no_match")); } @Test public void getPluralRuleIndexNumber() { fxt.Init__pluralRulesXml diff --git a/400_xowa/src/gplx/xowa/mediawiki/vendor/wikimedia/cldr_plural_rule_parser/src/XomwRange.java b/400_xowa/src/gplx/xowa/mediawiki/vendor/wikimedia/cldr_plural_rule_parser/src/XomwRange.java index 05c10e3cb..e49a50662 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/vendor/wikimedia/cldr_plural_rule_parser/src/XomwRange.java +++ b/400_xowa/src/gplx/xowa/mediawiki/vendor/wikimedia/cldr_plural_rule_parser/src/XomwRange.java @@ -36,7 +36,7 @@ class XomwRange { if (end == null) { this.parts.Add(start); } else { - this.parts.Add(XophpArray.New().Add(start).Add(end)); + this.parts.Add(XophpArray.New(start, end)); } } diff --git a/baselib/src/gplx/objects/strings/bfrs/String_bfr.java b/baselib/src/gplx/objects/strings/bfrs/String_bfr.java index f6631e4dd..64a94734b 100644 --- a/baselib/src/gplx/objects/strings/bfrs/String_bfr.java +++ b/baselib/src/gplx/objects/strings/bfrs/String_bfr.java @@ -17,6 +17,7 @@ package gplx.objects.strings.bfrs; import gplx.*; import gplx.objects.*; import import java.lang.*; import gplx.objects.primitives.*; import gplx.objects.errs.*; +import gplx.objects.strings.unicodes.*; public class String_bfr { private java.lang.StringBuilder sb = new java.lang.StringBuilder(); public boolean Has_none() {return this.Len() == 0;} @@ -32,6 +33,16 @@ public class String_bfr { Add_char(c); return this; } + public String_bfr Add_char_by_code(int code) { + if (code >= Ustring_.Surrogate_cp_bgn && code <= Ustring_.Surrogate_cp_end) { + sb.append((char)((code - 0x10000) / 0x400 + 0xD800)); + sb.append((char)((code - 0x10000) % 0x400 + 0xDC00)); + } + else { + sb.append((char)code); + } + return this; + } public String_bfr Add_int_pad_bgn(char pad_char, int str_len, int val) { int digit_len = Int_.Count_digits(val); int pad_len = str_len - digit_len;<noinclude> b</noinclude> c