XomwTemplateParser: Port XomwPreprocessor classes as direct translation of MW code [#632]

staging
gnosygnu 4 years ago
parent 17d7a7ebed
commit 7ad1291768

@ -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) {

@ -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)

@ -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);}

@ -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;
}
}

@ -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();}

@ -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

@ -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;}
}

@ -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);
}

@ -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

@ -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()));
}

@ -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_();

@ -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;}
}

@ -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];

@ -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));
}
}

@ -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);
}
}

@ -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;
}

@ -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().

@ -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

@ -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;
}

@ -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.
// *

@ -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<hr />", text);
//
// text = this.doDoubleUnderscore(text);
//
// text = this.doHeadings(text);
// text = this.replaceInternalLinks(text);
// text = this.doAllQuotes(text);
// text = this.replaceExternalLinks(text);
//
// // replaceInternalLinks may sometimes leave behind
// // absolute URLs, which have to be masked to hide them from replaceExternalLinks
// text = str_replace(self::MARKER_PREFIX . "NOPARSE", "", text);
//
// text = this.doMagicLinks(text);
// text = this.formatHeadings(text, origText, isMain);
return text;
}
/**
* Helper function for parse() that transforms half-parsed HTML into fully
* parsed HTML.
@ -2286,329 +2349,356 @@ public class XomwParser implements XomwParserIface {
// this.addTrackingCategory("$limitationType-category");
// }
//
// /**
// * Return the text of a template, after recursively
// * replacing any variables or templates within the template.
// *
// * @param array $piece The parts of the template
// * $piece['title']: the title, i.e. the part before the |
// * $piece['parts']: the parameter array
// * $piece['lineStart']: whether the brace was at the start of a line
// * @param PPFrame $frame The current frame, contains template arguments
// * @throws Exception
// * @return String The text of the template
// */
// MW.SRC:1.33.1
/**
* Return the text of a template, after recursively
* replacing any variables or templates within the template.
*
* @param array $piece The parts of the template
* $piece['title']: the title, i.e. the part before the |
* $piece['parts']: the parameter array
* $piece['lineStart']: whether the brace was at the start of a line
* @param PPFrame $frame The current frame, contains template arguments
* @throws Exception
* @return String The text of the template
*/
public XophpArray braceSubstitution(XophpArray piece, XomwPPFrame frame) {
//
// // Flags
//
// // $text has been filled
// $found = false;
// // wiki markup in $text should be escaped
// $nowiki = false;
// // $text is HTML, armour it against wikitext transformation
// $isHTML = false;
// // Force interwiki transclusion to be done in raw mode not rendered
// $forceRawInterwiki = false;
// // $text is a DOM node needing expansion in a child frame
// $isChildObj = false;
// // $text is a DOM node needing expansion in the current frame
// $isLocalObj = false;
//
// // Title Object, where $text came from
// $title = false;
//
// // $part1 is the bit before the first |, and must contain only title characters.
// // Various prefixes will be stripped from it later.
// $titleWithSpaces = $frame->expand($piece['title']);
// $part1 = trim($titleWithSpaces);
// $titleText = false;
//
// // Original title text preserved for various purposes
// $originalTitle = $part1;
//
// // $args is a list of argument nodes, starting from index 0, not including $part1
// // @todo FIXME: If piece['parts'] is null then the call to getLength()
// // below won't work b/c this $args isn't an Object
// $args = (null == $piece['parts']) ? [] : $piece['parts'];
//
// $profileSection = null; // profile templates
//
// // SUBST
// if (!$found) {
// $substMatch = this.mSubstWords->matchStartAndRemove($part1);
// Flags
// text has been filled
boolean found = false;
// wiki markup in text should be escaped
boolean nowiki = false;
// text is HTML, armour it against wikitext transformation
boolean isHTML = false;
// Force interwiki transclusion to be done in raw mode not rendered
boolean forceRawInterwiki = false;
// text is a DOM node needing expansion in a child frame
boolean isChildObj = false;
// text is a DOM node needing expansion in the current frame
boolean isLocalObj = false;
// Title Object, where text came from
XomwTitle title = null;
// part1 is the bit before the first |, and must contain only title characters.
// Various prefixes will be stripped from it later.
String titleWithSpaces = frame.expand(piece.Get_by("title"));
String part1 = XophpString_.trim(titleWithSpaces);
String titleText = XophpString_.False;
// Original title text preserved for various purposes
String originalTitle = part1;
// args is a list of argument nodes, starting from index 0, not including part1
// @todo FIXME: If piece["parts"] is null then the call to getLength()
// below won"t work b/c this args isn"t an Object
XomwPPNode args = (piece.Get_by("parts") == null) ? new XomwPPNode_Hash_Array(XophpArray.New()) : (XomwPPNode_Hash_Array)piece.Get_by("parts");
// boolean profileSection = XophpBool_.Null; // profile templates
String text = null;
// SUBST
if (!found) {
// substMatch = this.mSubstWords.matchStartAndRemove(part1);
//
// // Possibilities for substMatch: "subst", "safesubst" or FALSE
// // Decide whether to expand template or keep wikitext as-is.
// if (this.ot['wiki']) {
// if ($substMatch === false) {
// $literal = true; // literal when in PST with no prefix
// if (this.ot["wiki"]) {
// if (substMatch === false) {
// literal = true; # literal when in PST with no prefix
// } else {
// $literal = false; // expand when in PST with subst: or safesubst:
// literal = false; # expand when in PST with subst: or safesubst:
// }
// } else {
// if ($substMatch == 'subst') {
// $literal = true; // literal when not in PST with plain subst:
// if (substMatch == "subst") {
// literal = true; # literal when not in PST with plain subst:
// } else {
// $literal = false; // expand when not in PST with safesubst: or no prefix
// literal = false; # expand when not in PST with safesubst: or no prefix
// }
// }
// if ($literal) {
// $text = $frame->virtualBracketedImplode('{{', '|', '}}', $titleWithSpaces, $args);
// $isLocalObj = true;
// $found = true;
// if (literal) {
// text = frame.virtualBracketedImplode("{{", "|", "}}", titleWithSpaces, args);
// isLocalObj = true;
// found = true;
// }
// }
//
}
// // Variables
// if (!$found && $args->getLength() == 0) {
// $id = this.mVariables->matchStartToEnd($part1);
// if ($id !== false) {
// $text = this.getVariableValue($id, $frame);
// if (MagicWord::getCacheTTL($id) > -1) {
// this.mOutput->updateCacheExpiry(MagicWord::getCacheTTL($id));
// if (!found && args.getLength() == 0) {
// id = this.mVariables.matchStartToEnd(part1);
// if (id !== false) {
// text = this.getVariableValue(id, frame);
// if (this.magicWordFactory.getCacheTTL(id) > -1) {
// this.mOutput.updateCacheExpiry(
// this.magicWordFactory.getCacheTTL(id));
// }
// $found = true;
// found = true;
// }
// }
//
// // MSG, MSGNW and RAW
// if (!$found) {
// if (!found) {
// // Check for MSGNW:
// mwMsgnw = MagicWord::get('msgnw');
// if (mwMsgnw->matchStartAndRemove($part1)) {
// $nowiki = true;
// mwMsgnw = this.magicWordFactory.get("msgnw");
// if (mwMsgnw.matchStartAndRemove(part1)) {
// nowiki = true;
// } else {
// // Remove obsolete MSG:
// mwMsg = MagicWord::get('msg');
// mwMsg->matchStartAndRemove($part1);
// mwMsg = this.magicWordFactory.get("msg");
// mwMsg.matchStartAndRemove(part1);
// }
//
// // Check for RAW:
// mwRaw = MagicWord::get('raw');
// if (mwRaw->matchStartAndRemove($part1)) {
// $forceRawInterwiki = true;
// }
// }
//
// // Parser functions
// if (!$found) {
// $colonPos = strpos($part1, ':');
// if ($colonPos !== false) {
// $func = substr($part1, 0, $colonPos);
// $funcArgs = [ trim(substr($part1, $colonPos + 1)) ];
// $argsLength = $args->getLength();
// for ($i = 0; $i < $argsLength; $i++) {
// $funcArgs[] = $args->item($i);
// }
// try {
// $result = this.callParserFunction($frame, $func, $funcArgs);
// } catch (Exception $ex) {
// throw $ex;
// }
//
// // The interface for parser functions allows for extracting
// // flags into the local scope. Extract any forwarded flags
// // here.
// extract($result);
// mwRaw = this.magicWordFactory.get("raw");
// if (mwRaw.matchStartAndRemove(part1)) {
// forceRawInterwiki = true;
// }
// }
//
// // Finish mangling title and then check for loops.
// // Set $title to a Title Object and $titleText to the PDBK
// if (!$found) {
// $ns = NS_TEMPLATE;
// // Split the title into page and subpage
// $subpage = '';
// $relative = this.maybeDoSubpageLink($part1, $subpage);
// if ($part1 !== $relative) {
// $part1 = $relative;
// $ns = this.mTitle->getNamespace();
// }
// $title = Title::newFromText($part1, $ns);
// if ($title) {
// $titleText = $title->getPrefixedText();
// // Check for language variants if the template is not found
// if (this.getConverterLanguage()->hasVariants() && $title->getArticleID() == 0) {
// this.getConverterLanguage()->findVariantLink($part1, $title, true);
// }
// // Do recursion depth check
// $limit = this.mOptions->getMaxTemplateDepth();
// if ($frame->depth >= $limit) {
// $found = true;
// $text = '<span class="error">'
// . wfMessage('parser-template-recursion-depth-warning')
// ->numParams($limit)->inContentLanguage()->text()
// . '</span>';
// Parser functions
if (!found) {
int colonPos = XophpString_.strpos(part1, ":");
if (!XophpInt_.is_false(colonPos)) {
// String func = XophpString_.substr(part1, 0, colonPos);
XophpArray funcArgs = XophpArray.New(XophpString_.trim(XophpString_.substr(part1, colonPos + 1)));
int argsLength = args.getLength();
for (int i = 0; i < argsLength; i++) {
funcArgs.Add(args.item(i));
}
XophpArray result = XophpArray.New();
// result = this.callParserFunction(frame, func, funcArgs);
// Extract any forwarded flags
if (XophpArray_.isset(result, "title")) {
title = (XomwTitle)result.Get_by("title");
}
if (XophpArray_.isset(result, "found")) {
found = result.Get_by_bool("found");
}
if (XophpArray_.array_key_exists("text", result)) {
// a String or null
text = result.Get_by_str("text");
}
if (XophpArray_.isset(result, "nowiki")) {
nowiki = result.Get_by_bool("nowiki");
}
if (XophpArray_.isset(result, "isHTML")) {
isHTML = result.Get_by_bool("isHTML");
}
if (XophpArray_.isset(result, "forceRawInterwiki")) {
forceRawInterwiki = result.Get_by_bool("forceRawInterwiki");
}
if (XophpArray_.isset(result, "isChildObj")) {
isChildObj = result.Get_by_bool("isChildObj");
}
if (XophpArray_.isset(result, "isLocalObj")) {
isLocalObj = result.Get_by_bool("isLocalObj");
}
}
}
// Finish mangling title and then check for loops.
// Set title to a Title Object and titleText to the PDBK
if (!found) {
int ns = XomwDefines.NS_TEMPLATE;
// Split the title into page and subpage
// String subpage = "";
// String relative = this.maybeDoSubpageLink(part1, subpage);
String relative = "";
if (!String_.Eq(part1, relative)) {
part1 = relative;
ns = this.mTitle.getNamespace();
}
title = XomwTitle.newFromText(env, part1, ns);
if (XophpObject_.is_true(title)) {
titleText = title.getPrefixedTextStr();
// Check for language variants if the template is not found
// if (this.getTargetLanguage().hasVariants() && title.getArticleID() == 0) {
// this.getTargetLanguage().findVariantLink(part1, title, true);
// }
// }
// }
//
// // Load from database
// if (!$found && $title) {
// $profileSection = this.mProfiler->scopedProfileIn($title->getPrefixedDBkey());
// if (!$title->isExternal()) {
// if ($title->isSpecialPage()
// && this.mOptions->getAllowSpecialInclusion()
// && this.ot['html']
// Do recursion depth check
int limit = this.mOptions.getMaxTemplateDepth();
if (frame.depth >= limit) {
found = true;
// text = "<span class=\"error\">"
// + wfMessage("parser-template-recursion-depth-warning")
// .numParams(limit).inContentLanguage().text()
// + "</span>";
}
}
}
// Load from database
if (!found && XophpObject_.is_true(title)) {
// profileSection = this.mProfiler.scopedProfileIn(title.getPrefixedDBkey());
// if (!title.isExternal()) {
// if (title.isSpecialPage()
// && this.mOptions.getAllowSpecialInclusion()
// && this.ot["html"]
// ) {
// $specialPage = SpecialPageFactory::getPage($title->getDBkey());
// specialPage = this.specialPageFactory.getPage(title.getDBkey());
// // Pass the template arguments as URL parameters.
// // "uselang" will have no effect since the Language Object
// // is forced to the one defined in ParserOptions.
// $pageArgs = [];
// $argsLength = $args->getLength();
// for ($i = 0; $i < $argsLength; $i++) {
// $bits = $args->item($i)->splitArg();
// if (strval($bits['index']) === '') {
// $name = trim($frame->expand($bits['name'], PPFrame::STRIP_COMMENTS));
// $value = trim($frame->expand($bits['value']));
// $pageArgs[$name] = $value;
// pageArgs = [];
// argsLength = args.getLength();
// for (i = 0; i < argsLength; i++) {
// bits = args.item(i).splitArg();
// if (strval(bits["index"]) === "") {
// name = trim(frame.expand(bits["name"], PPFrame::STRIP_COMMENTS));
// value = trim(frame.expand(bits["value"]));
// pageArgs[name] = value;
// }
// }
//
// // Create a new context to execute the special page
// $context = new RequestContext;
// $context->setTitle($title);
// $context->setRequest(new FauxRequest($pageArgs));
// if ($specialPage && $specialPage->maxIncludeCacheTime() === 0) {
// $context->setUser(this.getUser());
// context = new RequestContext;
// context.setTitle(title);
// context.setRequest(new FauxRequest(pageArgs));
// if (specialPage && specialPage.maxIncludeCacheTime() === 0) {
// context.setUser(this.getUser());
// } else {
// // If this page is cached, then we better not be per user.
// $context->setUser(User::newFromName('127.0.0.1', false));
// context.setUser(User::newFromName("127.0.0.1", false));
// }
// $context->setLanguage(this.mOptions->getUserLangObj());
// $ret = SpecialPageFactory::capturePath(
// $title, $context, this.getLinkRenderer());
// if ($ret) {
// $text = $context->getOutput()->getHTML();
// this.mOutput->addOutputPageMetadata($context->getOutput());
// $found = true;
// $isHTML = true;
// if ($specialPage && $specialPage->maxIncludeCacheTime() !== false) {
// this.mOutput->updateRuntimeAdaptiveExpiry(
// $specialPage->maxIncludeCacheTime()
// context.setLanguage(this.mOptions.getUserLangObj());
// ret = this.specialPageFactory.capturePath(title, context, this.getLinkRenderer());
// if (ret) {
// text = context.getOutput().getHTML();
// this.mOutput.addOutputPageMetadata(context.getOutput());
// found = true;
// isHTML = true;
// if (specialPage && specialPage.maxIncludeCacheTime() !== false) {
// this.mOutput.updateRuntimeAdaptiveExpiry(
// specialPage.maxIncludeCacheTime()
// );
// }
// }
// } elseif (MWNamespace::isNonincludable($title->getNamespace())) {
// $found = false; // access denied
// } elseif (MWNamespace::isNonincludable(title.getNamespace())) {
// found = false; # access denied
// wfDebug(__METHOD__ . ": template inclusion denied for " .
// $title->getPrefixedDBkey() . "\n");
// title.getPrefixedDBkey() . "\n");
// } else {
// list($text, $title) = this.getTemplateDom($title);
// if ($text !== false) {
// $found = true;
// $isChildObj = true;
// list(text, title) = this.getTemplateDom(title);
// if (text !== false) {
// found = true;
// isChildObj = true;
// }
// }
//
// // If the title is valid but undisplayable, make a link to it
// if (!$found && (this.ot['html'] || this.ot['pre'])) {
// $text = "[[:$titleText]]";
// $found = true;
// if (!found && (this.ot["html"] || this.ot["pre"])) {
// text = "[[:titleText]]";
// found = true;
// }
// } elseif ($title->isTrans()) {
// } elseif (title.isTrans()) {
// // Interwiki transclusion
// if (this.ot['html'] && !$forceRawInterwiki) {
// $text = this.interwikiTransclude($title, 'render');
// $isHTML = true;
// if (this.ot["html"] && !forceRawInterwiki) {
// text = this.interwikiTransclude(title, "render");
// isHTML = true;
// } else {
// $text = this.interwikiTransclude($title, 'raw');
// text = this.interwikiTransclude(title, "raw");
// // Preprocess it like a template
// $text = this.preprocessToDom($text, XomwParser.PTD_FOR_INCLUSION);
// $isChildObj = true;
// text = this.preprocessToDom(text, self::PTD_FOR_INCLUSION);
// isChildObj = true;
// }
// $found = true;
// }
//
// // Do infinite loop check
// // This has to be done after redirect resolution to avoid infinite loops via redirects
// if (!$frame->loopCheck($title)) {
// $found = true;
// $text = '<span class="error">'
// . wfMessage('parser-template-loop-warning', $titleText)->inContentLanguage()->text()
// . '</span>';
// wfDebug(__METHOD__ . ": template loop broken at '$titleText'\n");
// found = true;
// }
// }
//
// // If we haven't found text to substitute by now, we're done
// // Recover the source wikitext and return it
// if (!$found) {
// $text = $frame->virtualBracketedImplode('{{', '|', '}}', $titleWithSpaces, $args);
// if ($profileSection) {
// this.mProfiler->scopedProfileOut($profileSection);
// Do infinite loop check
// This has to be done after redirect resolution to avoid infinite loops via redirects
if (!frame.loopCheck(title)) {
found = true;
// text = "<span class=\"error\">"
// + wfMessage("parser-template-loop-warning", titleText).inContentLanguage().text()
// + "</span>";
// this.addTrackingCategory("template-loop-category");
// this.mOutput.addWarning(wfMessage("template-loop-warning",
// wfEscapeWikiText(titleText)).text());
// wfDebug(__METHOD__ . ": template loop broken at "titleText"\n");
}
}
// If we haven"t found text to substitute by now, we"re done
// Recover the source wikitext and return it
// if (!found) {
// text = frame.virtualBracketedImplode("{{", "|", "}}", titleWithSpaces, args);
// if (profileSection) {
// this.mProfiler.scopedProfileOut(profileSection);
// }
// return [ 'Object' => $text ];
// return ["Object", text];
// }
//
// XomwPPFrame newFrame = null;
// // Expand DOM-style return values in a child frame
// if ($isChildObj) {
// if (isChildObj) {
// // Clean up argument array
// $newFrame = $frame->newChild($args, $title);
// newFrame = frame.newChild(args, title);
//
// if ($nowiki) {
// $text = $newFrame->expand($text, PPFrame::RECOVER_ORIG);
// } elseif ($titleText !== false && $newFrame->isEmpty()) {
// if (nowiki) {
// text = newFrame.expand(text, XomwPPFrame.RECOVER_ORIG);
// } else if (!XophpString_.is_false(titleText) && newFrame.isEmpty()) {
// // Expansion is eligible for the empty-frame cache
// $text = $newFrame->cachedExpand($titleText, $text);
// text = newFrame.cachedExpand(titleText, text);
// } else {
// // Uncached expansion
// $text = $newFrame->expand($text);
// text = newFrame.expand(text);
// }
// }
// if ($isLocalObj && $nowiki) {
// $text = $frame->expand($text, PPFrame::RECOVER_ORIG);
// $isLocalObj = false;
// if (isLocalObj && nowiki) {
// text = frame.expand(text, PPFrame::RECOVER_ORIG);
// isLocalObj = false;
// }
//
// if ($profileSection) {
// this.mProfiler->scopedProfileOut($profileSection);
// if (profileSection) {
// this.mProfiler.scopedProfileOut(profileSection);
// }
//
// // Replace raw HTML by a placeholder
// if ($isHTML) {
// $text = this.insertStripItem($text);
// } elseif ($nowiki && (this.ot['html'] || this.ot['pre'])) {
// if (isHTML) {
// text = this.insertStripItem(text);
// } elseif (nowiki && (this.ot["html"] || this.ot["pre"])) {
// // Escape nowiki-style return values
// $text = wfEscapeWikiText($text);
// } elseif (is_string($text)
// && !$piece['lineStart']
// && preg_match('/^(?:{\\||:|;|#|\*)/', $text)
// text = wfEscapeWikiText(text);
// } elseif (is_string(text)
// && !piece["lineStart"]
// && preg_match("/^(?:{\\||:|;|#|\*)/", text)
// ) {
// // T2529: if the template begins with a table or block-level
// // element, it should be treated as beginning a new line.
// // This behavior is somewhat controversial.
// $text = "\n" . $text;
// text = "\n" . text;
// }
//
// if (is_string($text) && !this.incrementIncludeSize('post-expand', strlen($text))) {
// if (is_string(text) && !this.incrementIncludeSize("post-expand", strlen(text))) {
// // Error, oversize inclusion
// if ($titleText !== false) {
// if (titleText !== false) {
// // Make a working, properly escaped link if possible (T25588)
// $text = "[[:$titleText]]";
// text = "[[:titleText]]";
// } else {
// // This will probably not be a working link, but at least it may
// // provide some hint of where the problem is
// preg_replace('/^:/', '', $originalTitle);
// $text = "[[:$originalTitle]]";
// preg_replace("/^:/", "", originalTitle);
// text = "[[:originalTitle]]";
// }
// $text .= this.insertStripItem('<!-- WARNING: template omitted, '
// . 'post-expand include size too large -->');
// this.limitationWarn('post-expand-template-inclusion');
// text .= this.insertStripItem("<!-- WARNING: template omitted, "
// . "post-expand include size too large -.");
// this.limitationWarn("post-expand-template-inclusion");
// }
//
// if ($isLocalObj) {
// $ret = [ 'Object' => $text ];
// } else {
// $ret = [ 'text' => $text ];
// }
//
// return $ret;
return null;
XophpArray ret;
if (isLocalObj) {
ret = XophpArray.New("Object", text);
} else {
ret = XophpArray.New("text", text);
}
Tfds.Write(nowiki, isHTML, forceRawInterwiki, isChildObj, isLocalObj, titleText, originalTitle);
return ret;
}
//
// /**
// * Call a parser function and return an array with text and flags.
// *

