diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java index dbc11dc62..dbee3ed3b 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray_.java @@ -16,17 +16,34 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; import gplx.core.strings.*; public class XophpArray_ { + // REF.PHP:https://www.php.net/manual/en/function.array-merge.php public static XophpArray array_merge(XophpArray... vals) { XophpArray rv = new XophpArray(); for (XophpArray ary : vals) { XophpArrayItm[] itms = ary.To_ary(); for (XophpArrayItm itm : itms) { - array_add(rv, itm); + array_itm_add(rv, itm); } } return rv; } - private static void array_add(XophpArray ary, XophpArrayItm itm) { + // REF.PHP:https://www.php.net/manual/en/function.array-merge.php + // "If you want to append array elements from the second array to the first array while not overwriting the elements from the first array and not re-indexing, use the + array union operator:" + public static XophpArray array_add(XophpArray lhs, XophpArray... vals) { + for (XophpArray ary : vals) { + XophpArrayItm[] itms = ary.To_ary(); + for (XophpArrayItm itm : itms) { + if (lhs.Has(itm.Key())) { + continue; + } + else { + lhs.Add(itm.Key(), itm.Val()); + } + } + } + return lhs; + } + private static void array_itm_add(XophpArray ary, XophpArrayItm itm) { if (itm.Key_is_int()) ary.Add(itm.Val()); else @@ -51,14 +68,14 @@ public class XophpArray_ { // add src from 0 to bgn for (int i = 0; i < bgn; i++) { - array_add(src, itms[i]); + array_itm_add(src, itms[i]); } // add repl if (repl != null) { XophpArrayItm[] repl_itms = repl.To_ary(); for (XophpArrayItm itm : repl_itms) { - array_add(src, itm); + array_itm_add(src, itm); } } @@ -79,13 +96,13 @@ public class XophpArray_ { // add src from end to len for (int i = end; i < src_len; i++) { - array_add(src, itms[i]); + array_itm_add(src, itms[i]); } // add del to rv XophpArray rv = new XophpArray(); for (int i = bgn; i < end; i++) { - array_add(rv, itms[i]); + array_itm_add(rv, itms[i]); } return rv; } @@ -100,6 +117,16 @@ public class XophpArray_ { return rv; } + public static XophpArray array_keys(XophpArray array) { + XophpArray rv = XophpArray.New(); + int len = array.count(); + for (int i = 0; i < len; i++) { + XophpArrayItm itm = array.Get_at_itm(i); + rv.Add(itm.Key()); + } + return rv; + } + // DEPRECATE:use XophpArray public static boolean popBoolOrN(List_adp list) {return Bool_.Cast(List_adp_.Pop_or(list, false));} public static byte[] popBryOrNull(List_adp list) {return (byte[])List_adp_.Pop_or(list, null);} diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java b/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java index 23c0c0918..631bb8e84 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpArray__tst.java @@ -20,44 +20,51 @@ public class XophpArray__tst { // REF:https://www.php.net/manual/en/function.arr @Test public void array_merge__basic() { XophpArray ary1 = fxt.Make().Add("key1", "val1").Add("a"); XophpArray ary2 = fxt.Make().Add("key2", "val2").Add("b"); - fxt.Test__array_merge + fxt.Test__eq ( fxt.Make().Add("key1", "val1").Add("a").Add("key2", "val2").Add("b") - , ary1, ary2); + , XophpArray_.array_merge(ary1, ary2)); } @Test public void array_merge__same_key() { XophpArray ary1 = fxt.Make().Add("key", "val1"); XophpArray ary2 = fxt.Make().Add("key", "val2"); - fxt.Test__array_merge + fxt.Test__eq ( fxt.Make().Add("key", "val2") - , ary1, ary2); + , XophpArray_.array_merge(ary1, ary2)); } @Test public void array_merge__same_idx() { XophpArray ary1 = fxt.Make().Add(0, "a"); XophpArray ary2 = fxt.Make().Add(0, "b"); - fxt.Test__array_merge + fxt.Test__eq ( fxt.Make().Add(0, "a").Add(1, "b") - , ary1, ary2); + , XophpArray_.array_merge(ary1, ary2)); } @Test public void array_merge__renumber() { XophpArray ary1 = fxt.Make().Add(3, "a"); XophpArray ary2 = fxt.Make().Add(2, "b"); - fxt.Test__array_merge + fxt.Test__eq ( fxt.Make().Add(0, "a").Add(1, "b") - , ary1, ary2); + , XophpArray_.array_merge(ary1, ary2)); } @Test public void array_merge__example_1() { XophpArray ary1 = fxt.Make().Add("color", "red").Add_many(2, 4); XophpArray ary2 = fxt.Make().Add_many("a", "b").Add("color", "green").Add("shape", "trapezoid").Add(4); - fxt.Test__array_merge + fxt.Test__eq ( fxt.Make().Add("color", "green").Add_many(2, 4, "a", "b").Add("shape", "trapezoid").Add(4) - , ary1, ary2); + , XophpArray_.array_merge(ary1, ary2)); } @Test public void array_merge__example_2() { XophpArray ary1 = fxt.Make(); XophpArray ary2 = fxt.Make().Add(1, "data"); - fxt.Test__array_merge + fxt.Test__eq ( fxt.Make().Add(0, "data") - , ary1, ary2); + , XophpArray_.array_merge(ary1, ary2)); + } + @Test public void array_add() { + XophpArray ary1 = fxt.Make().Add(0, "zero_a").Add(2, "two_a").Add(3, "three_a"); + XophpArray ary2 = fxt.Make().Add(1, "one_b").Add(3, "three_b").Add(4, "four_b"); + fxt.Test__eq + ( fxt.Make().Add(0, "zero_a").Add(2, "two_a").Add(3, "three_a").Add(1, "one_b").Add(4, "four_b") + , XophpArray_.array_add(ary1, ary2)); } @Test public void array_splice__bgn_is_positive() { XophpArray src = fxt.Make().Add_many("a", "b", "c", "d"); @@ -253,10 +260,6 @@ public class XophpArray__tst { // REF:https://www.php.net/manual/en/function.arr } class XophpArray__fxt { public XophpArray Make() {return new XophpArray();} - public void Test__array_merge(XophpArray expd, XophpArray... vals) { - XophpArray actl = XophpArray_.array_merge(vals); - Gftest.Eq__str(expd.To_str(), actl.To_str()); - } public void Test__eq(XophpArray expd, XophpArray actl) { Gftest.Eq__str(expd.To_str(), actl.To_str()); } diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpIo_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpIo_.java index 353b2f461..058b94a4e 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpIo_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpIo_.java @@ -17,7 +17,7 @@ package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*; public class XophpIo_ { public static String file_get_contents(String path) { String rv = Io_mgr.Instance.LoadFilStr(path); - return String_.Eq(rv, String_.Empty) ? XophpString_.Null : rv; + return String_.Eq(rv, String_.Empty) ? XophpString_.False : rv; } public static boolean file_exists(String path) { return Io_mgr.Instance.ExistsFil(Io_url_.new_fil_(path)); diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java b/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java index 65dce67a4..66892c6d9 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpString_.java @@ -19,7 +19,7 @@ import gplx.core.intls.*; import gplx.objects.strings.unicodes.*; import gplx.core.primitives.*; public class XophpString_ implements XophpCallbackOwner { - public static final String Null = null; + 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; // REF.PHP: https://www.php.net/manual/en/function.strpos.php @@ -240,6 +240,9 @@ public class XophpString_ implements XophpCallbackOwner { public static byte[] str_replace(byte[] find, byte[] repl, byte[] src) { return Bry_.Replace(src, find, repl); } + public static String str_replace(String find, String repl, String src) { + return String_.Replace(src, find, repl); + } public static byte[] strstr(byte[] src, byte[] find) { int pos = Bry_find_.Find_fwd(src, find); return pos == Bry_find_.Not_found ? null : Bry_.Mid(src, pos, src.length); @@ -256,93 +259,135 @@ public class XophpString_ implements XophpCallbackOwner { , Int_obj_ref.New(Byte_ascii.Null) , Int_obj_ref.New(Byte_ascii.Vertical_tab) ); - public static String rtrim(String src) {return rtrim(src, null);} - public static String rtrim(String src_str, String pad_str) { - Hash_adp pad_hash = null; - if (pad_str == null) pad_hash = trim_ws_hash; - + public static String trim (String src) {return trim_outer( 0, src, null);} + public static String trim (String src, String pad) {return trim_outer( 0, src, pad);} + public static String ltrim(String src) {return trim_outer( 1, src, null);} + public static String ltrim(String src, String pad) {return trim_outer( 1, src, pad);} + public static String rtrim(String src) {return trim_outer(-1, src, null);} + public static String rtrim(String src, String pad) {return trim_outer(-1, src, pad);} + private static String trim_outer(int type, String src_str, String pad_str) { // init brys / lens byte[] src_bry = Bry_.new_u8(src_str); int src_len = src_bry.length; byte[] pad_bry = Bry_.new_u8(pad_str); int pad_len = pad_bry.length; + // create pad_hash if not ws_hash + // NOTE: this does not support mutlibyte chars, and PHP does not support multibyte chars; see TEST + Hash_adp pad_hash = null; + if (pad_len > 1) { + if (pad_str == null) { + pad_hash = trim_ws_hash; + } + else { + pad_hash = Hash_adp_.New(); + byte prv_byte = Byte_.Zero; + for (int i = 0; i < pad_len; i++) { + byte pad_byte = pad_bry[i]; + if (pad_byte == Byte_ascii.Dot && i < pad_len - 1) { + byte nxt_byte = pad_bry[i + 1]; + if (nxt_byte == Byte_ascii.Dot) { + if (i == 0) { + throw new XophpError(".. found but at start of String; src=" + pad_str); + } + else if (i == pad_len - 2) { + throw new XophpError(".. found but at end of String; src=" + pad_str); + } + else { + nxt_byte = pad_bry[i + 2]; + if (nxt_byte > prv_byte) { + for (byte j = prv_byte; j < nxt_byte; j++) { + Byte_obj_ref rng_obj = Byte_obj_ref.new_(j); + if (!pad_hash.Has(rng_obj)) + pad_hash.Add_as_key_and_val(rng_obj); + } + i += 2; + continue; + } + else { + throw new XophpError(".. found but next byte must be greater than previous byte; src=" + pad_str); + } + } + } + } + prv_byte = pad_byte; + Byte_obj_ref pad_obj = Byte_obj_ref.new_(pad_byte); + if (!pad_hash.Has(pad_obj)) + pad_hash.Add_as_key_and_val(pad_obj); + } + } + } + + // do trim + int[] rv = new int[2]; + rv[0] = 0; + rv[1] = src_len; + if (type <= 0) { // trim or rtrim + trim_inner(Bool_.N, rv, src_bry, src_len, pad_bry, pad_len, pad_hash); + } + if (type >= 0) { // trim or ltrim + trim_inner(Bool_.Y, rv, src_bry, src_len, pad_bry, pad_len, pad_hash); + } + + // return String + int trim_bgn = rv[0]; + int trim_end = rv[1]; + return trim_bgn == 0 && trim_end == src_len + ? src_str + : String_.new_u8(Bry_.Mid(src_bry, trim_bgn, trim_end)); + } + private static void trim_inner(boolean is_bos, int[] rv, byte[] src_bry, int src_len, byte[] pad_bry, int pad_len, Hash_adp pad_hash) { // ---------------------- - // 0, 1 chars (optimized) + // init vars // ---------------------- - int last = 0; + int rv_idx = 1; + int trim_bgn = src_len - 1; + int trim_end = -1; + int trim_add = -1; + int trim_adj = 1; + if (is_bos) { + rv_idx = 0; + trim_bgn = 0; + trim_end = src_len; + trim_add = 1; + trim_adj = 0; + } + int trim_pos = trim_end; + switch (pad_len) { - // pad is "" + // pad is 0 char; aka: "" case 0: - return src_str; + break; // pad is 1 char case 1: - last = src_len; byte pad_byte = pad_bry[0]; - for (int i = src_len - 1; i > -1; i--) { + for (int i = trim_bgn; i != trim_end; i += trim_add) { byte cur = src_bry[i]; - last = i + 1; + trim_pos = i + trim_adj; if (cur != pad_byte) { break; } } - return (last == src_len) ? src_str : String_.new_u8(Bry_.Mid(src_bry, 0, last)); - } - - // -------- - // 2+ chars - // -------- - // create pad_hash if not ws_hash - // NOTE: PHP does not support multibyte strings; see TEST - if (pad_hash == null) { - pad_hash = Hash_adp_.New(); - byte prv_byte = Byte_.Zero; - for (int i = 0; i < pad_len; i++) { - byte pad_byte = pad_bry[i]; - if (pad_byte == Byte_ascii.Dot && i < pad_len - 1) { - byte nxt_byte = pad_bry[i + 1]; - if (nxt_byte == Byte_ascii.Dot) { - if (i == 0) { - throw new XophpError(".. found but at start of String; src=" + pad_str); - } - else if (i == pad_len - 2) { - throw new XophpError(".. found but at end of String; src=" + pad_str); - } - else { - nxt_byte = pad_bry[i + 2]; - if (nxt_byte > prv_byte) { - for (byte j = prv_byte; j < nxt_byte; j++) { - Byte_obj_ref rng_obj = Byte_obj_ref.new_(j); - if (!pad_hash.Has(rng_obj)) - pad_hash.Add_as_key_and_val(rng_obj); - } - i += 2; - continue; - } - else { - throw new XophpError(".. found but next byte must be greater than previous byte; src=" + pad_str); - } - } + break; + // pad is 2+ chars + default: + // loop src until non-matching pad int + Byte_obj_ref temp = Byte_obj_ref.zero_(); + trim_pos = src_len; + for (int i = trim_bgn; i != trim_end; i += trim_add) { + temp.Val_(src_bry[i]); + trim_pos = i + trim_adj; + if (!pad_hash.Has(temp)) { + break; } } - prv_byte = pad_byte; - Byte_obj_ref pad_obj = Byte_obj_ref.new_(pad_byte); - if (!pad_hash.Has(pad_obj)) - pad_hash.Add_as_key_and_val(pad_obj); - } + break; } - // loop src until non-matching pad int - Byte_obj_ref temp = Byte_obj_ref.zero_(); - last = src_len; - for (int i = src_len - 1; i > -1; i--) { - temp.Val_(src_bry[i]); - last = i + 1; - if (!pad_hash.Has(temp)) { - break; - } + // set return + if (trim_pos != trim_end) { + rv[rv_idx] = trim_pos; } - return (last == src_len) ? src_str : String_.new_u8(Bry_.Mid(src_bry, 0, last)); } public static String str_repeat(String val, int count) { int val_len = String_.Len(val); diff --git a/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java b/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java index e95b99db9..72ff9fd0e 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java +++ b/400_xowa/src/gplx/xowa/mediawiki/XophpString__tst.java @@ -94,6 +94,48 @@ public class XophpString__tst { // REF.MW:https://www.php.net/manual/en/function.trim.php fxt.Test__rtrim("\u00A0µ déjà\u00A0", "\u00A0", "\u00A0µ déj�");// NOTE: technically should be "...j\xC3", but String_.new_u8 ignores invalid bytes } + @Test public void ltrim() { + // pad is 0, 1 char + fxt.Test__ltrim("0010", "", "0010"); // empty pad returns String + fxt.Test__ltrim("010", "0", "10"); // basic test; trim 1; + fxt.Test__ltrim("0010", "0", "10"); // basic test; trim 2; + fxt.Test__ltrim("10", "0", "10"); // nothing to trim + + // pad is 2+char + fxt.Test__ltrim("10ab10", "01", "ab10"); // basic test + fxt.Test__ltrim("10ab10", "34", "10ab10"); // nothing to trim + fxt.Test__ltrim("10ab10", "010", "ab10"); // don't fail if repeated chars + + // pad has .. + fxt.Test__ltrim("23ab23", "0..4", "ab23"); // basic test + fxt.Test__ltrim(".23ab23", "0.4", "23ab23"); // single dot is not range + + // PHP samples + fxt.Test__ltrim("\t\tThese are a few words :) ... ", " \t.", "These are a few words :) ... "); + fxt.Test__ltrim("Hello World", "Hdle", "o World"); + fxt.Test__ltrim("\u0009Example String\n", "\u0000..\u001F", "Example String\n"); + } + @Test public void trim() { + // pad is 0, 1 char + fxt.Test__trim("0010", "", "0010"); // empty pad returns String + fxt.Test__trim("010", "0", "1"); // basic test; trim 1; + fxt.Test__trim("00100", "0", "1"); // basic test; trim 2; + fxt.Test__trim("10", "0", "1"); // nothing to trim + + // pad is 2+char + fxt.Test__trim("10ab10", "01", "ab"); // basic test + fxt.Test__trim("10ab10", "34", "10ab10"); // nothing to trim + fxt.Test__trim("10ab10", "010", "ab"); // don't fail if repeated chars + + // pad has .. + fxt.Test__trim("23ab23", "0..4", "ab"); // basic test + fxt.Test__trim(".23ab23.", "0.4", "23ab23"); // single dot is not range + + // PHP samples + fxt.Test__trim("\t\tThese are a few words :) ... ", " \t.", "These are a few words :)"); + fxt.Test__trim("Hello World", "Hdle", "o Wor"); + fxt.Test__trim("\u0009Example String\n", "\u0000..\u001F", "Example String"); + } @Test public void ord() { fxt.Test__ord("a", 97); // 1 char fxt.Test__ord("abc", 97); // 2+ chars takes first @@ -168,6 +210,12 @@ class XophpString__fxt { } Gftest.Fail("expected failure, but got none: " + character_mask); } + public void Test__ltrim(String str, String character_mask, String expd) { + Gftest.Eq__str(expd, XophpString_.ltrim(str, character_mask)); + } + public void Test__trim(String str, String character_mask, String expd) { + Gftest.Eq__str(expd, XophpString_.trim(str, character_mask)); + } public void Test__ord(String str, int expd) { Gftest.Eq__int(expd, XophpString_.ord(str)); } diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPFrame_Hash.java b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPFrame_Hash.java index a53dbf9cc..2bc842f48 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPFrame_Hash.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPFrame_Hash.java @@ -54,7 +54,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /** /** * @var array */ - protected XophpArray childExpansionCache; + public XophpArray childExpansionCache; /** * Construct a new preprocessor frame. @@ -64,7 +64,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /** this.preprocessor = preprocessor; this.parser = preprocessor.Parser(); this.title = this.parser.mTitle; - this.titleCache = XophpArray.New().Add(XophpObject_.is_true(this.title) ? this.title.getPrefixedDBkeyStr() : XophpString_.Null); + this.titleCache = XophpArray.New().Add(XophpObject_.is_true(this.title) ? this.title.getPrefixedDBkeyStr() : XophpString_.False); this.loopCheckHash = XophpArray.New(); this.depth = 0; this.childExpansionCache = XophpArray.New(); @@ -214,7 +214,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /** } Object newIterator = XophpObject_.False; - String contextName = XophpString_.Null; + String contextName = XophpString_.False; XophpArray contextChildren = XophpArray.False; if (!XophpObject_.is_true(contextNode)) { @@ -523,7 +523,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /** return this.title.getPrefixedDBkeyStr(); } else { // return isset( $this->titleCache[$level] ) ? $this->titleCache[$level] : false; - return this.titleCache.count() > 0 ? ((String)this.titleCache.Get_at(0)) : XophpString_.Null; + return this.titleCache.count() > 0 ? ((String)this.titleCache.Get_at(0)) : XophpString_.False; } } @@ -562,7 +562,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /** * @return boolean Always false in this implementation. */ @Override public String getArgument(String name) { - return XophpString_.Null; + return XophpString_.False; } /** diff --git a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPTemplateFrame_Hash.java b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPTemplateFrame_Hash.java index d919535ea..64972b6d9 100644 --- a/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPTemplateFrame_Hash.java +++ b/400_xowa/src/gplx/xowa/mediawiki/includes/parsers/preprocessors/XomwPPTemplateFrame_Hash.java @@ -18,181 +18,188 @@ package gplx.xowa.mediawiki.includes.parsers.preprocessors; import gplx.*; impor // * Expansion frame with template arguments // * @ingroup Parser // */ -//// @codingStandardsIgnoreStart Squiz.Classes.ValidClassName.NotCamelCaps -// extends PPFrame_Hash -class XomwPPTemplateFrame_Hash extends XomwPPFrame { // // @codingStandardsIgnoreEnd -// -// public $numberedArgs, $namedArgs, $parent; -// public $numberedExpansionCache, $namedExpansionCache; -// -// /** -// * @param Preprocessor $preprocessor -// * @param boolean|PPFrame $parent -// * @param array $numberedArgs -// * @param array $namedArgs -// * @param boolean|Title $title -// */ -// public function __construct($preprocessor, $parent = false, $numberedArgs = [], -// $namedArgs = [], $title = false -// ) { -// parent::__construct($preprocessor); -// -// this.parent = $parent; -// this.numberedArgs = $numberedArgs; -// this.namedArgs = $namedArgs; -// this.title = $title; -// $pdbk = $title ? $title.getPrefixedDBkey() : false; -// this.titleCache = $parent.titleCache; -// this.titleCache[] = $pdbk; -// this.loopCheckHash = /*clone*/ $parent.loopCheckHash; -// if ($pdbk !== false) { -// this.loopCheckHash[$pdbk] = true; -// } -// this.depth = $parent.depth + 1; -// this.numberedExpansionCache = this.namedExpansionCache = []; -// } -// -// public function __toString() { -// $s = 'tplframe{'; -// $first = true; -// $args = this.numberedArgs + this.namedArgs; -// for each ($args as $name => $value) { -// if ($first) { -// $first = false; -// } else { -// $s .= ', '; -// } -// $s .= "\"$name\":\"" . -// str_replace('"', '\\"', $value.__toString()) . '"'; -// } -// $s .= '}'; -// return $s; -// } -// -// /** -// * @throws MWException -// * @param String|int $key -// * @param String|PPNode $root -// * @param int $flags -// * @return String -// */ -// public function cachedExpand($key, $root, $flags = 0) { -// if (isset(this.parent.childExpansionCache[$key])) { -// return this.parent.childExpansionCache[$key]; -// } -// $retval = this.expand($root, $flags); -// if (!this.isVolatile()) { -// this.parent.childExpansionCache[$key] = $retval; -// } -// return $retval; -// } -// -// /** -// * Returns true if there are no arguments in this frame -// * -// * @return boolean -// */ -// public function isEmpty() { -// return !count(this.numberedArgs) && !count(this.namedArgs); -// } -// -// /** -// * @return array -// */ -// public function getArguments() { -// $arguments = []; -// for each (array_merge( -// array_keys(this.numberedArgs), -// array_keys(this.namedArgs)) as $key) { -// $arguments[$key] = this.getArgument($key); -// } -// return $arguments; -// } -// -// /** -// * @return array -// */ -// public function getNumberedArguments() { -// $arguments = []; -// for each (array_keys(this.numberedArgs) as $key) { -// $arguments[$key] = this.getArgument($key); -// } -// return $arguments; -// } -// -// /** -// * @return array -// */ -// public function getNamedArguments() { -// $arguments = []; -// for each (array_keys(this.namedArgs) as $key) { -// $arguments[$key] = this.getArgument($key); -// } -// return $arguments; -// } -// -// /** -// * @param int $index -// * @return String|boolean -// */ -// public function getNumberedArgument($index) { -// if (!isset(this.numberedArgs[$index])) { -// return false; -// } -// if (!isset(this.numberedExpansionCache[$index])) { -// # No trimming for unnamed arguments -// this.numberedExpansionCache[$index] = this.parent.expand( -// this.numberedArgs[$index], -// PPFrame::STRIP_COMMENTS -// ); -// } -// return this.numberedExpansionCache[$index]; -// } -// -// /** -// * @param String $name -// * @return String|boolean -// */ -// public function getNamedArgument($name) { -// if (!isset(this.namedArgs[$name])) { -// return false; -// } -// if (!isset(this.namedExpansionCache[$name])) { -// # Trim named arguments post-expand, for backwards compatibility -// this.namedExpansionCache[$name] = trim( -// this.parent.expand(this.namedArgs[$name], PPFrame::STRIP_COMMENTS)); -// } -// return this.namedExpansionCache[$name]; -// } -// -// /** -// * @param int|String $name -// * @return String|boolean -// */ -// public function getArgument($name) { -// $text = this.getNumberedArgument($name); -// if ($text === false) { -// $text = this.getNamedArgument($name); -// } -// return $text; -// } -// -// /** -// * Return true if the frame is a template frame -// * -// * @return boolean -// */ -// public function isTemplate() { -// return true; -// } -// -// public function setVolatile($flag = true) { -// parent::setVolatile($flag); -// this.parent.setVolatile($flag); -// } -// -// public function setTTL($ttl) { -// parent::setTTL($ttl); -// this.parent.setTTL($ttl); -// } +class XomwPPTemplateFrame_Hash extends XomwPPFrame_Hash { public XophpArray numberedArgs, namedArgs; + public XomwPPFrame_Hash parent; + public XophpArray numberedExpansionCache, namedExpansionCache; + + /** + * @param Preprocessor $preprocessor + * @param boolean|PPFrame $parent + * @param array $numberedArgs + * @param array $namedArgs + * @param boolean|Title $title + */ + // parent = false, numberedArgs = [], namedArgs = []; titl = false + public XomwPPTemplateFrame_Hash(XomwPreprocessor preprocessor, XomwPPFrame_Hash parent, XophpArray numberedArgs, + XophpArray namedArgs, XomwTitle title + ) {super(preprocessor); + this.parent = parent; + this.numberedArgs = numberedArgs; + this.namedArgs = namedArgs; + this.title = title; + String pdbk = title != null ? title.getPrefixedDBkeyStr() : XophpString_.False; + this.titleCache = parent.titleCache; + this.titleCache.Add(pdbk); + this.loopCheckHash = /*clone*/ parent.loopCheckHash; + if (pdbk != XophpString_.False) { + this.loopCheckHash.Add(pdbk, true); + } + this.depth = parent.depth + 1; + this.numberedExpansionCache = this.namedExpansionCache = XophpArray.New(); + } + + @Override public String toString() { + String s = "tplframe{"; + boolean first = true; + XophpArray args = XophpArray_.array_add(this.numberedArgs, this.namedArgs); + int args_len = args.count(); + for (int i = 0; i < args_len; i++) { + XophpArrayItm itm = args.Get_at_itm(i); + if (first) { + first = false; + } else { + s += ", "; + } + s += "\"" + itm.Key() + "\":\"" + + XophpString_.str_replace("\"", "\\\"", itm.Val().toString()) + "\""; + } + s += "}"; + return s; + } + + /** + * @throws MWException + * @param String|int $key + * @param String|PPNode $root + * @param int $flags + * @return String + */ + @Override public String cachedExpand(String key, XomwPPNode root, int flags) { // flags = 0 + if (XophpObject_.isset_obj(this.parent.childExpansionCache.Get_by(key))) { + return this.parent.childExpansionCache.Get_by_str(key); + } + String retval = this.expand(root, flags); + if (!this.isVolatile()) { + this.parent.childExpansionCache.Set(key, retval); + } + return retval; + } + + /** + * Returns true if there are no arguments in this frame + * + * @return boolean + */ + @Override public boolean isEmpty() { + return !this.numberedArgs.count_bool() && !this.namedArgs.count_bool(); + } + + /** + * @return array + */ + @Override public XophpArray getArguments() { + XophpArray arguments = XophpArray.New(); + XophpArray merged = XophpArray_.array_merge( + XophpArray_.array_keys(this.numberedArgs), + XophpArray_.array_keys(this.namedArgs)); + int merged_len = merged.count(); + for (int i = 0; i < merged_len; i++) { + String key = merged.Get_at_str(i); + arguments.Set(key, this.getArgument(key)); + } + return arguments; + } + + /** + * @return array + */ + @Override public XophpArray getNumberedArguments() { + XophpArray arguments = XophpArray.New(); + XophpArray temp = XophpArray_.array_keys(this.numberedArgs); + int temp_len = temp.count(); + for (int i = 0; i < temp_len; i++) { + String key = temp.Get_at_str(i); + arguments.Set(key, this.getArgument(key)); + } + return arguments; + } + + /** + * @return array + */ + @Override public XophpArray getNamedArguments() { + XophpArray arguments = XophpArray.New(); + XophpArray temp = XophpArray_.array_keys(this.namedArgs); + int temp_len = temp.count(); + for (int i = 0; i < temp_len; i++) { + String key = temp.Get_at_str(i); + arguments.Set(key, this.getArgument(key)); + } + return arguments; + } + + /** + * @param int $index + * @return String|boolean + */ + public String getNumberedArgument(int index) { + if (!XophpObject_.isset_obj(this.numberedArgs.Get_at(index))) { + return XophpString_.False; + } + if (!XophpObject_.isset_obj(this.numberedExpansionCache.Get_at(index))) { + // No trimming for unnamed arguments + this.numberedExpansionCache.Set(index, this.parent.expand( + (XomwPPNode)this.numberedArgs.Get_at(index), + XomwPPFrame.STRIP_COMMENTS + )); + } + return this.numberedExpansionCache.Get_at_str(index); + } + + /** + * @param String $name + * @return String|boolean + */ + public String getNamedArgument(String name) { + if (!XophpObject_.isset_obj(this.namedArgs.Get_by_str(name))) { + return XophpString_.False; + } + if (!XophpObject_.isset_obj(this.namedExpansionCache.Get_by_str(name))) { + // Trim named arguments post-expand, for backwards compatibility + this.namedExpansionCache.Set(name, XophpString_.trim( + this.parent.expand((XomwPPNode)this.namedArgs.Get_by(name), XomwPPFrame.STRIP_COMMENTS))); + } + return this.namedExpansionCache.Get_by_str(name); + } + + /** + * @param int|String $name + * @return String|boolean + */ + public String getArgument(Object name) { + String text = this.getNumberedArgument((int)name); + if (String_.Eq(text, XophpString_.False)) { + text = this.getNamedArgument((String)name); + } + return text; + } + + /** + * Return true if the frame is a template frame + * + * @return boolean + */ + @Override public boolean isTemplate() { + return true; + } + + @Override public void setVolatile(boolean flag) { // flag = true + super.setVolatile(flag); + this.parent.setVolatile(flag); + } + + @Override public void setTTL(int ttl) { + super.setTTL(ttl); + this.parent.setTTL(ttl); + } }