@ -89,11 +89,11 @@ public class XomwParserOptions {
*/
private int mMaxPPExpandDepth;
// /**
// * Maximum recursion depth for templates within templates
// */
// private $mMaxTemplateDepth;
//
/**
* Maximum recursion depth for templates within templates
*/
private int mMaxTemplateDepth;
// /**
// * Maximum number of calls per parse to expensive parser functions
// */
@ -288,10 +288,10 @@ public class XomwParserOptions {
return this.mMaxPPExpandDepth;
}
// public function getMaxTemplateDepth() {
// return this.mMaxTemplateDepth;
// }
//
public int getMaxTemplateDepth() {
return this.mMaxTemplateDepth;
}
// /* @since 1.20 */
// public function getExpensiveParserFunctionLimit() {
// return this.mExpensiveParserFunctionLimit;
@ -706,7 +706,7 @@ public class XomwParserOptions {
this.mMaxPPNodeCount = XomwDefaultSettings.wgMaxPPNodeCount;
// this.mMaxGeneratedPPNodeCount = $wgMaxGeneratedPPNodeCount;
this.mMaxPPExpandDepth = XomwDefaultSettings.wgMaxPPExpandDepth;
// this.mMaxTemplateDepth = $wgMaxTemplateDepth;
this.mMaxTemplateDepth = XomwDefaultSettings.wgMaxTemplateDepth;
// this.mExpensiveParserFunctionLimit = $wgExpensiveParserFunctionLimit;
// this.mCleanSignatures = $wgCleanSignatures;
// this.mExternalLinkTarget = $wgExternalLinkTarget;

@ -0,0 +1,27 @@
/*
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.includes.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import org.junit.*;
public class XomwParser_internalParse_tst {
// private final XomwParser_fxt fxt = new XomwParser_fxt();
@Test public void Basic() {
// fxt.Test__internalParse(String_.Concat_lines_nl_skip_last
// ( "{{A}}"
// ), String_.Concat_lines_nl_skip_last
// ( ""
// ));
}
}

@ -15,6 +15,7 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki.includes.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import org.junit.*;
import gplx.xowa.mediawiki.includes.parsers.preprocessors.*;
public class XomwParser_tst {
private final XomwParser_fxt fxt = new XomwParser_fxt();
@Test public void Basic() {
@ -75,4 +76,8 @@ class XomwParser_fxt {
parser.internalParseHalfParsed(pbfr, pctx, true, true);
Tfds.Eq_str_lines(expd, pbfr.Rslt().To_str_and_clear(), src_str);
}
public void Test__internalParse(String src_str, String expd) {
String actl = parser.internalParse(src_str, false, new XomwPPFrame_Hash(new XomwPreprocessor_Hash(parser)));
Tfds.Eq_str_lines(expd, actl, src_str);
}
}

@ -121,7 +121,7 @@ class XomwPreprocessor_DOM extends XomwPreprocessor { private final Bry_bfr
int parts_len = parts.count();
for (int j = 0; j < parts_len; j++) {
XomwPPDPart_DOM part = (XomwPPDPart_DOM)parts.Get_at(j);
if (part.eqpos != -1) {
if (part.eqpos != 0) {
byte[] part_bfr_bry = part.To_bry();
tmp_bfr.Add_str_a7("<part><name>").Add_mid(part_bfr_bry, 0, part.eqpos);
tmp_bfr.Add_str_a7("</name>=<value>").Add_mid(part_bfr_bry, part.eqpos + 1, part.Len());

@ -126,7 +126,7 @@ class XomwPreprocessor_Hash extends XomwPreprocessor { private XophpArray accum
for (int j = 0; j < parts_len; j++) {
XomwPPDPart_Hash part = (XomwPPDPart_Hash)parts.Get_at(j);
XophpArray part_out = (XophpArray)part.Accum_hash().Ary();
if (part.eqpos != -1) {
if (part.eqpos != 0) {
Object equalsNode = part_out.Get_at(part.eqpos);
XophpArray nameNode = XophpArray.New("name" , XophpArray_.array_slice(part_out, 0, part.eqpos));
XophpArray valueNode = XophpArray.New("value", XophpArray_.array_slice(part_out, part.eqpos + 1));

@ -22,36 +22,21 @@ public abstract class XomwPPDPart {
/**
* @var String Output accumulator String
*/
// public final byte[] output;
public String output;
// REF.MW:https://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.property.pkg.html
// Optional member variables:
// eqpos Position of equals sign in output accumulator
// commentEnd Past-the-end input pointer for the last comment encountered
// visualEnd Past-the-end input pointer for the end of the accumulator minus comments
public int eqpos = -1;
public int commentEnd = -1;
public int visualEnd = -1;
public int eqpos;
public int commentEnd;
public int visualEnd;
public XomwPPDPart(String output) {
// accum.Add_bry(Bry_.new_u8(output));
// bfr = ((Xomw_prepro_accum__dom)accum).Bfr();
this.output = output;
}
public abstract Xomw_prepro_accum Accum();
public abstract XomwPPDPart Make_new(String val);
}
// class XomwPPDPart {
// /**
// * @var String Output accumulator String
// */
// public String output;
//
// // Optional member variables:
// // eqpos Position of equals sign in output accumulator
// // commentEnd Past-the-end input pointer for the last comment encountered
// // visualEnd Past-the-end input pointer for the end of the accumulator minus comments
//
// public XomwPPDPart(String output) {
// this.output = output;
// }
// }

@ -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.includes.parsers.preprocessors; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
public class XomwPPDStack_Hash {
}

@ -32,6 +32,10 @@ public abstract class XomwPPFrame {
/** This constant exists when $indexOffset is supported in newChild() */
public static final int SUPPORTS_INDEX_OFFSET = 1;
// https://github.com/wikimedia/mediawiki/blob/ce615343ee8d192a65cf65f35f853c5ffcfad390/includes/parser/PPFrame.php#L25-L26
public int depth;
public XomwPPFrame parent;
/**
* Create a child frame
*
@ -41,6 +45,7 @@ public abstract class XomwPPFrame {
*
* @return PPFrame
*/
public XomwPPFrame newChild(Object args, XomwTitle title) {return newChild(args, title, 0);}
@gplx.Virtual public XomwPPFrame newChild(Object args, XomwTitle title, int indexOffset) {return null;}
/**
@ -50,6 +55,7 @@ public abstract class XomwPPFrame {
* @param int $flags
* @return String
*/
public String cachedExpand(String key, XomwPPNode root) {return cachedExpand(key, root, 0);}
@gplx.Virtual public String cachedExpand(String key, XomwPPNode root, int flags) {return null;}
/**
@ -58,7 +64,8 @@ public abstract class XomwPPFrame {
* @param int $flags
* @return String
*/
@gplx.Virtual public String expand(XomwPPNode root, int flags) {return null;}
@gplx.Virtual public String expand(Object root) {return expand(root, 0);}
@gplx.Virtual public String expand(Object root, int flags) {return null;}
/**
* Implode with flags for expand()

@ -20,7 +20,7 @@ import gplx.core.bits.*;
* An expansion frame, used as a context to expand the result of preprocessToObj()
* @ingroup Parser
*/
class XomwPPFrame_Hash extends XomwPPFrame { /**
public class XomwPPFrame_Hash extends XomwPPFrame { /**
* @var Parser
*/
public XomwParser parser;
@ -46,7 +46,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
* Recursion depth of this frame, top = 0
* Note that this is NOT the same as expansion depth in expand()
*/
public int depth;
@gplx.New public int depth;
private boolean volatile_bool;
private int ttl;
@ -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_.False);
this.titleCache = XophpArray.New(XophpObject_.is_true(this.title) ? this.title.getPrefixedDBkeyStr() : XophpString_.False);
this.loopCheckHash = XophpArray.New();
this.depth = 0;
this.childExpansionCache = XophpArray.New();
@ -105,11 +105,11 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
// Numbered parameter
int index = bits.Get_by_int("index") - indexOffset;
if (namedArgs.isset(index) || numberedArgs.isset(index)) {
// this.parser.getOutput().addWarning(wfMessage('duplicate-args-warning',
// wfEscapeWikiText(this.title),
// wfEscapeWikiText(title),
// wfEscapeWikiText(index)).text());
// this.parser.addTrackingCategory('duplicate-args-category');
// this.parser.getOutput().addWarning(wfMessage('duplicate-args-warning',
// wfEscapeWikiText(this.title),
// wfEscapeWikiText(title),
// wfEscapeWikiText(index)).text());
// this.parser.addTrackingCategory('duplicate-args-category');
}
numberedArgs.Set(index, bits.Get_by("value"));
namedArgs.unset(index);
@ -117,11 +117,11 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
// Named parameter
String name = String_.Trim(this.expand(bits.Get_by("name"), XomwPPFrame.STRIP_COMMENTS));
if (namedArgs.isset(name) || numberedArgs.isset(name)) {
// this.parser.getOutput().addWarning(wfMessage('duplicate-args-warning',
// wfEscapeWikiText(this.title),
// wfEscapeWikiText(title),
// wfEscapeWikiText(name)).text());
// this.parser.addTrackingCategory('duplicate-args-category');
// this.parser.getOutput().addWarning(wfMessage('duplicate-args-warning',
// wfEscapeWikiText(this.title),
// wfEscapeWikiText(title),
// wfEscapeWikiText(name)).text());
// this.parser.addTrackingCategory('duplicate-args-category');
}
namedArgs.Set(name, bits.Get_by("value"));
numberedArgs.unset(name);
@ -151,23 +151,23 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
* @param int $flags
* @return String
*/
public String expand(Object root, int flags) {
@Override public String expand(Object root, int flags) {
if (XophpString_.is_string(root)) {
return (String)root;
}
if (++this.parser.mPPNodeCount > this.parser.mOptions.getMaxPPNodeCount()) {
// this.parser.limitationWarn('node-count-exceeded',
// this.parser.mPPNodeCount,
// this.parser.mOptions.getMaxPPNodeCount()
// );
// this.parser.limitationWarn('node-count-exceeded',
// this.parser.mPPNodeCount,
// this.parser.mOptions.getMaxPPNodeCount()
// );
return "<span class=\"error\">Node-count limit exceeded</span>";
}
if (expansionDepth > this.parser.mOptions.getMaxPPExpandDepth()) {
// this.parser.limitationWarn('expansion-depth-exceeded',
// expansionDepth,
// this.parser.mOptions.getMaxPPExpandDepth()
// );
// this.parser.limitationWarn('expansion-depth-exceeded',
// expansionDepth,
// this.parser.mOptions.getMaxPPExpandDepth()
// );
return "<span class=\"error\">Expansion depth limit exceeded</span>";
}
++expansionDepth;
@ -396,7 +396,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
root = (XophpArray)((XomwPPNode_Hash_Array)root_obj).value;
}
if (!XophpType_.is_array(root_obj)) {
root = XophpArray.New().Add(root_obj);
root = XophpArray.New(root_obj);
}
int root_len = root.Len();
for (int j = 0; j < root_len; j++) {
@ -428,7 +428,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
root = ((XomwPPNode_Hash_Array)rootObj).value;
}
if (!XophpArray.is_array(rootObj)) {
root = XophpArray.New().Add(root);
root = XophpArray.New(root);
}
int rootLen = root.count();
for (int i = 0; i < rootLen; i++) {
@ -462,7 +462,7 @@ class XomwPPFrame_Hash extends XomwPPFrame { /**
root = ((XomwPPNode_Hash_Array)rootObj).value;
}
if (!XophpArray.is_array(rootObj)) {
root = XophpArray.New().Add(root);
root = XophpArray.New(root);
}
int rootLen = root.count();
for (int i = 0; i < rootLen; i++) {

@ -365,7 +365,7 @@ public class XomwPPNode_Hash_Tree extends XomwPPNode { public final String na
*/
public static XophpArray splitRawTemplate(XophpArray children) {
XophpArray parts = XophpArray.New();
XophpArray bits = XophpArray.New().Add("lineStart" , "");
XophpArray bits = XophpArray.New("lineStart" , "");
int len = children.count();
for (int i = 0; i < len; i++) {
Object childObj = children.Get_at(i);

@ -19,7 +19,7 @@ package gplx.xowa.mediawiki.includes.parsers.preprocessors; import gplx.*; impor
// * @ingroup Parser
// */
class XomwPPTemplateFrame_Hash extends XomwPPFrame_Hash { public XophpArray numberedArgs, namedArgs;
public XomwPPFrame_Hash parent;
@gplx.New public XomwPPFrame_Hash parent;
public XophpArray numberedExpansionCache, namedExpansionCache;
/**

@ -0,0 +1,39 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
public abstract class XomwPPDPart {
/**
* @var String Output accumulator String
*/
public XophpArray output;
// REF.MW:https://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_tags.property.pkg.html
// Optional member variables:
// eqpos Position of equals sign in output accumulator
// commentEnd Past-the-end input pointer for the last comment encountered
// visualEnd Past-the-end input pointer for the end of the accumulator minus comments
public int eqpos;
public int commentEnd;
public int visualEnd;
public void ctor_XomwPPDPart(XophpArray output) {
this.output = output;
}
public XomwPPDPart New() {return New("");}
public abstract XomwPPDPart New(String val);
}

@ -0,0 +1,34 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
/**
* @ingroup Parser
*/
public class XomwPPDPart_Hash extends XomwPPDPart {
public XomwPPDPart_Hash(String output) {
XophpArray accum;
if (XophpString_.eq_not(output, "")) {
accum = XophpArray.New(output);
} else {
accum = XophpArray.New();
}
this.ctor_XomwPPDPart(accum);
}
@Override public XomwPPDPart New(String val) {return new XomwPPDPart_Hash(val);}
public static final XomwPPDPart_Hash Instance = new XomwPPDPart_Hash("");
}

@ -0,0 +1,116 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
import gplx.xowa.mediawiki.includes.exception.*;
/**
* Stack class to help Preprocessor::preprocessToObj()
* @ingroup Parser
*/
public class XomwPPDStack {
public XophpArray stack;
public XophpArray rootAccum;
public XophpArray accum;
/**
* @var PPDStack
*/
public XomwPPDStackElement top;
// public out;
public XomwPPDStackElement elementClass;
// public static false = false;
private final XomwPPDPart partClass;
public XomwPPDStack(XomwPPDPart partClass) {
this.stack = XophpArray.New();
this.top = null;
this.rootAccum = XophpArray.New("");
this.accum = this.rootAccum; // =&
this.partClass = partClass;
}
/**
* @return int
*/
public int count() {
return XophpArray_.count(this.stack);
}
public XophpArray getAccum() { // &getAccum
return this.accum;
}
/**
* @return boolean|PPDPart
*/
public XomwPPDPart getCurrentPart() {
if (XophpObject_.is_false(this.top)) {
return null;
} else {
return this.top.getCurrentPart();
}
}
public void push(Object data) {
if (XophpType_.instance_of(data, XomwPPDStackElement.class)) {
this.stack.Add(data);
} else {
XophpArray array = (XophpArray)data;
this.stack.Add(elementClass.New(partClass, array.Get_by_str("open"), array.Get_by_str("close"), array.Get_by_ary_or("parts", null), array.Get_by_int_or("count", 0), array.Get_by_bool_or("lineStart", false), array.Get_by_int_or("startPos", 0)));
}
this.top = (XomwPPDStackElement)this.stack.Get_at(XophpArray_.count(this.stack) - 1);
this.accum = this.top.getAccum(); //=&
}
public XomwPPDStackElement pop() {
if (this.stack.Eq_to_new()) {
throw XomwMWException.New_by_method_obj(this, "pop", ": no elements remaining");
}
XomwPPDStackElement temp = (XomwPPDStackElement)XophpArray_.array_pop(this.stack);
if (XophpArray_.count_bool(this.stack)) {
this.top = (XomwPPDStackElement)this.stack.Get_at(XophpArray_.count(this.stack) - 1);
this.accum = this.top.getAccum(); // =&
} else {
this.top = null;
this.accum = this.rootAccum; // =&
}
return temp;
}
public void addPart() {addPart("");}
public void addPart(String s) {
this.top.addPart(s);
this.accum = this.top.getAccum(); // =&
}
/**
* @return array
*/
public XophpArray getFlags() {
if (this.stack.Eq_to_new()) {
return XophpArray.New()
.Add("findEquals", false)
.Add("findPipe", false)
.Add("inHeading", false)
;
} else {
return this.top.getFlags();
}
}
}

@ -0,0 +1,138 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
/**
* Stack cl+ass to help Preprocessor::preprocessToObj()
* @ingroup Parser
*/
public abstract class XomwPPDStackElement {
/**
* @var String Opening character (\n for heading)
*/
public String open;
/**
* @var String Matching closing character
*/
public String close;
/**
* @var String Saved prefix that may affect later processing,
* e.g. to differentiate `-{{{{` and `{{{{` after later seeing `}}}`.
*/
public String savedPrefix = "";
/**
* @var int Number of opening characters found (number of "=" for heading)
*/
public int count;
/**
* @var PPDPart[] Array of PPDPart objects describing pipe-separated parts.
*/
public XophpArray parts;
/**
* @var boolean True if the open char appeared at the start of the input line.
* Not set for headings.
*/
public boolean lineStart;
public XomwPPDPart partClass;// = new XomwPPDPart("");
public int startPos; // XO.NOTE: added from Preprocessor_Hash; assume this is magic property somewhere
public XomwPPDStackElement(XomwPPDPart partClass, String open, String close, XophpArray parts, int count, boolean lineStart, int startPos) {
this.partClass = partClass;
this.parts = parts == null ? XophpArray.New(partClass.New()) : parts;
// XO.NOTE: PHP does XophpArray.New().Add("open", open)... but PHP does reflection-like assignment of array members to class members;
// EX: foreach (item in data) {item.key = item.val;}
this.open = open;
this.close = close;
this.count = count;
this.lineStart = lineStart;
this.startPos = startPos;
}
public XophpArray getAccum() {
return (XophpArray)((XomwPPDPart)(this.parts.Get_at(XophpArray_.count(this.parts) - 1))).output;
}
public void addPart(String s) { // s = ""
XomwPPDPart clz = this.partClass;
this.parts.Add(clz.New(s));
}
/**
* @return PPDPart
*/
public XomwPPDPart getCurrentPart() {
return (XomwPPDPart)this.parts.Get_at(XophpArray_.count(this.parts) - 1);
}
/**
* @return array
*/
public XophpArray getFlags() {
int partCount = XophpArray_.count(this.parts);
boolean findPipe = !String_.Eq(this.open, "\n") && !String_.Eq(this.open, "[");
return XophpArray.New()
.Add("findPipe", findPipe)
.Add("findEquals", findPipe && partCount > 1 && !XophpObject_.isset_obj(((XomwPPDPart)this.parts.Get_at(partCount - 1)).eqpos))
.Add("inHeading", String_.Eq(this.open, "\n"))
;
}
/**
* Get the output String that would result if the close is not found.
*
* @param boolean|int $openingCount
* @return String
*/
// Used by XomwPPDStackElement_DOM
// public String breakSyntax(int openingCount) { // openingCount = false
// String s = null;
// if (String_.Eq(this.open, "\n")) {
// s = this.savedPrefix + ((XomwPPDPart)this.parts.Get_at(0)).output;
// } else {
// if (XophpInt_.is_false(openingCount)) {
// openingCount = this.count;
// }
// s = XophpString_.substr(this.open, 0, -1);
// s += XophpString_.str_repeat(
// XophpString_.substr(this.open, -1),
// openingCount - XophpString_.strlen(s)
// );
// s = this.savedPrefix + s;
// boolean first = true;
// int parts_len = this.parts.Len();
// for (int i = 0; i < parts_len; i++) {
// XomwPPDPart part = (XomwPPDPart)this.parts.Get_at(i);
// if (first) {
// first = false;
// } else {
// s += '|';
// }
// s += part.output;
// }
// }
// return s;
// }
public abstract XomwPPDStackElement New(XomwPPDPart partClass, String open, String close, XophpArray parts, int count, boolean lineStart, int startPos);
}

@ -0,0 +1,74 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
class XomwPPDStackElement_Hash extends XomwPPDStackElement {
public XomwPPDStackElement_Hash(XomwPPDPart partClass, String open, String close, XophpArray parts, int count, boolean lineStart, int startPos) {super(partClass, open, close, parts, count, lineStart, startPos);
}
/**
* Get the accumulator that would result if the close is not found.
*
* @param int|boolean $openingCount
* @return array
*/
public XophpArray breakSyntax() {return breakSyntax(0);}
public XophpArray breakSyntax(int openingCount) {
XophpArray accum;
if (String_.Eq(this.open, "\n")) {
accum = XophpArray_.array_merge(XophpArray.New(this.savedPrefix), XophpArray.New(((XomwPPDPart)this.parts.Get_at(0)).output));
} else {
if (XophpInt_.is_false(openingCount)) {
openingCount = this.count;
}
String s = XophpString_.substr(this.open, 0, -1);
s += XophpString_.str_repeat(
XophpString_.substr(this.open, -1),
openingCount - XophpString_.strlen(s)
);
accum = XophpArray.New(this.savedPrefix + s);
int lastIndex = 0;
boolean first = true;
int parts_len = this.parts.Len();
for (int i = 0; i < parts_len; i++) {
XomwPPDPart part = (XomwPPDPart)this.parts.Get_at(i);
if (first) {
first = false;
} else if (XophpType_.is_string(accum.Get_at(lastIndex))) {
accum.Concat_str(lastIndex, "|");
} else {
accum.Set(++lastIndex, "|");
}
XophpArray partOutput = part.output;
int partOutputLen = partOutput.Len();
for (int j = 0; j < partOutputLen; j++) {
Object node = partOutput.Get_at(j);
if (XophpType_.is_string(node) && XophpType_.is_string(accum.Get_at(lastIndex))) {
accum.Concat_str(lastIndex, (String)node);
} else {
accum.Set(++lastIndex, node);
}
}
}
}
return accum;
}
@Override public XomwPPDStackElement New(XomwPPDPart partClass, String open, String close, XophpArray parts, int count, boolean lineStart, int startPos) {return new XomwPPDStackElement_Hash(partClass, open, close, parts, count, lineStart, startPos);}
public static final XomwPPDStackElement_Hash Prototype = new XomwPPDStackElement_Hash(new XomwPPDPart_Hash(""), null, null, null, 0, false, 0);
}

@ -0,0 +1,27 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
/**
* Stack cl+ass to help Preprocessor::preprocessToObj()
* @ingroup Parser
*/
public class XomwPPDStack_Hash extends XomwPPDStack {
public XomwPPDStack_Hash(XomwPPDPart partClass) {super(partClass);
this.elementClass = XomwPPDStackElement_Hash.Prototype;
// this.rootAccum = XophpArray.New(); // XO.NOTE: PHP does accum &= rootAccum, so doing "this.rootAccum = XophpArray.New()" will also do "accum = this.rootAccum" automatically
}
}

@ -0,0 +1,205 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.FILE:Preprocessor
/**
* @ingroup Parser
*/
// XOWA: TODO: change VIRTUAL back to ABSTRACT; WHEN: after adding _DOM classes
public abstract class XomwPPFrame {
public static final int NO_ARGS = 1;
public static final int NO_TEMPLATES = 2;
public static final int STRIP_COMMENTS = 4;
public static final int NO_IGNORE = 8;
public static final int RECOVER_COMMENTS = 16;
public static final int NO_TAGS = 32;
public static final int RECOVER_ORIG = 59; // = 1|2|8|16|32 no constant expression support in PHP yet
/** This constant exists when $indexOffset is supported in newChild() */
public static final int SUPPORTS_INDEX_OFFSET = 1;
// https://github.com/wikimedia/mediawiki/blob/ce615343ee8d192a65cf65f35f853c5ffcfad390/includes/parser/PPFrame.php#L25-L26
public int depth;
public XomwPPFrame parent;
/**
* Create a child frame
*
* @param array|boolean $args
* @param boolean|Title $title
* @param int $indexOffset A number subtracted from the index attributes of the arguments
*
* @return PPFrame
*/
@gplx.Virtual public XomwPPFrame newChild(Object args, XomwTitle title, int indexOffset) {return null;}
/**
* Expand a document tree node, caching the result on its parent with the given key
* @param String|int $key
* @param String|PPNode $root
* @param int $flags
* @return String
*/
@gplx.Virtual public String cachedExpand(String key, XomwPPNode root, int flags) {return null;}
/**
* Expand a document tree node
* @param String|PPNode $root
* @param int $flags
* @return String
*/
@gplx.Virtual public String expand(Object root) {return expand(root, 0);}
@gplx.Virtual public String expand(Object root, int flags) {return null;}
/**
* Implode with flags for expand()
* @param String $sep
* @param int $flags
* @param String|PPNode $args,...
* @return String
*/
@gplx.Virtual public String implodeWithFlags(String sep, int flags, Object... args) {return null;}
/**
* Implode with no flags specified
* @param String $sep
* @param String|PPNode $args,...
* @return String
*/
@gplx.Virtual public String implode(String sep, Object... args) {return null;}
/**
* Makes an Object that, when expand()ed, will be the same as one obtained
* with implode()
* @param String $sep
* @param String|PPNode $args,...
* @return PPNode
*/
@gplx.Virtual public XomwPPNode virtualImplode(String sep, Object... args) {return null;}
/**
* Virtual implode with brackets
* @param String $start
* @param String $sep
* @param String $end
* @param String|PPNode $args,...
* @return PPNode
*/
@gplx.Virtual public XomwPPNode virtualBracketedImplode(String start, String sep, String end, Object... args) {return null;}
/**
* Returns true if there are no arguments in this frame
*
* @return boolean
*/
@gplx.Virtual public boolean isEmpty() {return false;}
/**
* Returns all arguments of this frame
* @return array
*/
@gplx.Virtual public XophpArray getArguments() {return null;}
/**
* Returns all numbered arguments of this frame
* @return array
*/
@gplx.Virtual public XophpArray getNumberedArguments() {return null;}
/**
* Returns all named arguments of this frame
* @return array
*/
@gplx.Virtual public XophpArray getNamedArguments() {return null;}
/**
* Get an argument to this frame by name
* @param int|String $name
* @return String|boolean
*/
@gplx.Virtual public String getArgument(String name) {return null;}
/**
* Returns true if the infinite loop check is OK, false if a loop is detected
*
* @param Title $title
* @return boolean
*/
@gplx.Virtual public boolean loopCheck(XomwTitle title) {return false;}
/**
* Return true if the frame is a template frame
* @return boolean
*/
@gplx.Virtual public boolean isTemplate() {return false;}
/**
* Set the "volatile" flag.
*
* Note that this is somewhat of a "hack" in order to make extensions
* with side effects (such as Cite) work with the PHP parser. New
* extensions should be written in a way that they do not need this
* function, because other parsers (such as Parsoid) are not guaranteed
* to respect it, and it may be removed in the future.
*
* @param boolean $flag
*/
@gplx.Virtual public void setVolatile(boolean flag) {}
/**
* Get the "volatile" flag.
*
* Callers should avoid caching the result of an expansion if it has the
* volatile flag set.
*
* @see self::setVolatile()
* @return boolean
*/
@gplx.Virtual public boolean isVolatile() {return false;}
/**
* Get the TTL of the frame's output.
*
* This is the maximum amount of time, in seconds, that this frame's
* output should be cached for. A value of null indicates that no
* maximum has been specified.
*
* Note that this TTL only applies to caching frames as parts of pages.
* It is not relevant to caching the entire rendered output of a page.
*
* @return int|null
*/
@gplx.Virtual public int getTTL() {return 0;}
/**
* Set the TTL of the output of this frame and all of its ancestors.
* Has no effect if the new TTL is greater than the one already set.
* Note that it is the caller's responsibility to change the cache
* expiry of the page as a whole, if such behavior is desired.
*
* @see self::getTTL()
* @param int $ttl
*/
@gplx.Virtual public void setTTL(int ttl) {}
/**
* Get a title of frame
*
* @return Title
*/
@gplx.Virtual public XomwTitle getTitle() {return null;}
}

@ -0,0 +1,638 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
import gplx.xowa.mediawiki.includes.exception.*;
import gplx.core.bits.*;
/**
* An expansion frame, used as a context to expand the result of preprocessToObj()
* @ingroup Parser
*/
public class XomwPPFrame_Hash extends XomwPPFrame { // /**
// * @var Parser
// */
// public XomwParser parser;
//
// /**
// * @var Preprocessor
// */
// public XomwPreprocessor preprocessor;
//
// /**
// * @var Title
// */
// public XomwTitle title;
// public XophpArray titleCache;
//
// /**
// * Hashtable listing templates which are disallowed for expansion in this frame,
// * having been encountered previously in parent frames.
// */
// public XophpArray loopCheckHash;
//
// /**
// * Recursion depth of this frame, top = 0
// * Note that this is NOT the same as expansion depth in expand()
// */
// @gplx.New public int depth;
//
// private boolean volatile_bool;
// private int ttl;
//
// /**
// * @var array
// */
// public XophpArray childExpansionCache;
//
// /**
// * Construct a new preprocessor frame.
// * @param Preprocessor preprocessor The parent preprocessor
// */
public XomwPPFrame_Hash(XomwPreprocessor preprocessor) {
// this.preprocessor = preprocessor;
// this.parser = preprocessor.Parser();
// this.title = this.parser.mTitle;
// this.titleCache = XophpArray.New(XophpObject_.is_true(this.title) ? this.title.getPrefixedDBkeyStr() : XophpString_.False);
// this.loopCheckHash = XophpArray.New();
// this.depth = 0;
// this.childExpansionCache = XophpArray.New();
}
//
// /**
// * Create a new child frame
// * $args is optionally a multi-root PPNode or array containing the template arguments
// *
// * @param array|boolean|PPNode_Hash_Array $args
// * @param Title|boolean $title
// * @param int $indexOffset
// * @throws MWException
// * @return PPTemplateFrame_Hash
// */
// public override XomwPPFrame newChild(Object argsObj, XomwTitle title, int indexOffset) {
// XophpArray namedArgs = XophpArray.New();
// XophpArray numberedArgs = XophpArray.New();
// if (!XophpObject_.is_true(title)) {
// title = this.title;
// }
// if (XophpObject_.is_true(argsObj)) {
// XophpArray args = null;
// if (Type_.Eq_by_obj(argsObj, typeof(XomwPPNode_Hash_Array))) {
// args = ((XomwPPNode_Hash_Array)argsObj).value;
// } else if (!XophpArray.is_array(argsObj)) {
// throw XomwMWException.New_by_method(typeof(XomwPPFrame_Hash), "newChild", ": args must be array or PPNode_Hash_Array");
// }
// else {
// args = (XophpArray)argsObj;
// }
//
// int argsLen = args.count();
// for (int i = 0; i < argsLen; i++) {
// XomwPPNode arg = (XomwPPNode)args.Get_at(i);
// XophpArray bits = arg.splitArg();
// if (bits.Has("index")) {
// // Numbered parameter
// int index = bits.Get_by_int("index") - indexOffset;
// if (namedArgs.isset(index) || numberedArgs.isset(index)) {
// // this.parser.getOutput().addWarning(wfMessage('duplicate-args-warning',
// // wfEscapeWikiText(this.title),
// // wfEscapeWikiText(title),
// // wfEscapeWikiText(index)).text());
// // this.parser.addTrackingCategory('duplicate-args-category');
// }
// numberedArgs.Set(index, bits.Get_by("value"));
// namedArgs.unset(index);
// } else {
// // Named parameter
// String name = String_.Trim(this.expand(bits.Get_by("name"), XomwPPFrame.STRIP_COMMENTS));
// if (namedArgs.isset(name) || numberedArgs.isset(name)) {
// // this.parser.getOutput().addWarning(wfMessage('duplicate-args-warning',
// // wfEscapeWikiText(this.title),
// // wfEscapeWikiText(title),
// // wfEscapeWikiText(name)).text());
// // this.parser.addTrackingCategory('duplicate-args-category');
// }
// namedArgs.Set(name, bits.Get_by("value"));
// numberedArgs.unset(name);
// }
// }
// }
// return new XomwPPTemplateFrame_Hash(this.preprocessor, this, numberedArgs, namedArgs, title);
// }
//
// /**
// * @throws MWException
// * @param String|int $key
// * @param String|PPNode $root
// * @param int $flags
// * @return String
// */
// public String cachedExpand(String key, Object root, int flags) { // DEFAULT:flags=0
// // we don't have a parent, so we don't have a cache
// return this.expand(root, flags);
// }
//
// private static int expansionDepth = 0; // MW.GLOBAL:expand
// private static int expand_flags_default = 0;
// /**
// * @throws MWException
// * @param String|PPNode $root
// * @param int $flags
// * @return String
// */
// public override String expand(Object root, int flags) {
// if (XophpString_.is_string(root)) {
// return (String)root;
// }
//
// if (++this.parser.mPPNodeCount > this.parser.mOptions.getMaxPPNodeCount()) {
// // this.parser.limitationWarn('node-count-exceeded',
// // this.parser.mPPNodeCount,
// // this.parser.mOptions.getMaxPPNodeCount()
// // );
// return "<span class=\"error\">Node-count limit exceeded</span>";
// }
// if (expansionDepth > this.parser.mOptions.getMaxPPExpandDepth()) {
// // this.parser.limitationWarn('expansion-depth-exceeded',
// // expansionDepth,
// // this.parser.mOptions.getMaxPPExpandDepth()
// // );
// return "<span class=\"error\">Expansion depth limit exceeded</span>";
// }
// ++expansionDepth;
// if (expansionDepth > this.parser.mHighestExpansionDepth) {
// this.parser.mHighestExpansionDepth = expansionDepth;
// }
//
// XophpArray outStack = XophpArray.New("", "");
// XophpArray iteratorStack = XophpArray.New(XophpObject_.False, root);
// XophpArray indexStack = XophpArray.New(0, 0);
//
// while (iteratorStack.count() > 1) {
// int level = outStack.count() - 1;
// Object iteratorNode = iteratorStack.Get_at(level);
// String outItm = outStack.Get_at_str(level);
// int index = indexStack.Get_at_int(level);
// Object contextNode;
// if (XophpArray.is_array(iteratorNode)) {
// XophpArray iteratorNodeArray = (XophpArray)iteratorNode;
// if (index >= iteratorNodeArray.count()) {
// // All done with this iterator
// iteratorStack.Set(level, XophpObject_.False);
// contextNode = XophpObject_.False;
// } else {
// contextNode = iteratorNodeArray.Get_at(index);
// index++;
// }
// } else if (Type_.Eq_by_obj(iteratorNode, typeof(XomwPPNode_Hash_Array))) {
// XomwPPNode_Hash_Array iteratorNodeHashArray = (XomwPPNode_Hash_Array)iteratorNode;
// if (index >= iteratorNodeHashArray.getLength()) {
// // All done with this iterator
// iteratorStack.Set(level, XophpObject_.False);
// contextNode = XophpObject_.False;
// } else {
// contextNode = iteratorNodeHashArray.item(index);
// index++;
// }
// } else {
// // Copy to contextNode and then delete from iterator stack,
// // because this is not an iterator but we do have to execute it once
// contextNode = iteratorStack.Get_at(level);
// iteratorStack.Set(level, XophpObject_.False);
// }
//
// Object newIterator = XophpObject_.False;
// String contextName = XophpString_.False;
// XophpArray contextChildren = XophpArray.False;
//
// if (!XophpObject_.is_true(contextNode)) {
// // nothing to do
// } else if (XophpString_.is_string(contextNode)) {
// outItm += (String)contextNode;
// } else if (Type_.Eq_by_obj(contextNode, typeof(XomwPPNode_Hash_Array))) {
// newIterator = contextNode;
// } else if (Type_.Eq_by_obj(contextNode, typeof(XomwPPNode_Hash_Attr))) {
// // No output
// } else if (Type_.Eq_by_obj(contextNode, typeof(XomwPPNode_Hash_Text))) {
// outItm += ((XomwPPNode_Hash_Text)contextNode).value;
// } else if (Type_.Eq_by_obj(contextNode, typeof(XomwPPNode_Hash_Tree))) {
// XomwPPNode_Hash_Tree contextNodeHashTree = (XomwPPNode_Hash_Tree)contextNode;
// contextName = contextNodeHashTree.name;
// contextChildren = contextNodeHashTree.getRawChildren();
// } else if (XophpArray.is_array(contextNode)) {
// XophpArray contextNodeArray = (XophpArray)contextNode;
// // Node descriptor array
// if (contextNodeArray.count() != 2) {
// throw XomwMWException.New_by_method(typeof(XomwPPFrame_Hash), "expand",
// ": found an array where a node descriptor should be");
// }
// contextName = (String)contextNodeArray.Get_at(0);
// contextChildren = contextNodeArray.Get_at_ary(1);
// } else {
// throw XomwMWException.New_by_method(typeof(XomwPPFrame_Hash), "expand", ": Invalid parameter type");
// }
//
// // Handle node descriptor array or tree Object
// if (!XophpString_.is_true(contextName)) {
// // Not a node, already handled above
// } else if (String_.CharAt(contextName, 0) == '@') {
// // Attribute: no output
// } else if (String_.Eq(contextName, "template")) {
// // Double-brace expansion
// XophpArray bits = XomwPPNode_Hash_Tree.splitRawTemplate(contextChildren);
// if (Bitmask_.Has_int(flags, XomwPPFrame.NO_TEMPLATES)) {
// newIterator = this.virtualBracketedImplode(
// "{{", "|", "}}",
// bits.Get_by("title"),
// bits.Get_by("parts")
// );
// } else {
// XophpArray ret = this.parser.braceSubstitution(bits, this);
// if (ret.isset(Object_.Cls_val_name)) {// NOTE: using Cls_val_name b/c of transpilation and Object . Object
// newIterator = ret.Get_by(Object_.Cls_val_name);
// } else {
// outItm += ret.Get_by_str("text");
// }
// }
// } else if (String_.Eq(contextName, "tplarg")) {
// // Triple-brace expansion
// XophpArray bits = XomwPPNode_Hash_Tree.splitRawTemplate(contextChildren);
// if (Bitmask_.Has_int(flags, XomwPPFrame.NO_ARGS)) {
// newIterator = this.virtualBracketedImplode(
// "{{{", "|", "}}}",
// bits.Get_by("title"),
// bits.Get_by("parts")
// );
// } else {
// XophpArray ret = this.parser.argSubstitution(bits, this);
// if (ret.isset(Object_.Cls_val_name)) {// NOTE: using Cls_val_name b/c of transpilation and Object . Object
// newIterator = ret.Get_by("Object");
// } else {
// outItm += ret.Get_by_str("text");
// }
// }
// } else if (String_.Eq(contextName, "comment")) {
// // HTML-style comment
// // Remove it in HTML, pre+remove and STRIP_COMMENTS modes
// // Not in RECOVER_COMMENTS mode (msgnw) though.
// if ((this.parser.ot.Has("html"))
// || (this.parser.ot.Has("pre") && this.parser.mOptions.getRemoveComments())
// || (Bitmask_.Has_int(flags, XomwPPFrame.STRIP_COMMENTS))
// && !(Bitmask_.Has_int(flags, XomwPPFrame.RECOVER_COMMENTS))
// ) {
// outItm += ""; // XOWA: no purpose?
// } else if (this.parser.ot.Has("wiki") && !(Bitmask_.Has_int(flags, XomwPPFrame.RECOVER_COMMENTS))) {
// // Add a strip marker in PST mode so that pstPass2() can
// // run some old-fashioned regexes on the result.
// // Not in RECOVER_COMMENTS mode (extractSections) though.
// outItm += this.parser.insertStripItem(contextChildren.Get_at_str(0));
// } else {
// // Recover the literal comment in RECOVER_COMMENTS and pre+no-remove
// outItm += contextChildren.Get_at_str(0);
// }
// } else if (String_.Eq(contextName, "ignore")) {
// // Output suppression used by <includeonly> etc.
// // OT_WIKI will only respect <ignore> in substed templates.
// // The other output types respect it unless NO_IGNORE is set.
// // extractSections() sets NO_IGNORE and so never respects it.
//// if ((!XophpObject_.isset(this.parent) && this.parser.ot.Has("wiki")) // this.parent doesn't exist?
// if ((this.parser.ot.Has("wiki"))
// || (Bitmask_.Has_int(flags, XomwPPFrame.NO_IGNORE))
// ) {
// outItm += contextChildren.Get_at_str(0);
// } else {
// // outItm .= '';
// }
// } else if (String_.Eq(contextName, "ext")) {
// // Extension tag
// XophpArray bits = XomwPPNode_Hash_Tree.splitRawExt(contextChildren)
// .Add("attr", null).Add("inner", null).Add("close", null);
// if (Bitmask_.Has_int(flags, XomwPPFrame.NO_TAGS)) {
// String s = '<' + ((XomwPPNode_Hash_Text)((XomwPPNode)bits.Get_by("name")).getFirstChild()).value;
// if (bits.Has("attr")) {
// s += ((XomwPPNode_Hash_Text)((XomwPPNode)bits.Get_by("attr")).getFirstChild()).value;
// }
// if (bits.Has("inner")) {
// s += '>' + ((XomwPPNode_Hash_Text)((XomwPPNode)bits.Get_by("inner")).getFirstChild()).value;
// if (bits.Has("close")) {
// s += ((XomwPPNode_Hash_Text)((XomwPPNode)bits.Get_by("close")).getFirstChild()).value;
// }
// } else {
// s += "/>";
// }
// outItm += s;
// } else {
// outItm += this.parser.extensionSubstitution(bits, this);
// }
// } else if (String_.Eq(contextName, "h")) {
// // Heading
// if (this.parser.ot.Has("html")) {
// // Expand immediately and insert heading index marker
// String s = this.expand(contextChildren, flags);
// XophpArray bits = XomwPPNode_Hash_Tree.splitRawHeading(contextChildren);
// String titleText = this.title.getPrefixedDBkeyStr();
// this.parser.mHeadings.Add(titleText, bits.Get_by("i"));
// int serial = XophpArray_.count(this.parser.mHeadings) - 1;
// String marker = XomwParser.MARKER_PREFIX + "-h-" + Int_.To_str(serial) + "-" + XomwParser.MARKER_SUFFIX;
// s = XophpString_.substr(s, 0, bits.Get_by_int("level")) + marker + XophpString_.substr(s, bits.Get_by_int("level"));
// this.parser.mStripState.addGeneral(marker, "");
// outItm += s;
// } else {
// // Expand in virtual stack
// newIterator = contextChildren;
// }
// } else {
// // Generic recursive expansion
// newIterator = contextChildren;
// }
//
// if (XophpObject_.is_true(newIterator)) {
// outStack.Add("");
// iteratorStack.Add(newIterator);
// indexStack.Add(0);
// } else if (!XophpObject_.is_true(iteratorStack.Get_at(level))) {
// // Return accumulated value to parent
// // With tail recursion
// while (!XophpObject_.is_true(iteratorStack.Get_at(level)) && level > 0) {
// outStack.Itm_str_concat_end(level - 1, outItm);
// outStack.pop();
// iteratorStack.pop();
// indexStack.pop();
// level--;
// }
// }
// }
// --expansionDepth;
// return outStack.Get_at_str(0);
// }
//
// /**
// * @param String $sep
// * @param int $flags
// * @param String|PPNode $args,...
// * @return String
// */
// public String implodeWithFlags(String sep, int flags, XophpArray args) {
// // args = XophpArray_.array_slice(func_get_args(), 2);
//
// boolean first = true;
// String s = "";
// int len = args.Len();
// for (int i = 0; i < len; i++) {
// Object root_obj = args.Get_at(i);
// XophpArray root = null;
// if (XophpType_.instance_of(root_obj, typeof(XomwPPNode_Hash_Array))) {
// root = (XophpArray)((XomwPPNode_Hash_Array)root_obj).value;
// }
// if (!XophpType_.is_array(root_obj)) {
// root = XophpArray.New(root_obj);
// }
// int root_len = root.Len();
// for (int j = 0; j < root_len; j++) {
// XomwPPNode node = (XomwPPNode)root.Get_at(j);
// if (first) {
// first = false;
// } else {
// s += sep;
// }
// s += this.expand(node, flags);
// }
// }
// return s;
// }
//
// /**
// * Implode with no flags specified
// * This previously called implodeWithFlags but has now been inlined to reduce stack depth
// * @param String $sep
// * @param String|PPNode $args,...
// * @return String
// */
// public override String implode(String sep, params Object[] args) {
// boolean first = true;
// String s = "";
// foreach (Object rootObj in args) {
// XophpArray root = null;
// if (Type_.Eq_by_obj(root, typeof(XomwPPNode_Hash_Array))) {
// root = ((XomwPPNode_Hash_Array)rootObj).value;
// }
// if (!XophpArray.is_array(rootObj)) {
// root = XophpArray.New(root);
// }
// int rootLen = root.count();
// for (int i = 0; i < rootLen; i++) {
// Object node = root.Get_at(i);
// if (first) {
// first = false;
// } else {
// s += sep;
// }
// s += this.expand(node, expand_flags_default);
// }
// }
// return s;
// }
//
// /**
// * Makes an Object that, when expand()ed, will be the same as one obtained
// * with implode()
// *
// * @param String $sep
// * @param String|PPNode $args,...
// * @return PPNode_Hash_Array
// */
// public override XomwPPNode virtualImplode(String sep, params Object[] args) {
// XophpArray outItm = XophpArray.New();
// boolean first = true;
//
// foreach (Object rootObj in args) {
// XophpArray root = null;
// if (Type_.Eq_by_obj(root, typeof(XomwPPNode_Hash_Array))) {
// root = ((XomwPPNode_Hash_Array)rootObj).value;
// }
// if (!XophpArray.is_array(rootObj)) {
// root = XophpArray.New(root);
// }
// int rootLen = root.count();
// for (int i = 0; i < rootLen; i++) {
// Object node = root.Get_at(i);
// if (first) {
// first = false;
// } else {
// outItm.Add(sep);
// }
// outItm.Add(node);
// }
// }
// return new XomwPPNode_Hash_Array(outItm);
// }
//
// /**
// * Virtual implode with brackets
// *
// * @param String $start
// * @param String $sep
// * @param String $end
// * @param String|PPNode $args,...
// * @return PPNode_Hash_Array
// */
// public override XomwPPNode virtualBracketedImplode(String start, String sep, String end, params Object[] args) {
// XophpArray outItm = XophpArray.New(start);
// boolean first = true;
//
// foreach (Object rootObj in args) {
// XophpArray root = null;
// if (Type_.Eq_by_obj(rootObj, typeof(XomwPPNode_Hash_Array))) {
// root = ((XomwPPNode_Hash_Array)rootObj).value;
// }
// if (!XophpArray.is_array(rootObj)) {
// root = XophpArray.New((String)rootObj);
// }
// int root_len = root.count();
// for (int i = 0; i < root_len; i++) {
// String node = root.Get_at_str(i);
// if (first) {
// first = false;
// } else {
// outItm.Add(sep);
// }
// outItm.Add(node);
// }
// }
// outItm.Add(end);
// return new XomwPPNode_Hash_Array(outItm);
// }
//
// public override String toString() {
// return "frame{}";
// }
//
// /**
// * @param boolean $level
// * @return array|boolean|String
// */
// public String getPDBK(boolean level) { // DEFAULT:false
// if (level == false) {
// 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_.False;
// }
// }
//
// /**
// * @return array
// */
// public override XophpArray getArguments() {
// return XophpArray.False;
// }
//
// /**
// * @return array
// */
// public override XophpArray getNumberedArguments() {
// return XophpArray.False;
// }
//
// /**
// * @return array
// */
// public override XophpArray getNamedArguments() {
// return XophpArray.False;
// }
//
// /**
// * Returns true if there are no arguments in this frame
// *
// * @return boolean
// */
// public override boolean isEmpty() {
// return true;
// }
//
// /**
// * @param int|String $name
// * @return boolean Always false in this implementation.
// */
// public override String getArgument(String name) {
// return XophpString_.False;
// }
//
// /**
// * Returns true if the infinite loop check is OK, false if a loop is detected
// *
// * @param Title $title
// *
// * @return boolean
// */
// public override boolean loopCheck(XomwTitle title) {
// return !this.loopCheckHash.isset(title.getPrefixedDBkeyStr());
// }
//
// /**
// * Return true if the frame is a template frame
// *
// * @return boolean
// */
// public override boolean isTemplate() {
// return false;
// }
//
// /**
// * Get a title of frame
// *
// * @return Title
// */
// public override XomwTitle getTitle() {
// return this.title;
// }
//
// /**
// * Set the volatile_bool flag
// *
// * @param boolean $flag
// */
// public override void setVolatile(boolean flag) { // DEFAULT: flag = true
// this.volatile_bool = flag;
// }
//
// /**
// * Get the volatile_bool flag
// *
// * @return boolean
// */
// public override boolean isVolatile() {
// return this.volatile_bool;
// }
//
// /**
// * Set the TTL
// *
// * @param int ttl
// */
// public override void setTTL(int val) {
// if (this.ttl == Int_.Null || val < this.ttl) {
// this.ttl = val;
// }
// }
//
// /**
// * Get the TTL
// *
// * @return int|null
// */
// public override int getTTL() {
// return this.ttl;
// }
}

@ -0,0 +1,110 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.FILE:Preprocessor
/**
* There are three types of nodes:
* * Tree nodes, which have a name and contain other nodes as children
* * Array nodes, which also contain other nodes but aren't considered part of a tree
* * Leaf nodes, which contain the actual data
*
* This interface provides access to the tree structure and to the contents of array nodes,
* but it does not provide access to the @gplx.Internal protected structure of leaf nodes. Access to leaf
* data is provided via two means:
* * PPFrame::expand(), which provides expanded text
* * The PPNode::split*() functions, which provide metadata about certain types of tree node
* @ingroup Parser
*/
public abstract class XomwPPNode {
/**
* Get an array-type node containing the children of this node.
* Returns false if this is not a tree node.
* @return PPNode
*/
public abstract XomwPPNode_Hash_Array getChildren();
/**
* Get the first child of a tree node. False if there isn't one.
*
* @return PPNode
*/
public abstract XomwPPNode getFirstChild();
/**
* Get the next sibling of any node. False if there isn't one
* @return PPNode
*/
public abstract XomwPPNode getNextSibling();
/**
* Get all children of this tree node which have a given name.
* Returns an array-type node, or false if this is not a tree node.
* @param String $type
* @return boolean|PPNode
*/
public abstract XomwPPNode_Hash_Array getChildrenOfType(String type);
/**
* Returns the length of the array, or false if this is not an array-type node
*/
public abstract int getLength();
/**
* Returns an item of an array-type node
* @param int $i
* @return boolean|PPNode
*/
public abstract XomwPPNode item(int i);
/**
* Get the name of this node. The following names are defined here:
*
* h A heading node.
* template A double-brace node.
* tplarg A triple-brace node.
* title The first argument to a template or tplarg node.
* part Subsequent arguments to a template or tplarg node.
* #nodelist An array-type node
*
* The subclass may define various other names for tree and leaf nodes.
* @return String
*/
public abstract String getName();
/**
* Split a "<part>" node into an associative array containing:
* name PPNode name
* index String index
* value PPNode value
* @return array
*/
public abstract XophpArray splitArg();
/**
* Split an "<ext>" node into an associative array containing name, attr, inner and close
* All values in the resulting array are PPNodes. Inner and close are optional.
* @return array
*/
public abstract XophpArray splitExt();
/**
* Split an "<h>" node
* @return array
*/
public abstract XophpArray splitHeading();
public abstract String toString();
}

@ -0,0 +1,72 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
import gplx.xowa.mediawiki.includes.exception.*;
// MW.FILE:Preprocessor_Hash
/**
* @ingroup Parser
*/
public class XomwPPNode_Hash_Array extends XomwPPNode { public XophpArray value;
public XomwPPNode_Hash_Array(XophpArray value) {
this.value = value;
}
@Override public String toString() {
// return var_export($this, true);
return value.To_str();
}
@Override public int getLength() {
return XophpArray_.count(this.value);
}
@Override public XomwPPNode item(int i) {
return (XomwPPNode)this.value.Get_at(i);
}
@Override public String getName() {
return "#nodelist";
}
@Override public XomwPPNode getNextSibling() {
return null;
}
@Override public XomwPPNode_Hash_Array getChildren() {
return null;
}
@Override public XomwPPNode getFirstChild() {
return null;
}
@Override public XomwPPNode_Hash_Array getChildrenOfType(String name) {
return null;
}
@Override public XophpArray splitArg() {
throw XomwMWException.New_by_method_obj(this, "splitArg", ": not supported");
}
@Override public XophpArray splitExt() {
throw XomwMWException.New_by_method_obj(this, "splitExt", ": not supported");
}
@Override public XophpArray splitHeading() {
throw XomwMWException.New_by_method_obj(this, "splitHeading", ": not supported");
}
}

@ -0,0 +1,90 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
import gplx.xowa.mediawiki.includes.exception.*;
// MW.FILE:Preprocessor_Hash
/**
* @ingroup Parser
*/
public class XomwPPNode_Hash_Attr extends XomwPPNode { public String name, value;
private final XophpArray store;
private final int index;
/**
* Construct an Object using the data from $store[$index]. The rest of the
* store array can be accessed via getNextSibling().
*
* @param array $store
* @param integer $index
*/
public XomwPPNode_Hash_Attr(XophpArray store, int index) {
XophpArray descriptor = (XophpArray)store.Get_at(index);
String descriptor_name = descriptor.Get_at_str(XomwPPNode_Hash_Tree.NAME);
if (!(String_.CharAt(descriptor_name, 0) == '@')) {
throw Err_.new_wo_type("XomwPPNode_Hash_Attr.CTOR: invalid name in attribute descriptor");
}
this.name = String_.new_u8(XophpString_.substr(Bry_.new_u8(descriptor_name), 1));
XophpArray descriptor_children = (XophpArray)descriptor.Get_at(XomwPPNode_Hash_Tree.CHILDREN);
Object value_obj = descriptor_children.Get_at(0);
this.value = Type_.Eq_by_obj(value_obj, byte[].class) ? String_.new_u8((byte[])value_obj): value_obj.toString();
this.store = store;
this.index = index;
}
@Override public String toString() {
return String_.Format("<@{0}>{1}</@{0}>", this.name, Bry_.Escape_html(Bry_.new_u8(this.value)));
}
@Override public String getName() {
return this.name;
}
@Override public XomwPPNode getNextSibling() {
return XomwPPNode_Hash_Tree.factory(this.store, this.index + 1);
}
@Override public XomwPPNode_Hash_Array getChildren() {
return null;
}
@Override public XomwPPNode getFirstChild() {
return null;
}
@Override public XomwPPNode_Hash_Array getChildrenOfType(String name) {
return null;
}
@Override public int getLength() {
return XophpInt_.False;
}
@Override public XomwPPNode item(int i) {
return null;
}
@Override public XophpArray splitArg() {
throw XomwMWException.New_by_method_obj(this, "splitArg", ": not supported");
}
@Override public XophpArray splitExt() {
throw XomwMWException.New_by_method_obj(this, "splitExt", ": not supported");
}
@Override public XophpArray splitHeading() {
throw XomwMWException.New_by_method_obj(this, "splitHeading", ": not supported");
}
}

@ -0,0 +1,86 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.FILE:Preprocessor_Hash
/**
* @ingroup Parser
*/
import gplx.xowa.mediawiki.includes.exception.*;
public class XomwPPNode_Hash_Text extends XomwPPNode { public String value;
private final XophpArray store;
private final int index;
/**
* Construct an Object using the data from $store[$index]. The rest of the
* store array can be accessed via getNextSibling().
*
* @param array $store
* @param integer $index
*/
public XomwPPNode_Hash_Text(XophpArray store, int index) {
Object value_obj = store.Get_at(index);
if (!XophpType_.is_scalar(value_obj)) {
throw XomwMWException.New_by_method(XomwPPNode_Hash_Text.class, "CTOR", "given Object instead of String");
}
this.value = Object_.Xto_str_strict_or_null(value_obj);
this.store = store;
this.index = index;
}
@Override public String toString() {
return String_.new_u8(Bry_.Escape_html(Bry_.new_u8(this.value)));
}
@Override public XomwPPNode getNextSibling() {
return XomwPPNode_Hash_Tree.factory(this.store, this.index + 1);
}
@Override public XomwPPNode_Hash_Array getChildren() {
return null;
}
@Override public XomwPPNode getFirstChild() {
return null;
}
@Override public XomwPPNode_Hash_Array getChildrenOfType(String name) {
return null;
}
@Override public int getLength() {
return XophpInt_.False;
}
@Override public XomwPPNode item(int i) {
return null;
}
@Override public String getName() {
return "#text";
}
@Override public XophpArray splitArg() {
throw XomwMWException.New_by_method_obj(this, "splitArg", ": not supported");
}
@Override public XophpArray splitExt() {
throw XomwMWException.New_by_method_obj(this, "splitExt", ": not supported");
}
@Override public XophpArray splitHeading() {
throw XomwMWException.New_by_method_obj(this, "splitHeading", ": not supported");
}
}

@ -0,0 +1,392 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.FILE:Preprocessor_Hash
/**
* @ingroup Parser
*/
import gplx.xowa.mediawiki.includes.exception.*;
public class XomwPPNode_Hash_Tree extends XomwPPNode { public final String name;
/**
* The store array for children of this node. It is "raw" in the sense that
* nodes are two-element arrays ("descriptors") rather than PPNode_Hash_*
* objects.
*/
private XophpArray rawChildren;
/**
* The store array for the siblings of this node, including this node itself.
*/
private final XophpArray store;
/**
* The index into this.store which contains the descriptor of this node.
*/
private final int index;
/**
* The offset of the name within descriptors, used in some places for
* readability.
*/
public static final int NAME = 0;
/**
* The offset of the child list within descriptors, used in some places for
* readability.
*/
public static final int CHILDREN = 1;
/**
* Construct an Object using the data from store[index]. The rest of the
* store array can be accessed via getNextSibling().
*
* @param array store
* @param integer index
*/
public XomwPPNode_Hash_Tree(XophpArray store, int index) {
this.store = store;
this.index = index;
XophpArray list = this.store.Get_at_ary(index);
this.name = list.Get_at_str(0);
Object rawChildrenObj = list.Get_at(1);
if (XophpType_.To_type_id(rawChildrenObj) == Type_ids_.Id__array) {
this.rawChildren = (XophpArray)rawChildrenObj;
}
else {
// this.rawChildren = ((Xomw_prepro_accum__hash)rawChildrenObj).Ary();
}
}
/**
* Construct an appropriate PPNode_Hash_* Object with a class that depends
* on what is at the relevant store index.
*
* @param array store
* @param integer index
* @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text
*/
public static XomwPPNode factory(XophpArray store, int index) {
Object descriptor = store.Get_at(index);
if (!XophpObject_.isset_obj(descriptor)) {
return null;
}
XophpType type = XophpType.New(descriptor);
if (type.is_string()) {
return new XomwPPNode_Hash_Text(store, index);
}
else if (type.is_array()) {
XophpArray descriptor_array = (XophpArray)descriptor;
String name = (String)(descriptor_array.Get_by(NAME));
if (String_.CharAt(name, 0) == '@') {
return new XomwPPNode_Hash_Attr(store, index);
}
else {
return new XomwPPNode_Hash_Tree(store, index);
}
}
else {
throw XomwMWException.New_by_method(XomwPPNode_Hash_Tree.class, "factory", "invalid node descriptor");
}
}
/**
* Convert a node to XML, for debugging
*/
@Override public String toString() {
String inner = "";
String attribs = "";
for (XomwPPNode node = this.getFirstChild(); node != null; node = node.getNextSibling()) {
if (Type_.Eq_by_obj(node, XomwPPNode_Hash_Attr.class)) {
XomwPPNode_Hash_Attr node_attr = (XomwPPNode_Hash_Attr)node;
attribs += " " + node_attr.name + "=\"" + String_.new_u8(Bry_.Escape_html(Bry_.new_u8(node_attr.value))) + "\"";
} else {
inner += node.toString();
}
}
if (String_.Eq(inner, "") && String_.Eq(name, "name")) {
return "<" + this.name + attribs + " />";
} else {
if (String_.Eq(name, "equals")) {
return inner;
}
else {
return "<" + this.name + attribs + ">" + inner + "</" + this.name + ">";
}
}
}
/**
* @return PPNode_Hash_Array
*/
@Override public XomwPPNode_Hash_Array getChildren() {
XophpArray children = XophpArray.New();
int rawChildrenLen = rawChildren.Len();
for (int i = 0; i < rawChildrenLen; i++) {
XophpArrayItm itm = rawChildren.Get_at_itm(i);
children.Add(XomwPPNode_Hash_Tree.factory(this.rawChildren, itm.Key_as_int()));
}
return new XomwPPNode_Hash_Array(children);
}
/**
* Get the first child, or false if there is none. Note that this will
* return a temporary proxy Object: different instances will be returned
* if this is called more than once on the same node.
*
* @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|boolean
*/
@Override public XomwPPNode getFirstChild() {
if (!XophpArray_.isset(this.rawChildren, 0)) {
return null;
}
else {
return factory(this.rawChildren, 0);
}
}
/**
* Get the next sibling, or false if there is none. Note that this will
* return a temporary proxy Object: different instances will be returned
* if this is called more than once on the same node.
*
* @return PPNode_Hash_Tree|PPNode_Hash_Attr|PPNode_Hash_Text|boolean
*/
@Override public XomwPPNode getNextSibling() {
return factory(this.store, this.index + 1);
}
/**
* Get an array of the children with a given node name
*
* @param String name
* @return PPNode_Hash_Array
*/
@Override public XomwPPNode_Hash_Array getChildrenOfType(String name) {
XophpArray children = XophpArray.New();
int rawChildren_len = this.rawChildren.Len();
for (int idx = 0; idx < rawChildren_len; idx++) {
XophpArrayItm itm = this.rawChildren.Get_at_itm(idx);
Object child = itm.Val();
if (XophpType_.is_array(child) && String_.Eq(((XophpArray)child).Get_at_str(XomwPPNode_Hash_Tree.NAME), name)) {
children.Add(XomwPPNode_Hash_Tree.factory(this.rawChildren, itm.Key_as_int()));
}
}
return new XomwPPNode_Hash_Array(children);
}
/**
* Get the raw child array. For @gplx.Internal protected use.
* @return array
*/
public XophpArray getRawChildren() {
return this.rawChildren;
}
/**
* @return boolean
*/
@Override public int getLength() {
return XophpInt_.False;
}
/**
* @param int i
* @return boolean
*/
@Override public XomwPPNode item(int i) {
return null;
}
/**
* @return String
*/
@Override public String getName() {
return this.name;
}
/**
* Split a "<part>" node into an associative array containing:
* - name PPNode name
* - index String index
* - value PPNode value
*
* @throws MWException
* @return array
*/
@Override public XophpArray splitArg() {
return XomwPPNode_Hash_Tree.splitRawArg(this.rawChildren);
}
/**
* Like splitArg() but for a raw child array. For @gplx.Internal protected use only.
*/
public static XophpArray splitRawArg(XophpArray children) {
XophpArray bits = XophpArray.New();
int childrenLen = children.Len();
for (int j = 0; j < childrenLen; j++) {
XophpArrayItm itm = children.Get_at_itm(j);
Object childObj = itm.Val();
if (!XophpType_.is_array(childObj)) {
continue;
}
XophpArray child = (XophpArray)childObj;
int i = itm.Key_as_int();
if (String_.Eq(child.Get_at_str(XomwPPNode_Hash_Tree.NAME), "name")) {
bits.Set("name", new XomwPPNode_Hash_Tree(children, i));
if (XophpObject_.isset_obj(child.Get_at_ary(XomwPPNode_Hash_Tree.CHILDREN).Get_at_ary(0).Get_at(XomwPPNode_Hash_Tree.NAME))
&& String_.Eq(child.Get_at_ary(XomwPPNode_Hash_Tree.CHILDREN).Get_at_ary(0).Get_at_str(XomwPPNode_Hash_Tree.NAME), "@index")
) {
bits.Set("index", child.Get_at_ary(XomwPPNode_Hash_Tree.CHILDREN).Get_at_ary(0).Get_at_ary(XomwPPNode_Hash_Tree.CHILDREN).Get_at(0));
}
}
else if (String_.Eq(child.Get_at_str(XomwPPNode_Hash_Tree.NAME), "value")) {
bits.Set("value", new XomwPPNode_Hash_Tree(children, i));
}
}
if (!XophpObject_.isset_obj(bits.Get_by("name"))) {
throw XomwMWException.New_by_method(XomwPPNode_Hash_Tree.class, "splitRawArg", "Invalid brace node passed to " + "splitRawArg");
}
if (!XophpObject_.isset_obj(bits.Get_by("index"))) {
bits.Set("index", "");
}
return bits;
}
/**
* Split an "<ext>" node into an associative array containing name, attr, inner and close
* All values in the resulting array are PPNodes. Inner and close are optional.
*
* @throws MWException
* @return array
*/
@Override public XophpArray splitExt() {
return XomwPPNode_Hash_Tree.splitRawExt(this.rawChildren);
}
/**
* Like splitExt() but for a raw child array. For @gplx.Internal protected use only.
*/
public static XophpArray splitRawExt(XophpArray children) {
XophpArray bits = XophpArray.New();
int len = children.count();
for (int i = 0; i < len; i++) {
Object childObj = children.Get_at(i);
if (!XophpArray.is_array(childObj)) {
continue;
}
XophpArray child = (XophpArray)childObj;
String childName = child.Get_at_str(XomwPPNode_Hash_Tree.NAME);
if (String_.Eq(childName, "name")) {
bits.Add("name", new XomwPPNode_Hash_Tree(children, i));
}
else if (String_.Eq(childName, "attr")) {
bits.Add("attr", new XomwPPNode_Hash_Tree(children, i));
}
else if (String_.Eq(childName, "inner")) {
bits.Add("inner", new XomwPPNode_Hash_Tree(children, i));
}
else if (String_.Eq(childName, "close")) {
bits.Add("close", new XomwPPNode_Hash_Tree(children, i));
}
}
if (!bits.isset("name")) {
throw new XomwMWException("Invalid ext node passed to " + "splitRawExt");
}
return bits;
}
/**
* Split an "<h>" node
*
* @throws MWException
* @return array
*/
@Override public XophpArray splitHeading() {
if (!String_.Eq(this.name, "h")) {
throw new XomwMWException("Invalid h node passed to " + "splitHeading");
}
return XomwPPNode_Hash_Tree.splitRawHeading(this.rawChildren);
}
/**
* Like splitHeading() but for a raw child array. For @gplx.Internal protected use only.
*/
public static XophpArray splitRawHeading(XophpArray children) {
XophpArray bits = XophpArray.New();
int len = children.count();
for (int i = 0; i < len; i++) {
Object childObj = children.Get_at(i);
if (!XophpArray.is_array(childObj)) {
continue;
}
XophpArray child = (XophpArray)childObj;
String childName = child.Get_at_str(XomwPPNode_Hash_Tree.NAME);
XophpArray childChildren = child.Get_at_ary(XomwPPNode_Hash_Tree.CHILDREN);
if (String_.Eq(childName, "@i")) {
bits.Add("i", childChildren.Get_at(0));
} else if (String_.Eq(childName, "@level")) {
bits.Add("level", childChildren.Get_at(0));
}
}
if (!bits.isset("i")) {
throw new XomwMWException("Invalid h node passed to " + "splitRawHeading");
}
return bits;
}
/**
* Split a "<template>" or "<tplarg>" node
*
* @throws MWException
* @return array
*/
public XophpArray splitTemplate() {
return XomwPPNode_Hash_Tree.splitRawTemplate(this.rawChildren);
}
/**
* Like splitTemplate() but for a raw child array. For @gplx.Internal protected use only.
*/
public static XophpArray splitRawTemplate(XophpArray children) {
XophpArray parts = XophpArray.New();
XophpArray bits = XophpArray.New("lineStart" , "");
int len = children.count();
for (int i = 0; i < len; i++) {
Object childObj = children.Get_at(i);
if (!XophpArray.is_array(childObj)) {
continue;
}
XophpArray child = (XophpArray)childObj;
String childName = child.Get_at_str(XomwPPNode_Hash_Tree.NAME);
XophpArray childChildren = child.Get_at_ary(XomwPPNode_Hash_Tree.CHILDREN);
if (String_.Eq(childName, "title")) {
bits.Add("title", new XomwPPNode_Hash_Tree(childChildren, i));
} else if (String_.Eq(childName, "part")) {
parts.Add(new XomwPPNode_Hash_Tree(childChildren, i));
} else if (String_.Eq(childName, "@lineStart")) {
bits.Add("lineStart", "1");
}
}
if (!bits.isset("title")) {
throw new XomwMWException("Invalid node passed to " + "splitRawTemplate");
}
bits.Add("parts", new XomwPPNode_Hash_Array(parts));
return bits;
}
}

@ -0,0 +1,206 @@
/*
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
*/
//namespace gplx.xowa.mediawiki.includes.parsers.preprocessors_new {
// ///**
// // * Expansion frame with template arguments
// // * @ingroup Parser
// // */
// class XomwPPTemplateFrame_Hash : XomwPPFrame_Hash { // public XophpArray numberedArgs, namedArgs;
// @gplx.New 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();
// }
//
// public override 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
// */
// public override 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
// */
// public override boolean isEmpty() {
// return !this.numberedArgs.count_bool() && !this.namedArgs.count_bool();
// }
//
// /**
// * @return array
// */
// public override 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
// */
// public override 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
// */
// public override 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
// */
// public override boolean isTemplate() {
// return true;
// }
//
// public override void setVolatile(boolean flag) { // flag = true
// super.setVolatile(flag);
// this.parent.setVolatile(flag);
// }
//
// public override void setTTL(int ttl) {
// super.setTTL(ttl);
// this.parent.setTTL(ttl);
// }
// }
//}

@ -0,0 +1,162 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
import gplx.core.btries.*;
import gplx.xowa.mediawiki.includes.parsers.preprocessors.*;
/**
* @ingroup Parser
*/
public abstract class XomwPreprocessor {
public static final int CACHE_VERSION = 1;
/**
* @var array Brace matching rules.
*/
protected XophpArray rules = XophpArray.New()
.Add("{", XophpArray.New()
.Add("end", "}")
.Add("names", XophpArray.New()
.Add(2, "template")
.Add(3, "tplarg")
)
.Add("min", 2)
.Add("max", 3)
)
.Add("[", XophpArray.New()
.Add("end", "]")
.Add("names", XophpArray.New()
.Add(2, null)
)
.Add("min", 2)
.Add("max", 2)
)
.Add("-{", XophpArray.New()
.Add("end", "}-")
.Add("names", XophpArray.New()
.Add(2, null)
)
.Add("min", 2)
.Add("max", 2)
)
;
/**
* Store a document tree in the cache.
*
* @param String $text
* @param int $flags
* @param String $tree
*/
protected void cacheSetTree(String text, int flags, String tree) {
// config = RequestContext::getMain()->getConfig();
//
// length = strlen(text);
// threshold = config->get("PreprocessorCacheThreshold");
// if (threshold === false || length < threshold || length > 1e6) {
// return;
// }
//
// cache = ObjectCache::getLocalClusterInstance();
// key = cache->makeKey(
// defined("static::CACHE_PREFIX") ? static::CACHE_PREFIX : static::class,
// md5(text), flags);
// value = sprintf("%08d", static::CACHE_VERSION) . tree;
//
// cache->set(key, value, 86400);
//
// LoggerFactory::getInstance("Preprocessor")
// ->info("Cached preprocessor output (key: key)");
}
/**
* Attempt to load a precomputed document tree for some given wikitext
* from the cache.
*
* @param String $text
* @param int $flags
* @return PPNode_Hash_Tree|boolean
*/
protected XomwPPNode_Hash_Tree cacheGetTree(String text, int flags) {
// $config = RequestContext::getMain()->getConfig();
//
// $length = strlen($text);
// $threshold = $config->get('PreprocessorCacheThreshold');
// if ($threshold === false || $length < $threshold || $length > 1e6) {
// return false;
// }
//
// $cache = ObjectCache::getLocalClusterInstance();
//
// $key = $cache->makeKey(
// defined('static::CACHE_PREFIX') ? static::CACHE_PREFIX : static::class,
// md5($text), $flags);
//
// $value = $cache->get($key);
// if (!$value) {
// return false;
// }
//
// $version = intval(substr($value, 0, 8));
// if ($version !== static::CACHE_VERSION) {
// return false;
// }
//
// LoggerFactory::getInstance('Preprocessor')
// ->info("Loaded preprocessor output from cache (key: $key)");
//
// return substr($value, 8);
return null;
}
/**
* Create a new top-level frame for expansion of a page
*
* @return PPFrame
*/
@gplx.Virtual public XomwPPFrame newFrame() {return null;}
/**
* Create a new custom frame for programmatic use of parameter replacement
* as used in some extensions.
*
* @param array $args
*
* @return PPFrame
*/
@gplx.Virtual public XomwPPFrame newCustomFrame(XophpArray args) {return null;}
/**
* Create a new custom node for programmatic use of parameter replacement
* as used in some extensions.
*
* @param array $values
*/
@gplx.Virtual public XomwPPNode newPartNodeArray(XophpArray values) {return null;}
/**
* Preprocess text to a PPNode
*
* @param String $text
* @param int $flags
*
* @return PPNode
*/
@gplx.Virtual public XomwPPNode preprocessToObj(String text, int flags) {return null;} // flags = 0
@gplx.Virtual public String preprocessToDbg(String src, boolean for_inclusion) {return null;}
}

@ -0,0 +1,828 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
// MW.SRC:1.33
import gplx.core.bits.*;
import gplx.langs.regxs.*;
import gplx.xowa.mediawiki.includes.exception.*;
import gplx.xowa.mediawiki.includes.parsers.preprocessors.*;
/**
* Differences from DOM schema:
* * attribute nodes are children
* * "<h>" nodes that aren"t at the top are replaced with <possible-h>
*
* Nodes are stored in a recursive array data structure. A node store is an
* array where each element may be either a scalar (representing a text node)
* or a "descriptor", which is a two-element array where the first element is
* the node name and the second element is the node store for the children.
*
* Attributes are represented as children that have a node name starting with
* "@", and a single text node child.
*
* @todo: Consider replacing descriptor arrays with objects of a new cl+ass.
* Benchmark and measure resulting memory impact.
*
* @ingroup Parser
*/
public class XomwPreprocessor_Hash extends XomwPreprocessor {
/**
* @var Parser
*/
public XomwParser parser;
public static final String CACHE_PREFIX = "preprocess-hash";
@gplx.New public static final int CACHE_VERSION = 2;
public XomwPreprocessor_Hash(XomwParser parser) {
this.parser = parser;
}
/**
* @return PPFrame_Hash
*/
@Override public XomwPPFrame newFrame() {
// return new XomwPPFrame_Hash(this);
return null;
}
/**
* @param array $args
* @return PPCustomFrame_Hash
*/
// public XomwPPCustomFrame_Hash newCustomFrame(XophpArray args) {
// return new XomwPPCustomFrame_Hash(this, args);
// }
/**
* @param array $values
* @return PPNode_Hash_Array
*/
@Override public XomwPPNode newPartNodeArray(XophpArray values) {
XophpArray list = XophpArray.New();
// foreach ($values as $k => $val) {
// if (is_int($k)) {
// $store = [ [ "part", [
// [ "name", [ [ "@index", [ $k ] ] ] ],
// [ "value", [ strval($val) ] ],
// ] ] ];
// } else {
// $store = [ [ "part", [
// [ "name", [ strval($k) ] ],
// "=",
// [ "value", [ strval($val) ] ],
// ] ] ];
// }
//
// $list[] = new PPNode_Hash_Tree($store, 0);
// }
XomwPPNode_Hash_Array node = new XomwPPNode_Hash_Array(list);
return node;
}
private static final Hash_adp strspn_hash__eq = XophpString_.strspn_hash("=");
private static final Hash_adp strspn_hash__ws_tab = XophpString_.strspn_hash(" \t");
/**
* Preprocess some wikitext and return the document tree.
*
* @param String $text The text to parse
* @param int $flags Bitwise combination of:
* Parser::PTD_FOR_INCLUSION Handle "<noinclude>" and "<includeonly>" as if the text is being
* included. Default is to assume a direct page view.
*
* The generated DOM tree must depend only on the input text and the flags.
* The DOM tree must be the same in OT_HTML and OT_WIKI mode, to avoid a regression of T6899.
*
* Any flag added to the $flags parameter here, or any other parameter liable to cause a
* change in the DOM tree for a given text, must be passed through the section identifier
* in the section edit link and thus back to extractSections().
*
* @throws MWException
* @return PPNode_Hash_Tree
*/
@Override public XomwPPNode preprocessToObj(String text, int flags) { // flags = 0;
// global wgDisableLangConversion;
// tree = this.cacheGetTree(text, flags);
// if (tree !== false) {
// store = json_decode(tree);
// if (is_array(store)) {
// return new PPNode_Hash_Tree(store, 0);
// }
// }
int forInclusion = flags & XomwParser.PTD_FOR_INCLUSION;
XophpArray xmlishElements = this.parser.getStripList();
XophpArray xmlishAllowMissingEndTag = XophpArray.New("includeonly", "noinclude", "onlyinclude");
boolean enableOnlyinclude = false;
XophpArray ignoredTags, ignoredElements;
if (XophpInt_.is_true(forInclusion)) {
ignoredTags = XophpArray.New("includeonly", "/includeonly");
ignoredElements = XophpArray.New("noinclude");
xmlishElements.Add("noinclude");
if (XophpString_.strpos(text, "<onlyinclude>") != -1
&& XophpString_.strpos(text, "</onlyinclude>") != -1
) {
enableOnlyinclude = true;
}
} else {
ignoredTags = XophpArray.New("noinclude", "/noinclude", "onlyinclude", "/onlyinclude");
ignoredElements = XophpArray.New("includeonly");
xmlishElements.Add("includeonly");
}
String xmlishRegex = XophpArray_.implode("|", XophpArray_.array_merge(xmlishElements, ignoredTags));
// Use "A" modifier (anchored) instead of "^", because ^ doesn"t work with an offset
Regx_adp elementsRegex = XophpRegex_.Pattern("(" + xmlishRegex + ")(?:\\s|\\/>|>)|(!--)", XophpRegex_.MODIFIER_i | XophpRegex_.MODIFIER_A);
XomwPPDStack_Hash stack = new XomwPPDStack_Hash(XomwPPDPart_Hash.Instance);
String searchBase = "[{<\n";
if (!XomwDefaultSettings.wgDisableLangConversion) {
searchBase += "-";
}
// For fast reverse searches
String revText = XophpString_.strrev(text);
int lengthText = XophpString_.strlen(text);
// Input pointer, starts out pointing to a pseudo-newline before the start
int i = 0;
// Current accumulator. See the doc comment for Preprocessor_Hash for the format.
XophpArray accum = stack.getAccum(); // =&
// True to find equals signs in arguments
boolean findEquals = false;
// True to take notice of pipe characters
boolean findPipe = false;
int headingIndex = 1;
// True if i is inside a possible heading
boolean inHeading = false;
// True if there are no more greater-than (>) signs right of i
boolean noMoreGT = false;
// Map of tag name => true if there are no more closing tags of given type right of i
XophpArray noMoreClosingTag = XophpArray.New();
// True to ignore all input up to the next <onlyinclude>
boolean findOnlyinclude = enableOnlyinclude;
// Do a line-start run without outputting an LF character
boolean fakeLineStart = true;
while (true) {
// // this.memCheck(); // XO.NOTE: commented out in MW
int startPos = 0;
if (findOnlyinclude) {
// Ignore all input up to the next <onlyinclude>
startPos = XophpString_.strpos(text, "<onlyinclude>", i);
if (XophpInt_.is_false(startPos)) {
// Ignored section runs to the end
accum.Add(XophpArray.New("ignore", XophpArray.New(XophpString_.substr(text, i))));
break;
}
int tagEndPos = startPos + XophpString_.strlen("<onlyinclude>"); // past-the-end
accum.Add(XophpArray.New("ignore", XophpArray.New(XophpString_.substr(text, i, tagEndPos - i))));
i = tagEndPos;
findOnlyinclude = false;
}
String found = null, curChar = null;
XophpArray rule = null;
if (fakeLineStart) {
found = "line-start";
curChar = "";
} else {
// Find next opening brace, closing brace or pipe
String search = searchBase;
String currentClosing;
if (XophpObject_.is_false(stack.top)) {
currentClosing = "";
} else {
currentClosing = stack.top.close;
search += currentClosing;
}
if (findPipe) {
search += "|";
}
if (findEquals) {
// First equals will be for the template
search += "=";
}
rule = null;
// Output literal section, advance input counter
int literalLength = XophpString_.strcspn(text, XophpString_.strspn_hash(search), i);
if (literalLength > 0) {
XomwPreprocessor_Hash.addLiteral(accum, XophpString_.substr(text, i, literalLength));
i += literalLength;
}
if (i >= lengthText) {
if (XophpString_.eq(currentClosing, "\n")) {
// Do a past-the-end run to finish off the heading
curChar = "";
found = "line-end";
} else {
// All done
break;
}
} else {
String curTwoChar = null;
curChar = curTwoChar = Char_.To_str(String_.CharAt(text, i));
if ((i + 1) < lengthText) {
curTwoChar += Char_.To_str(String_.CharAt(text, i + 1));
}
if (String_.Eq(curChar, "|")) {
found = "pipe";
} else if (String_.Eq(curChar, "=")) {
found = "equals";
} else if (String_.Eq(curChar, "<")) {
found = "angle";
} else if (String_.Eq(curChar, "\n")) {
if (inHeading) {
found = "line-end";
} else {
found = "line-start";
}
} else if (String_.Eq(curTwoChar, currentClosing)) {
found = "close";
curChar = curTwoChar;
} else if (String_.Eq(curChar, currentClosing)) {
found = "close";
} else if (XophpObject_.isset_obj(this.rules.Get_by(curTwoChar))) {
curChar = curTwoChar;
found = "open";
rule = this.rules.Get_by_ary(curChar);
} else if (XophpObject_.isset_obj(this.rules.Get_by(curChar))) {
found = "open";
rule = this.rules.Get_by_ary(curChar);
} else {
// Some versions of PHP have a strcspn which stops on
// null characters; ignore these and continue.
// We also may get "-" and "}" characters here which
// don"t match -{ or currentClosing. Add these to
// output and continue.
if (String_.Eq(curChar, "-") || String_.Eq(curChar, "}")) {
XomwPreprocessor_Hash.addLiteral(accum, curChar);
}
++i;
continue;
}
}
}
if (String_.Eq(found, "angle")) {
String inner, close;
int attrEnd;
XophpArray matches = XophpArray.New();
// Handle </onlyinclude>
if (enableOnlyinclude
&& XophpString_.eq(XophpString_.substr(text, i, XophpString_.strlen("</onlyinclude>")), "</onlyinclude>")
) {
findOnlyinclude = true;
continue;
}
// Determine element name
if (!XophpRegex_.preg_match_bool(elementsRegex, text, matches, 0, i + 1)) {
// Element name missing or not listed
XomwPreprocessor_Hash.addLiteral(accum, "<");
++i;
continue;
}
// Handle comments
if (XophpArray_.isset(matches, 2) && String_.Eq(matches.Get_at_str(2), "!--")) {
// To avoid leaving blank lines, when a sequence of
// space-separated comments is both preceded and followed by
// a newline (ignoring spaces), then
// trim leading and trailing spaces and the trailing newline.
// Find the end
int endPos = XophpString_.strpos(text, "-->", i + 4);
if (XophpInt_.is_false(endPos)) {
// Unclosed comment in input, runs to end
inner = XophpString_.substr(text, i);
accum.Add(XophpArray.New("comment", XophpArray.New(inner)));
i = lengthText;
} else {
// Search backwards for leading whitespace
int wsStart = XophpInt_.is_true(i) ? (i - XophpString_.strspn(revText, strspn_hash__ws_tab, lengthText - i)) : 0;
// Search forwards for trailing whitespace
// wsEnd will be the position of the last space (or the ">" if there"s none)
int wsEnd = endPos + 2 + XophpString_.strspn(text, strspn_hash__ws_tab, endPos + 3);
// Keep looking forward as long as we"re finding more
// comments.
XophpArray comments = XophpArray.New(XophpArray.New().Add(wsStart).Add(wsEnd));
while (XophpString_.eq(XophpString_.substr(text, wsEnd + 1, 4), "<!--")) {
int c = XophpString_.strpos(text, "-.", wsEnd + 4);
if (XophpInt_.is_false(c)) {
break;
}
c = c + 2 + XophpString_.strspn(text, strspn_hash__ws_tab, c + 3);
comments.Add(XophpArray.New(wsEnd + 1).Add(c));
wsEnd = c;
}
// Eat the line if possible
// TODO: This could theoretically be done if wsStart == 0, i.e. for comments at
// the overall start. That"s not how Sanitizer::removeHTMLcomments() did it, but
// it"s a possible beneficial b/c break.
if (wsStart > 0 && XophpString_.eq(XophpString_.substr(text, wsStart - 1, 1), "\n")
&& XophpString_.eq(XophpString_.substr(text, wsEnd + 1, 1), "\n")
) {
// Remove leading whitespace from the end of the accumulator
int wsLength = i - wsStart;
int endIndex = XophpArray_.count(accum) - 1;
// Sanity check
if (wsLength > 0
&& endIndex >= 0
&& XophpType_.is_string(accum.Get_at(endIndex))
&& XophpString_.strspn(accum.Get_at_str(endIndex), strspn_hash__ws_tab, -wsLength) == wsLength
) {
accum.Set(endIndex, XophpString_.substr(accum.Get_at_str(endIndex), 0, -wsLength));
}
// Dump all but the last comment to the accumulator
int commentsLen = comments.Len();
for (int commentsIdx = 0; commentsIdx < commentsLen; commentsIdx++) {
XophpArrayItm itm = comments.Get_at_itm(commentsIdx);
int j = itm.Key_as_int();
XophpArray com = (XophpArray)itm.Val();
startPos = com.Get_at_int(0);
endPos = com.Get_at_int(1) + 1;
if (j == (XophpArray_.count(comments) - 1)) {
break;
}
inner = XophpString_.substr(text, startPos, endPos - startPos);
accum.Add(XophpArray.New("comment", XophpArray.New(inner)));
}
// Do a line-start run next time to look for headings after the comment
fakeLineStart = true;
} else {
// No line to eat, just take the comment itself
startPos = i;
endPos += 2;
}
if (XophpObject_.is_true(stack.top)) {
XomwPPDPart part = stack.top.getCurrentPart();
if (!(XophpObject_.isset(part.commentEnd) && part.commentEnd == wsStart - 1)) {
part.visualEnd = wsStart;
}
// Else comments abutting, no change in visual end
part.commentEnd = endPos;
}
i = endPos + 1;
inner = XophpString_.substr(text, startPos, endPos - startPos + 1);
accum.Add(XophpArray.New("comment", XophpArray.New(inner)));
}
continue;
}
String name = matches.Get_at_str(1);
String lowerName = XophpString_.strtolower(name);
int attrStart = i + XophpString_.strlen(name) + 1;
// Find end of tag
int tagEndPos = noMoreGT ? XophpInt_.False : XophpString_.strpos(text, ">", attrStart);
if (XophpInt_.is_false(tagEndPos)) {
// Infinite backtrack
// Disable tag search to prevent worst-case O(N^2) performance
noMoreGT = true;
XomwPreprocessor_Hash.addLiteral(accum, "<");
++i;
continue;
}
// Handle ignored tags
if (XophpArray_.in_array(lowerName, ignoredTags)) {
accum.Add(XophpArray.New("ignore", XophpArray.New(XophpString_.substr(text, i, tagEndPos - i + 1))));
i = tagEndPos + 1;
continue;
}
int tagStartPos = i;
if (XophpString_.Char_eq(text, tagEndPos - 1, "/")) {
// Short end tag
attrEnd = tagEndPos - 1;
inner = null;
i = tagEndPos + 1;
close = null;
} else {
matches.Clear(); // XO
attrEnd = tagEndPos;
// Find closing tag
if (
!XophpObject_.isset_obj(noMoreClosingTag.Get_by(name)) &&
XophpRegex_.preg_match_bool("</" + XophpRegex_.preg_quote(name, "/") + "\\s*>", XophpRegex_.MODIFIER_i,
text, matches, XophpRegex_.PREG_OFFSET_CAPTURE, tagEndPos + 1)
) {
inner = XophpString_.substr(text, tagEndPos + 1, matches.Get_at_ary(0).Get_at_int(1) - tagEndPos - 1);
i = matches.Get_at_ary(0).Get_at_int(1) + XophpString_.strlen(matches.Get_at_ary(0).Get_at_str(0));
close = matches.Get_at_ary(0).Get_at_str(0);
} else {
// No end tag
if (XophpArray_.in_array(name, xmlishAllowMissingEndTag)) {
// Let it run out to the end of the text.
inner = XophpString_.substr(text, tagEndPos + 1);
i = lengthText;
close = null;
} else {
// Don"t match the tag, treat opening tag as literal and resume parsing.
i = tagEndPos + 1;
XomwPreprocessor_Hash.addLiteral(accum,
XophpString_.substr(text, tagStartPos, tagEndPos + 1 - tagStartPos));
// Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>...
noMoreClosingTag.Set(name, true);
continue;
}
}
}
// <includeonly> and <noinclude> just become <ignore> tags
if (XophpArray_.in_array(lowerName, ignoredElements)) {
accum.Add(XophpArray.New("ignore", XophpArray.New(XophpString_.substr(text, tagStartPos, i - tagStartPos))));
continue;
}
String attr = null;
if (attrEnd <= attrStart) {
attr = "";
} else {
// Note that the attr element contains the whitespace between name and attribute,
// this is necessary for precise reconstruction during pre-save transform.
attr = XophpString_.substr(text, attrStart, attrEnd - attrStart);
}
XophpArray children = XophpArray.New(
XophpArray.New("name", XophpArray.New(name)),
XophpArray.New("attr", XophpArray.New(attr)));
if (inner != null) {
children.Add(XophpArray.New("inner", XophpArray.New(inner)));
}
if (close != null) {
children.Add(XophpArray.New("close", XophpArray.New(close)));
}
accum.Add(XophpArray.New("ext", children));
} else if (String_.Eq(found, "line-start")) {
// Is this the start of a heading?
// Line break belongs before the heading element in any case
if (fakeLineStart) {
fakeLineStart = false;
} else {
XomwPreprocessor_Hash.addLiteral(accum, curChar);
i++;
}
int count = XophpString_.strspn(text, strspn_hash__eq, i, 6);
if (count == 1 && findEquals) {
// DWIM: This looks kind of like a name/value separator.
// Let"s let the equals handler have it and break the potential
// heading. This is heuristic, but AFAICT the methods for
// completely correct disambiguation are very complex.
} else if (count > 0) {
XophpArray piece = XophpArray.New()
.Add("open", "\n")
.Add("close", "\n")
.Add("parts", XophpArray.New(new XomwPPDPart_Hash(XophpString_.str_repeat("=", count))))
.Add("startPos", i)
.Add("count", count);
stack.push(piece);
accum = stack.getAccum(); // =&
XophpArray stackFlags = stack.getFlags();
if (XophpArray_.isset(stackFlags, "findEquals")) {
findEquals = stackFlags.Get_by_bool("findEquals");
}
if (XophpArray_.isset(stackFlags, "findPipe")) {
findPipe = stackFlags.Get_by_bool("findPipe");
}
if (XophpArray_.isset(stackFlags, "inHeading")) {
inHeading = stackFlags.Get_by_bool("inHeading");
}
i += count;
}
} else if (found == "line-end") {
XomwPPDStackElement piece = stack.top;
// A heading must be open, otherwise \n wouldn"t have been in the search list
// FIXME: Don"t use assert()
// phpcs:ignore MediaWiki.Usage.ForbiddenFunctions.assert
// assert(piece.open === "\n");
XomwPPDPart part = piece.getCurrentPart();
// Search back through the input to see if it has a proper close.
// Do this using the reversed String since the other solutions
// (end anchor, etc.) are inefficient.
int wsLength = XophpString_.strspn(revText, strspn_hash__ws_tab, lengthText - i);
int searchStart = i - wsLength;
// XO.NOTE: MW says isset(part.commentEnd) b/c commentEnd can be null due to magic property
if (XophpInt_.is_true(part.commentEnd) && searchStart - 1 == part.commentEnd) {
// Comment found at line end
// Search for equals signs before the comment
searchStart = part.visualEnd;
searchStart -= XophpString_.strspn(revText, strspn_hash__ws_tab, lengthText - searchStart);
}
XophpArray element;
int count = piece.count;
int equalsLength = XophpString_.strspn(revText, strspn_hash__eq, lengthText - searchStart);
if (equalsLength > 0) {
if (searchStart - equalsLength == piece.startPos) {
// This is just a single String of equals signs on its own line
// Replicate the doHeadings behavior /={count}(.+)={count}/
// First find out how many equals signs there really are (don"t stop at 6)
count = equalsLength;
if (count < 3) {
count = 0;
} else {
count = XophpMath_.min(6, XophpDouble_.intval((count - 1) / 2));
}
} else {
count = XophpMath_.min(equalsLength, count);
}
if (count > 0) {
// Normal match, output <h>
element = XophpArray.New(XophpArray.New("possible-h",
XophpArray_.array_merge(
XophpArray.New(
XophpArray.New("@level", XophpArray.New(count)),
XophpArray.New("@i", XophpArray.New(headingIndex++))
),
accum
)
));
} else {
// Single equals sign on its own line, count=0
element = accum;
}
} else {
// No match, no <h>, just pass down the inner text
element = accum;
}
// Unwind the stack
stack.pop();
accum = stack.getAccum(); // =&
XophpArray stackFlags = stack.getFlags();
if (XophpArray_.isset(stackFlags, "findEquals")) {
findEquals = stackFlags.Get_by_bool("findEquals");
}
if (XophpArray_.isset(stackFlags, "findPipe")) {
findPipe = stackFlags.Get_by_bool("findPipe");
}
if (XophpArray_.isset(stackFlags, "inHeading")) {
inHeading = stackFlags.Get_by_bool("inHeading");
}
// Append the result to the enclosing accumulator
XophpArray_.array_splice(accum, XophpArray_.count(accum), 0, element);
// Note that we do NOT increment the input pointer.
// This is because the closing linebreak could be the opening linebreak of
// another heading. Infinite loops are avoided because the next iteration MUST
// hit the heading open case above, which unconditionally increments the
// input pointer.
} else if (String_.Eq(found, "open")) {
// count opening brace characters
int curLen = XophpString_.strlen(curChar);
int count = (curLen > 1) ?
// allow the final character to repeat
XophpString_.strspn(text, XophpString_.strspn_hash(XophpString_.Char_as_str(curChar, curLen - 1)), i + 1) + 1 :
XophpString_.strspn(text, XophpString_.strspn_hash(curChar), i);
String savedPrefix = "";
boolean lineStart = (i > 0 && XophpString_.Char_eq(text, i - 1, "\n"));
if (String_.Eq(curChar, "-{") && count > curLen) {
// -{ => {{ transition because rightmost wins
savedPrefix = "-";
i++;
curChar = "{";
count--;
rule = this.rules.Get_by_ary(curChar);
}
// we need to add to stack only if opening brace count is enough for one of the rules
if (count >= rule.Get_by_int("min")) {
// Add it to the stack
XophpArray piece = XophpArray.New()
.Add("open", curChar)
.Add("close", rule.Get_by("end"))
.Add("savedPrefix", savedPrefix)
.Add("count", count)
.Add("lineStart", lineStart)
;
stack.push(piece);
accum = stack.getAccum(); // =&
XophpArray stackFlags = stack.getFlags();
if (XophpArray_.isset(stackFlags, "findEquals")) {
findEquals = stackFlags.Get_by_bool("findEquals");
}
if (XophpArray_.isset(stackFlags, "findPipe")) {
findPipe = stackFlags.Get_by_bool("findPipe");
}
if (XophpArray_.isset(stackFlags, "inHeading")) {
inHeading = stackFlags.Get_by_bool("inHeading");
}
} else {
// Add literal brace(s)
XomwPreprocessor_Hash.addLiteral(accum, savedPrefix + XophpString_.str_repeat(curChar, count));
}
i += count;
} else if (String_.Eq(found, "close")) {
XomwPPDStackElement_Hash piece = (XomwPPDStackElement_Hash)stack.top;
// lets check if there are enough characters for closing brace
int maxCount = piece.count;
if (String_.Eq(piece.close, "}-") && String_.Eq(curChar, "}")) {
maxCount--; // don"t try to match closing "-" as a "}"
}
int curLen = XophpString_.strlen(curChar);
int count = (curLen > 1) ? curLen :
XophpString_.strspn(text, XophpString_.strspn_hash(curChar), i, maxCount);
// check for maximum matching characters (if there are 5 closing
// characters, we will probably need only 3 - depending on the rules)
rule = this.rules.Get_by_ary(piece.open);
int matchingCount;
if (count > rule.Get_by_int("max")) {
// The specified maximum exists in the callback array, unless the caller
// has made an error
matchingCount = rule.Get_by_int("max");
} else {
// Count is less than the maximum
// Skip any gaps in the callback array to find the true largest match
// Need to use array_key_exists not isset because the callback can be null
matchingCount = count;
while (matchingCount > 0 && !XophpArray_.array_key_exists(matchingCount, rule.Get_by_ary("names"))) {
--matchingCount;
}
}
String endText;
XophpArray element;
if (matchingCount <= 0) {
// No matching element found in callback array
// Output a literal closing brace and continue
endText = XophpString_.substr(text, i, count);
XomwPreprocessor_Hash.addLiteral(accum, endText);
i += count;
continue;
}
String name = rule.Get_by_ary("names").Get_by_str(matchingCount);
if (name == null) {
// No element, just literal text
endText = XophpString_.substr(text, i, matchingCount);
element = piece.breakSyntax(matchingCount);
XomwPreprocessor_Hash.addLiteral(element, endText);
} else {
// Create XML element
XophpArray parts = piece.parts;
XophpArray titleAccum = ((XomwPPDPart)parts.Get_at(0)).output;
XophpArray_.unset(parts, 0);
XophpArray children = XophpArray.New();
// The invocation is at the start of the line if lineStart is set in
// the stack, and all opening brackets are used up.
if (maxCount == matchingCount &&
!XophpObject_.empty(piece.lineStart) &&
XophpString_.strlen(piece.savedPrefix) == 0) {
children.Add(XophpArray.New("@lineStart", XophpArray.New(1)));
}
XophpArray titleNode = XophpArray.New("title", titleAccum);
children.Add(titleNode);
int argIndex = 1;
int parts_len = parts.Len();
for (int j = 0; j < parts_len; j++) {
XomwPPDPart_Hash part = (XomwPPDPart_Hash)parts.Get_at(j);
if (XophpInt_.is_true(part.eqpos)) { // XO.NOTE: MW says isset(part.commentEnd) b/c commentEnd can be null due to magic property
Object equalsNode = part.output.Get_at(part.eqpos);
XophpArray nameNode = XophpArray.New("name", XophpArray_.array_slice(part.output, 0, part.eqpos));
XophpArray valueNode = XophpArray.New("value", XophpArray_.array_slice(part.output, part.eqpos + 1));
XophpArray partNode = XophpArray.New("part", XophpArray.New(nameNode, equalsNode, valueNode));
children.Add(partNode);
} else {
XophpArray nameNode = XophpArray.New("name", XophpArray.New(XophpArray.New("@index", XophpArray.New(argIndex++))));
XophpArray valueNode = XophpArray.New("value", part.output);
XophpArray partNode = XophpArray.New("part", XophpArray.New(nameNode, valueNode));
children.Add(partNode);
}
}
element = XophpArray.New(XophpArray.New(name, children));
}
// Advance input pointer
i += matchingCount;
// Unwind the stack
stack.pop();
accum = stack.getAccum(); // =&
// Re-add the old stack element if it still has unmatched opening characters remaining
if (matchingCount < piece.count) {
piece.parts = XophpArray.New(new XomwPPDPart_Hash(""));
piece.count -= matchingCount;
// do we still qualify for any callback with remaining count?
int min = this.rules.Get_by_ary(piece.open).Get_by_int("min");
if (piece.count >= min) {
stack.push(piece);
accum = stack.getAccum(); // =&
} else if (piece.count == 1 && String_.Eq(piece.open, "{") && String_.Eq(piece.savedPrefix, "-")) {
piece.savedPrefix = "";
piece.open = "-{";
piece.count = 2;
piece.close = this.rules.Get_by_ary(piece.open).Get_by_str("end");
stack.push(piece);
accum = stack.getAccum(); // =&
} else {
String s = XophpString_.substr(piece.open, 0, -1);
s += XophpString_.str_repeat(
XophpString_.substr(piece.open, -1),
piece.count - XophpString_.strlen(s)
);
XomwPreprocessor_Hash.addLiteral(accum, piece.savedPrefix + s);
}
} else if (!String_.Eq(piece.savedPrefix, "")) {
XomwPreprocessor_Hash.addLiteral(accum, piece.savedPrefix);
}
XophpArray stackFlags = stack.getFlags();
if (XophpArray_.isset(stackFlags, "findEquals")) {
findEquals = stackFlags.Get_by_bool("findEquals");
}
if (XophpArray_.isset(stackFlags, "findPipe")) {
findPipe = stackFlags.Get_by_bool("findPipe");
}
if (XophpArray_.isset(stackFlags, "inHeading")) {
inHeading = stackFlags.Get_by_bool("inHeading");
}
// Add XML element to the enclosing accumulator
XophpArray_.array_splice(accum, XophpArray_.count(accum), 0, element);
} else if (String_.Eq(found, "pipe")) {
findEquals = true; // shortcut for getFlags()
stack.addPart();
accum = stack.getAccum(); // =&
++i;
} else if (String_.Eq(found, "equals")) {
findEquals = false; // shortcut for getFlags()
accum.Add(XophpArray.New("equals", XophpArray.New("=")));
stack.getCurrentPart().eqpos = XophpArray_.count(accum) - 1;
++i;
}
}
// Output any remaining unclosed brackets
XophpArray tempStack = stack.stack;
int tempStackLen = tempStack.Len();
for (int j = 0; j < tempStackLen; j++) {
XomwPPDStackElement_Hash piece = (XomwPPDStackElement_Hash)tempStack.Get_at(j);
XophpArray_.array_splice(stack.rootAccum, XophpArray_.count(stack.rootAccum), 0, piece.breakSyntax());
}
// Enable top-level headings
XophpArray rootAccum = stack.rootAccum;
int rootAccumLen = rootAccum.Len();
for (int j = 0; j < rootAccumLen; j++) {
XophpArray node = rootAccum.Get_at_ary_or_null(j); // stack.rootAccum as &node
if (XophpArray_.is_array(node) && String_.Eq(node.Get_at_str(XomwPPNode_Hash_Tree.NAME), "possible-h")) {
node.Set(XomwPPNode_Hash_Tree.NAME, "h");
}
}
XophpArray rootStore = XophpArray.New(XophpArray.New("root", stack.rootAccum));
XomwPPNode_Hash_Tree rootNode = new XomwPPNode_Hash_Tree(rootStore, 0);
// Cache
// tree = json_encode(rootStore, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
// if (tree !== false) {
// this.cacheSetTree(text, flags, tree);
// }
return rootNode;
}
private static void addLiteral(XophpArray accum, String text) {
int n = XophpArray_.count(accum);
if (XophpInt_.is_true(n) && XophpType_.is_string(accum.Get_at(n - 1))) {
accum.Concat_str(n - 1, text);
} else {
accum.Add(text);
}
}
@Override public String preprocessToDbg(String src, boolean for_inclusion) {
XomwPPNode_Hash_Tree node = (XomwPPNode_Hash_Tree)this.preprocessToObj(src, for_inclusion ? 1 : 0);
return node.toString();
}
}

@ -0,0 +1,248 @@
/*
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.includes.parsers.preprocessors_new; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.parsers.*;
import org.junit.*;
public class XomwPreprocessor__tst {
private final XomwPreprocessor__fxt fxt = new XomwPreprocessor__fxt();
@Before public void init() {fxt.Clear();}
@Test public void Text() {
fxt.Test__parse("abc", "<root>abc</root>");
}
@Test public void Brack() {
fxt.Test__parse("a[[b]]c", "<root>a[[b]]c</root>");
}
@Test public void Brack__one() { // COVERS: "Add literal brace(s)"
fxt.Test__parse("a[b]c", "<root>a[b]c</root>");
}
@Test public void Brack__max() { // COVERS: "The specified maximum exists in the callback array, unless the caller"
fxt.Test__parse("a[[[[[b]]]]]c", "<root>a[[[[[b]]]]]c</root>");
}
@Test public void Template() {
fxt.Test__parse("a{{b}}c", "<root>a<template><title>b</title></template>c</root>");
}
@Test public void Template__args__idx() {
fxt.Test__parse("a{{b|c|d}}e", "<root>a<template><title>b</title><part><name index=\"1\" /><value>c</value></part><part><name index=\"2\" /><value>d</value></part></template>e</root>");
}
@Test public void Template__args__key() {
fxt.Test__parse("a{{b|c=d}}e", "<root>a<template><title>b</title><part><name>c</name>=<value>d</value></part></template>e</root>");
}
@Test public void Template__line_start() { // COVERS: "The invocation is at the start of the line if lineStart is set in"
fxt.Test__parse(String_.Concat_lines_nl_skip_last
( "a"
, "{{b}}"
), String_.Concat_lines_nl_skip_last
( "<root>a"
, "<template lineStart=\"1\"><title>b</title></template></root>"
));
}
@Test public void Template__max() { // COVERS: "do we still qualify for any callback with remaining count?"
fxt.Test__parse("a{{{{{b}}}}}c", "<root>a<template><title><tplarg><title>b</title></tplarg></title></template>c</root>");
}
@Test public void Tplarg() {
fxt.Test__parse("a{{{b}}}c", "<root>a<tplarg><title>b</title></tplarg>c</root>");
}
@Test public void Tplarg__dflt() {
fxt.Test__parse("a{{{b|c}}}d", "<root>a<tplarg><title>b</title><part><name index=\"1\" /><value>c</value></part></tplarg>d</root>");
}
@Test public void Comment() {
fxt.Test__parse("a<!--b-->c", "<root>a<comment>&lt;!--b--&gt;</comment>c</root>");
}
@Test public void Comment__dangling() {// COVERS: "Unclosed comment in input, runs to end"
fxt.Test__parse("a<!--b", "<root>a<comment>&lt;!--b</comment></root>");
}
@Test public void Comment__ws() { // COVERS: "Search backwards for leading whitespace"
fxt.Test__parse("a <!--b--> c", "<root>a <comment>&lt;!--b--&gt;</comment> c</root>"); // NOTE: space is outside comment
}
@Test public void Comment__many__ws() {// COVERS: "Dump all but the last comment to the accumulator"
fxt.Test__parse("a <!--1--> <!--2--> z", "<root>a <comment>&lt;!--1--&gt;</comment> <comment>&lt;!--2--&gt;</comment> z</root>"); // NOTE: space is outside comment;
}
@Test public void Comment__nl__ws() { // COVERS: "Eat the line if possible"
fxt.Test__parse(String_.Concat_lines_nl_skip_last
( "a"
, " <!--1--> "
, " <!--2--> "
, "z"
), String_.Concat_lines_nl_skip_last
( "<root>a"
, "<comment> &lt;!--1--&gt; " // NOTE: space is inside </comment> if flanked by nl;
, "</comment><comment> &lt;!--2--&gt; "
, "</comment>z</root>"
));
}
@Test public void Ext() { // COVERS.ALSO: "Note that the attr element contains the whitespace between name and attribute,"
fxt.Test__parse("a<pre id=\"1\">b</pre>c", "<root>a<ext><name>pre</name><attr> id=&quot;1&quot;</attr><inner>b</inner><close>&lt;/pre&gt;</close></ext>c</root>");
}
@Test public void Ext__inline() { // COVERS: "if ( $text[$tagEndPos - 1] == '/' ) {"
fxt.Test__parse("a<pre/>b" , "<root>a<ext><name>pre</name><attr></attr></ext>b</root>");
fxt.Test__parse("a<pre />b" , "<root>a<ext><name>pre</name><attr> </attr></ext>b</root>");
}
@Test public void Ext__end__pass__space() {// COVERS: "\s*" in `preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",`
fxt.Test__parse("a<pre>b</pre >c", "<root>a<ext><name>pre</name><attr></attr><inner>b</inner><close>&lt;/pre &gt;</close></ext>c</root>");
}
@Test public void Ext__end__pass__name() { // COVERS: "\s*" in `preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",`
fxt.Test__parse("a<pre>b</pro></pre>c", "<root>a<ext><name>pre</name><attr></attr><inner>b&lt;/pro&gt;</inner><close>&lt;/pre&gt;</close></ext>c</root>");
}
@Test public void Ext__end__fail__angle() {// COVERS: "\s*" in `preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",`
fxt.Test__parse("a<pre>b</pre c", "<root>a&lt;pre&gt;b&lt;/pre c</root>");
}
@Test public void Ext__dangling() { // COVERS: "Let it run out to the end of the text."
fxt.Test__parse("a<pre>bc", "<root>a&lt;pre&gt;bc</root>");
}
@Test public void Ext__dangling__many() { // COVERS: "Cache results, otherwise we have O(N^2) performance for input like <foo><foo><foo>..."
fxt.Test__parse("a<pre><pre><pre>bc", "<root>a&lt;pre&gt;&lt;pre&gt;&lt;pre&gt;bc</root>");
}
@Test public void Ext__unclosed() { // COVERS: "Infinite backtrack"
fxt.Test__parse("a<pre bcd", "<root>a&lt;pre bcd</root>");
}
@Test public void Ext__noinclude() { // COVERS: "<includeonly> and <noinclude> just become <ignore> tags"
fxt.Init__for_inclusion_(Bool_.N);
fxt.Test__parse("a<includeonly>b<noinclude>c</noinclude>d</includeonly>e", "<root>a<ignore>&lt;includeonly&gt;b&lt;noinclude&gt;c&lt;/noinclude&gt;d&lt;/includeonly&gt;</ignore>e</root>");
}
@Test public void Heading() {
fxt.Test__parse(String_.Concat_lines_nl_skip_last
( "a"
, "== b1 =="
, "z"
), String_.Concat_lines_nl_skip_last
( "<root>a"
, "<h level=\"2\" i=\"1\">== b1 ==</h>"
, "z</root>"
));
}
@Test public void Heading__eos__no_nl() {
fxt.Test__parse(String_.Concat_lines_nl_skip_last
( "a"
, "== b1 =="
), String_.Concat_lines_nl_skip_last
( "<root>a"
, "<h level=\"2\" i=\"1\">== b1 ==</h></root>"
));
}
@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
( "<root><h level=\"2\" i=\"1\">== b1 ==</h>"
, "z</root>"
));
}
@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
( "<root>a<template><title>b</title><part><name>"
, "</name>=<value>c="
, "</value></part></template>d</root>"
));
}
@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
( "<root>a<template><title>b</title><part><name index=\"1\" /><value>"
, "<possible-h level=\"2\" i=\"1\">==c==</possible-h>" // NOTE: verified against MW:1.29; possible-h line hit
, "</value></part></template>d</root>"
));
}
@Test public void Heading__comment() { // COVERS: "Comment found at line end"
fxt.Test__parse(String_.Concat_lines_nl_skip_last
( "a"
, "==b== <!--c-->"
, ""
), String_.Concat_lines_nl_skip_last
( "<root>a"
, "<h level=\"2\" i=\"1\">==b== <comment>&lt;!--c--&gt;</comment></h>"
, "</root>"
));
}
@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
( "<root>a"
, "<h level=\"2\" i=\"1\">=====</h>"
, "</root>"
));
}
@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
( "<root>a"
, "="
, "</root>"
));
}
@Test public void Heading__unclosed() { // COVERS: "No match, no <h>, just pass down the inner src"
fxt.Test__parse(String_.Concat_lines_nl_skip_last
( "a"
, "===b"
, ""
), String_.Concat_lines_nl_skip_last
( "<root>a"
, "===b"
, "</root>"
));
}
@Test public void Inclusion__n() {
fxt.Init__for_inclusion_(Bool_.N);
fxt.Test__parse("a<onlyinclude>b</onlyinclude>c", "<root>a<ignore>&lt;onlyinclude&gt;</ignore>b<ignore>&lt;/onlyinclude&gt;</ignore>c</root>");
}
@Test public void Inclusion__y() {
fxt.Init__for_inclusion_(Bool_.Y);
fxt.Test__parse("a<onlyinclude>b</onlyinclude>c", "<root><ignore>a&lt;onlyinclude&gt;</ignore>b<ignore>&lt;/onlyinclude&gt;c</ignore></root>");
}
@Test public void Ignored__noinclude() { // COVERS: "Handle ignored tags"
fxt.Init__for_inclusion_(Bool_.N);
fxt.Test__parse("a<noinclude>b</noinclude>c", "<root>a<ignore>&lt;noinclude&gt;</ignore>b<ignore>&lt;/noinclude&gt;</ignore>c</root>");
}
}
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);
}
}
}

@ -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;
}
// /**

@ -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

@ -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));
}
}

@ -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;

Loading…
Cancel
Save