1
0
mirror of https://github.com/gnosygnu/xowa.git synced 2026-03-02 03:49:30 +00:00

JsonConfig: Add Localize function for tabular data

This commit is contained in:
gnosygnu
2018-02-18 08:16:43 -05:00
parent 8967ca810a
commit 54d74b0acd
67 changed files with 5592 additions and 200 deletions

View File

@@ -15,31 +15,10 @@ Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt
*/
package gplx.xowa.mediawiki; import gplx.*; import gplx.xowa.*;
public class XophpArray {
public static boolean popBoolOrN(List_adp list) {return Bool_.Cast(List_adp_.Pop_or(list, false));}
public static byte[] popBryOrNull(List_adp list) {return (byte[])List_adp_.Pop_or(list, null);}
public static String[] array_keys_str(Ordered_hash array) {
int len = array.Len();
String[] rv = new String[len];
for (int i = 0; i < len; i++) {
rv[i] = (String)array.Get_at(i);
}
return rv;
}
public static byte[][] array_keys_bry(Ordered_hash array) {
int len = array.Len();
byte[][] rv = new byte[len][];
for (int i = 0; i < len; i++) {
rv[i] = (byte[])array.Get_at(i);
}
return rv;
}
public static boolean array_key_exists(int key, Ordered_hash array) {return array.Has(key);}
public static boolean array_key_exists(String key, Ordered_hash array) {return array.Has(key);}
public static boolean array_key_exists(byte[] key, Ordered_hash array) {return array.Has(key);}
public static boolean array_is_empty(Ordered_hash array) {
return array.Len() == 0;
}
public static void unset(Ordered_hash array, Object key) {
array.Del(key);
public static boolean in_array(String needle, String[] haystack) {
for (String hay : haystack)
if (String_.Eq(hay, needle))
return true;
return false;
}
}

View File

@@ -0,0 +1,54 @@
/*
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 XophpArrayUtl {
public static boolean popBoolOrN(List_adp list) {return Bool_.Cast(List_adp_.Pop_or(list, false));}
public static byte[] popBryOrNull(List_adp list) {return (byte[])List_adp_.Pop_or(list, null);}
public static String[] array_keys_str(Ordered_hash array) {
int len = array.Len();
String[] rv = new String[len];
for (int i = 0; i < len; i++) {
rv[i] = (String)array.Get_at(i);
}
return rv;
}
public static byte[][] array_keys_bry(Ordered_hash array) {
int len = array.Len();
byte[][] rv = new byte[len][];
for (int i = 0; i < len; i++) {
rv[i] = (byte[])array.Get_at(i);
}
return rv;
}
public static boolean array_key_exists(int key, Ordered_hash array) {return array.Has(key);}
public static boolean array_key_exists(String key, Ordered_hash array) {return array.Has(key);}
public static boolean array_key_exists(byte[] key, Ordered_hash array) {return array.Has(key);}
public static boolean array_is_empty(Ordered_hash array) {
return array.Len() == 0;
}
public static void unset(Ordered_hash array, Object key) {
array.Del(key);
}
public static Object[] unset_by_idx(Object[] ary, int idx) {
int ary_len = ary.length;
Object[] rv = new Object[ary_len];
for (int i = 0; i < idx; i++)
rv[i] = ary[i];
for (int i = idx + 1; i < ary_len; i++)
rv[i - 1] = ary[i];
return rv;
}
}

View File

@@ -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 interface XophpClassBldr {
String Id();
Object Make(Object... args);
}

View File

@@ -0,0 +1,25 @@
/*
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 XophpClassBldrs {
private final Ordered_hash hash = Ordered_hash_.New();
public void Add(String id, XophpClassBldr bldr) {
hash.Add(id, bldr);
}
public XophpClassBldr Get_by_or_null(String id) {
return (XophpClassBldr)hash.Get_by(id);
}
}

View File

@@ -0,0 +1,22 @@
/*
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 XophpEnv {
public XophpClassBldrs ClassBldrs() {return classBldrs;} private final XophpClassBldrs classBldrs = new XophpClassBldrs();
public Ordered_hash Singletons() {return singletons;} private final Ordered_hash singletons = Ordered_hash_.New();
public static final XophpEnv Instance = new XophpEnv();
}

View File

@@ -0,0 +1,81 @@
/*
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 XophpStdClass {
private final List_adp list = List_adp_.New();
private final Ordered_hash hash = Ordered_hash_.New();
public int Len() {return list.Len();}
public boolean Has(String key) {return hash.Has(key);}
public void Add_at_as_itm(XophpStdClass itm) {
list.Add(itm);
}
public void Add_by_as_obj(String key, Object itm) {
list.Add(itm);
hash.Add(key, itm);
}
public XophpStdClass Get_at_as_itm(int idx) {return (XophpStdClass)list.Get_at(idx);}
public Object Get_by_as_obj(String key) {return hash.Get_by(key);}
public XophpStdClass Get_by_as_itm(String key) {return (XophpStdClass)hash.Get_by(key);}
public String Get_by_as_str(String key) {return (String)hash.Get_by(key);}
public String Get_at_as_str(int idx) {return (String)list.Get_at(idx);}
public XophpStdClass Get_by_ary_as_itm(String... keys) {
return (XophpStdClass)Get_by_ary_or_null(false, keys, keys.length - 1, 0);
}
public boolean Comp_str(String key, String expd) {
String actl = Get_by_as_str(key);
return String_.Eq(expd, actl);
}
public void Set_by_as_itm(String key, XophpStdClass itm) {
hash.Add_if_dupe_use_nth(key, itm);
}
public void Set_by_as_itm(String[] keys, XophpStdClass rv) {
int keys_last_idx = keys.length - 1;
XophpStdClass itm = (XophpStdClass)Get_by_ary_or_null(true, keys, keys_last_idx - 1, 0);
itm.Set_by_as_itm(keys[keys_last_idx], rv);
}
public void Set_by_as_str(String key, String val) {
hash.Add_if_dupe_use_nth(key, val);
}
public void Set_at_as_str(int idx, String val) {
list.Del_at(idx);
list.Add_at(idx, val);
}
public void Del_by(String key) {
Object itm = hash.Get_by(key);
hash.Del(key);
list.Del(itm);
}
private Object Get_by_ary_or_null(boolean create, String[] keys, int keys_idx_last, int keys_idx) {
if (keys_idx == keys_idx_last) {
return hash.Get_by(keys[keys_idx_last]);
}
String key = keys[keys_idx];
XophpStdClass itm = Get_by_as_itm(key);
if (itm == null) {
// set
if (create) {
itm = new XophpStdClass();
Set_by_as_itm(key, itm);
}
// get
else {
return null;
}
}
return itm.Get_by_ary_or_null(create, keys, keys_idx_last, keys_idx + 1);
}
}

View File

@@ -1,85 +0,0 @@
/*
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.*;
import org.junit.*; import gplx.core.tests.*; import gplx.core.btries.*;
public class XophpStringTest {
private final XophpStringFxt fxt = new XophpStringFxt();
@Test public void Strspn_fwd__byte() {
fxt.Test_strspn_fwd__byte("aaaaab", Byte_ascii.Ltr_a, 0, -1, 5); // basic
fxt.Test_strspn_fwd__byte("aaaaab", Byte_ascii.Ltr_a, 1, -1, 4); // bgn
fxt.Test_strspn_fwd__byte("aaaaab", Byte_ascii.Ltr_a, 1, 2, 2); // max
}
@Test public void Strspn_fwd__space_or_tab() {
fxt.Test_strspn_fwd__space_or_tab(" a", 0, -1, 5); // basic
fxt.Test_strspn_fwd__space_or_tab(" a", 1, -1, 4); // bgn
fxt.Test_strspn_fwd__space_or_tab(" a", 1, 2, 2); // max
}
@Test public void Strspn_bwd__byte() {
fxt.Test_strspn_bwd__byte("aaaaab", Byte_ascii.Ltr_a, 5, -1, 5); // basic
fxt.Test_strspn_bwd__byte("aaaaab", Byte_ascii.Ltr_a, 4, -1, 4); // bgn
fxt.Test_strspn_bwd__byte("aaaaab", Byte_ascii.Ltr_a, 4, 2, 2); // max
}
@Test public void Strspn_bwd__space_or_tab() {
fxt.Test_strspn_bwd__space_or_tab(" a", 5, -1, 5); // basic
fxt.Test_strspn_bwd__space_or_tab(" a", 4, -1, 4); // bgn
fxt.Test_strspn_bwd__space_or_tab(" a", 4, 2, 2); // max
}
@Test public void Substr__bgn_is_neg() {
fxt.Test_substr("abcde" , -1, "e");
fxt.Test_substr("abcde" , -3, -1, "cd");
}
@Test public void Strtr() {
fxt.Init_strtr_by_trie("01", "89", "02", "79");
fxt.Test_strtr_by_trie("abc" , "abc"); // found=none
fxt.Test_strtr_by_trie("ab_01_cd" , "ab_89_cd"); // found=one
fxt.Test_strtr_by_trie("ab_01_cd_02_ef", "ab_89_cd_79_ef"); // found=many
fxt.Test_strtr_by_trie("01_ab" , "89_ab"); // BOS
fxt.Test_strtr_by_trie("ab_01" , "ab_89"); // EOS
}
}
class XophpStringFxt {
public void Test_strspn_fwd__byte(String src_str, byte find, int bgn, int max, int expd) {
byte[] src_bry = Bry_.new_u8(src_str);
Gftest.Eq__int(expd, XophpString.strspn_fwd__byte(src_bry, find, bgn, max, src_bry.length));
}
public void Test_strspn_fwd__space_or_tab(String src_str, int bgn, int max, int expd) {
byte[] src_bry = Bry_.new_u8(src_str);
Gftest.Eq__int(expd, XophpString.strspn_fwd__space_or_tab(src_bry, bgn, max, src_bry.length));
}
public void Test_strspn_bwd__byte(String src_str, byte find, int bgn, int max, int expd) {
Gftest.Eq__int(expd, XophpString.strspn_bwd__byte(Bry_.new_u8(src_str), find, bgn, max));
}
public void Test_strspn_bwd__space_or_tab(String src_str, int bgn, int max, int expd) {
Gftest.Eq__int(expd, XophpString.strspn_bwd__space_or_tab(Bry_.new_u8(src_str), bgn, max));
}
public void Test_substr(String src_str, int bgn, String expd) {Test_substr(src_str, bgn, String_.Len(src_str), expd);}
public void Test_substr(String src_str, int bgn, int len, String expd) {
Gftest.Eq__str(expd, XophpString.substr(Bry_.new_u8(src_str), bgn, len));
}
private Btrie_slim_mgr strtr_trie;
public void Init_strtr_by_trie(String... kvs) {
if (strtr_trie == null) strtr_trie = Btrie_slim_mgr.cs();
int len = kvs.length;
for (int i = 0; i < len; i += 2) {
strtr_trie.Add_str_str(kvs[i], kvs[i + 1]);
}
}
public void Test_strtr_by_trie(String src, String expd) {
Bry_bfr tmp = Bry_bfr_.New();
Btrie_rv trv = new Btrie_rv();
Gftest.Eq__str(expd, XophpString.strtr(Bry_.new_u8(src), strtr_trie, tmp, trv));
}
}

View File

@@ -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; import gplx.*; import gplx.xowa.*;
import gplx.core.brys.*;
public class Xophp_ary implements Bry_bfr_able {
private final Ordered_hash hash = Ordered_hash_.New();
private int nxt_idx;
public Xophp_ary Add(Object val) {
int key = nxt_idx++;
Xophp_ary_itm itm = Xophp_ary_itm.New(key, val);
hash.Add_if_dupe_use_nth(key, itm);
return this;
}
public Xophp_ary Add(int key, Object val) {
nxt_idx = key + 1;
Xophp_ary_itm itm = Xophp_ary_itm.New(key, val);
hash.Add_if_dupe_use_nth(key, itm);
return this;
}
public Xophp_ary Add(double key, Object val) {
int key_as_int = (int)key;
nxt_idx = key_as_int + 1;
Xophp_ary_itm itm = Xophp_ary_itm.New(key_as_int, val);
hash.Add_if_dupe_use_nth(key_as_int, itm);
return this;
}
public Xophp_ary Add(boolean key, Object val) {
int key_as_int = key ? 1 : 0;
nxt_idx = key_as_int + 1;
Xophp_ary_itm itm = Xophp_ary_itm.New(key_as_int, val);
hash.Add_if_dupe_use_nth(key_as_int, itm);
return this;
}
public Xophp_ary Add(String key, Object val) {
Xophp_ary_itm itm = null;
int key_as_int = Int_.Parse_or(key, Int_.Min_value);
if (key_as_int != Int_.Min_value) {
itm = Xophp_ary_itm.New(key_as_int, val);
nxt_idx = key_as_int + 1;
hash.Add_if_dupe_use_nth(key_as_int, itm);
}
else {
itm = Xophp_ary_itm.New(key, val);
hash.Add_if_dupe_use_nth(key, itm);
}
return this;
}
public Object Get(Object key) {
Xophp_ary_itm itm = (Xophp_ary_itm)hash.Get_by(key);
return itm.Val();
}
public void Unset(Object key) {
hash.Del(key);
}
public boolean Has(Object key) {
return hash.Has(key);
}
public Xophp_ary Values() {
Xophp_ary rv = new Xophp_ary();
int len = hash.Len();
for (int i = 0; i < len; i++) {
Xophp_ary_itm old_itm = (Xophp_ary_itm)hash.Get_at(i);
rv.Add(i, old_itm.Val());
}
return rv;
}
public Xophp_ary_itm[] To_ary() {
return (Xophp_ary_itm[])hash.To_ary(Xophp_ary_itm.class);
}
public void To_bfr(Bry_bfr bfr) {
Xophp_ary_itm[] itms = To_ary();
for (Xophp_ary_itm itm : itms) {
itm.To_bfr(bfr);
}
}
public static Xophp_ary New() {return new Xophp_ary();}
}

View File

@@ -0,0 +1,43 @@
/*
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.*;
import gplx.core.brys.*;
public class Xophp_ary_itm implements Bry_bfr_able {
public Xophp_ary_itm(int key_as_int, String key_as_str, Object val) {
this.key_as_int = key_as_int;
this.key_as_str = key_as_str;
this.val = val;
}
public int Key_as_int() {return key_as_int;} private final int key_as_int;
public String Key_as_str() {return key_as_str;} private final String key_as_str;
public Object Val() {return val;} private final Object val;
public void To_bfr(Bry_bfr bfr) {
String key = key_as_str == null ? Int_.To_str(key_as_int) : key_as_str;
bfr.Add_str_u8(key).Add_byte_eq();
if (Type_.Type_by_obj(val) == Xophp_ary.class) {
Xophp_ary sub_ary = (Xophp_ary)val;
bfr.Add_byte_nl();
sub_ary.To_bfr(bfr);
}
else {
bfr.Add_obj(val);
}
}
public static Xophp_ary_itm New(int key, Object val) {return new Xophp_ary_itm(key, null, val);}
public static Xophp_ary_itm New(String key, Object val) {return new Xophp_ary_itm(-1 , key , val);}
}

View File

@@ -0,0 +1,137 @@
/*
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.*;
import org.junit.*; import gplx.core.tests.*;
public class Xophp_ary_tst { // REF: http://php.net/manual/en/language.types.array.php
private final XophpArray_fxt fxt = new XophpArray_fxt();
@Test public void array__kvs() {
// $array = array("foo" => "bar", "bar" => "foo",);
fxt.Test__array
( Xophp_ary.New()
. Add("foo", "bar")
. Add("bar", "foo")
, Xophp_ary_itm.New("foo", "bar")
, Xophp_ary_itm.New("bar", "foo")
);
}
@Test public void array__casting() {
// $array = array(1 => "a", "1" => "b", 1.5 => "c", true => "d",);
fxt.Test__array
( Xophp_ary.New()
. Add(1 , "a")
. Add("1" , "b")
. Add(1.5 , "c")
. Add(true, "d")
, Xophp_ary_itm.New(1, "d"));
}
@Test public void array__mixed() {
// $array = array("foo" => "bar", "bar" => "foo", 100 => -100, -100 => 100);
fxt.Test__array
( Xophp_ary.New()
. Add("foo", "bar")
. Add("bar", "foo")
. Add(100, -100)
. Add(-100, 100)
, Xophp_ary_itm.New("foo", "bar")
, Xophp_ary_itm.New("bar", "foo")
, Xophp_ary_itm.New(100, -100)
, Xophp_ary_itm.New(-100, 100)
);
}
@Test public void array__objs() {
// $array = array("foo", "bar", "hello", "world");
fxt.Test__array
( Xophp_ary.New()
. Add("foo")
. Add("bar")
. Add("hello")
. Add("world")
, Xophp_ary_itm.New(0, "foo")
, Xophp_ary_itm.New(1, "bar")
, Xophp_ary_itm.New(2, "hello")
, Xophp_ary_itm.New(3, "world")
);
}
@Test public void array__unkeyed() {
// $array = array("a", "b", 6 => "c", "d");
fxt.Test__array
( Xophp_ary.New()
. Add("a")
. Add("b")
. Add(6, "c")
. Add("d")
, Xophp_ary_itm.New(0, "a")
, Xophp_ary_itm.New(1, "b")
, Xophp_ary_itm.New(6, "c")
, Xophp_ary_itm.New(7, "d")
);
}
@Test public void array__multidimensional() {
/*
$array = array(
"foo" => "bar",
42 => 24,
"multi" => array(
"dimensional" => array(
"array" => "foo"
)
)
);
*/
fxt.Test__array
( Xophp_ary.New()
. Add("foo" , "bar")
. Add(42 , 24)
. Add("multi" , Xophp_ary.New()
. Add("dimensional", Xophp_ary.New()
. Add("array", "foo")
))
, Xophp_ary_itm.New("foo", "bar")
, Xophp_ary_itm.New(42, "24")
, Xophp_ary_itm.New("multi", Xophp_ary.New()
. Add("dimensional", Xophp_ary.New()
. Add("array", "foo")
))
);
}
@Test public void array__unset() {
Xophp_ary ary = Xophp_ary.New();
ary.Add(0, "a").Add(1, "b");
// delete all
ary.Unset(0);
ary.Unset(1);
fxt.Test__array(ary);
// add new and assert idx is 2
ary.Add("c");
fxt.Test__array(ary, Xophp_ary_itm.New(2, "c"));
ary = ary.Values();
ary.Add("d");
fxt.Test__array(ary, Xophp_ary_itm.New(0, "c"), Xophp_ary_itm.New(1, "d"));
}
}
class XophpArray_fxt {
public void Test__array(Xophp_ary ary, Xophp_ary_itm... expd) {
Xophp_ary_itm[] actl = ary.To_ary();
Gftest.Eq__ary(expd, actl);
}
public void Test__unset(Xophp_ary ary, int idx, Xophp_ary_itm... expd) {
Xophp_ary_itm[] actl = ary.To_ary();
Gftest.Eq__ary(expd, actl);
}
}

View File

@@ -0,0 +1,226 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.content.*;
public class JCContent extends TextContent { /** @var array */
private Object rawData = null;
/** @var stdClass|array */
protected XophpStdClass data = null;
/** @var Status */
private XomwStatus status;
/** @var boolean */
private boolean thoroughVar;
/** @var JCContentView|null contains an instance of the view class */
// private Object view = null;
/**
* @param String text Json configuration. If null, default content will be inserted instead
* @param String $modelId
* @param boolean thorough True if extra validation should be performed
*/
public void __construct(byte[] text, String modelId, boolean thorough) {
if (text == null) {
// text = this.getView($modelId).getDefault($modelId);
}
super.__construct(text, modelId);
this.thoroughVar = thorough;
this.status = new XomwStatus();
this.parse();
}
/**
* Get validated data
* @return stdClass|stdClass[]
*/
public XophpStdClass getData() {
return this.data;
}
/**
* Returns data after sanitization, suitable for third-party use
*
* @param stdClass|stdClass[] data
* @return stdClass|stdClass[]
*/
public XophpStdClass getSafeData(XophpStdClass data) {
return data;
}
/**
* Returns JSON Object as resulted from parsing initial text,
* before any validation/modifications took place
* @return mixed
*/
public Object getRawData() {
return this.rawData;
}
/**
* Get content status Object
* @return Status
*/
public XomwStatus getStatus() {
return this.status;
}
/**
* @return boolean False if this configuration has parsing or validation errors
*/
public boolean isValid() {
return this.status.isGood();
}
private static final byte[] Bry__ary__empty = Bry_.new_a7("{}");
public boolean isEmpty() {
byte[] text = Bry_.Trim(this.getNativeData());
return Bry_.Len_eq_0(text) || Bry_.Eq(text, Bry__ary__empty);
}
/**
* Determines whether this content should be considered a "page" for statistics
* In our case, just making sure it's not empty or a redirect
* @param boolean $hasLinks
* @return boolean
*/
public boolean isCountable(boolean hasLinks) {
return !this.isEmpty() && !this.isRedirect();
}
/**
* Returns true if the text is in JSON format.
* @return boolean
*/
public boolean isValidJson() {
return this.rawData != null;
}
/**
* @return boolean true if thorough validation may be needed -
* e.g. rendering HTML or saving new value
*/
public boolean thorough() {
return this.thoroughVar;
}
/**
* Override this method to perform additional data validation
* @param mixed data
* @return mixed
*/
public XophpStdClass validate(XophpStdClass data) {
return data;
}
/**
* Perform initial json parsing and validation
*/
private void parse() {
// String rawText = this.getNativeData();
// parseOpts = FormatJson::STRIP_COMMENTS + FormatJson::TRY_FIXING;
// status = FormatJson::parse(rawText, parseOpts);
// if (!status.isOK()) {
// this.status = status;
// return;
// }
// data = status.getValue();
// // @fixme: HACK - need a deep clone of the data
// // @fixme: but doing (Object)(array)data will re-encode empty [] as {}
// // @performance: re-encoding is likely faster than stripping comments in PHP twice
//// this.rawData = FormatJson::decode(
//// FormatJson::encode(data, FormatJson::ALL_OK), true
//// );
// this.data = this.validate(data);
}
// /**
// * Beautifies JSON prior to save.
// * @param Title $title Title
// * @param \User $user User
// * @param \ParserOptions $popts
// * @return JCContent
// */
// public function preSaveTransform(Title $title, \User $user, \ParserOptions $popts) {
// if (!this.isValidJson()) {
// return this; // Invalid JSON - can't do anything with it
// }
// $formatted = FormatJson::encode(this.getData(), false, FormatJson::ALL_OK);
// if (this.getNativeData() !== $formatted) {
// return new static($formatted, this.getModel(), this.thorough());
// }
// return this;
// }
//
// protected function fillParserOutput(Title $title, $revId, ParserOptions $options,
// $generateHtml, ParserOutput &$output) {
// if (!$generateHtml) {
// return;
// }
//
// status = this.getStatus();
// if (!status.isGood()) {
// // Use user's language, and split parser cache. This should not have a big
// // impact because data namespace is rarely viewed, but viewing it localized
// // will be valuable
// $lang = $options.getUserLangObj();
// $html = status.getHTML(false, false, $lang);
// } else {
// $html = '';
// }
//
// if (status.isOK()) {
// $html .= this
// .getView(this.getModel())
// .valueToHtml(this, $title, $revId, $options, $generateHtml, $output);
// }
//
// $output.setText($html);
// }
//
// /**
// * Get a view Object for this content Object
// * @param String $modelId is required here because parent ctor might not have ran yet
// * @return JCContentView
// */
// protected function getView($modelId) {
// global $wgJsonConfigModels;
// view = this.view;
// if (view === null) {
// $configModels = \ExtensionRegistry::getInstance().getAttribute('JsonConfigModels')
// + $wgJsonConfigModels;
// if (array_key_exists($modelId, $configModels)) {
// $value = $configModels[$modelId];
// if (is_array($value) && array_key_exists('view', $value)) {
// $class = $value['view'];
// view = new $class();
// }
// }
// if (view === null) {
// view = this.createDefaultView();
// }
// this.view = view;
// }
// return view;
// }
//
// /**
// * In case view is not associated with the model for this class, this function will instantiate
// * a default. Override may instantiate a more appropriate view
// * @return JCContentView
// */
// protected function createDefaultView() {
// return new JCDefaultContentView();
// }
}

View File

@@ -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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*; import gplx.xowa.mediawiki.includes.content.*;
class JCContentHandler extends TextContentHandler { /**
* Internal format to force pretty-printed json serialization
*/
private static final String CONTENT_FORMAT_JSON_PRETTY = "application/json+pretty";
private JCSingleton singleton;
/**
* @param String $modelId
*/
public void __construct(String modelId, JCSingleton singleton) {
super.__construct(modelId, XomwDefines.CONTENT_FORMAT_JSON, CONTENT_FORMAT_JSON_PRETTY);
this.singleton = singleton;
}
// /**
// * Returns the content's text as-is.
// *
// * @param \Content|JCContent $content This is actually a Content Object
// * @param String|null $format
// * @return mixed
// */
// public function serializeContent(\Content $content, $format = null) {
// this.checkFormat($format);
// $status = $content->getStatus();
// if ($status->isGood()) {
// $data = $content->getData(); // There are no errors, normalize data
// } elseif ($status->isOK()) {
// $data = $content->getRawData(); // JSON is valid, but the data has errors
// } else {
// return $content->getNativeData(); // Invalid JSON - can't do anything with it
// }
//
// return FormatJson::encode($data, $format === self::CONTENT_FORMAT_JSON_PRETTY,
// FormatJson::ALL_OK);
// }
//
// /**
// * @param \Content|JCContent $oldContent
// * @param \Content|JCContent $myContent
// * @param \Content|JCContent $yourContent
// * @return boolean|JCContent
// */
// public function merge3(\Content $oldContent, \Content $myContent, \Content $yourContent) {
// // Almost identical clone of the parent's merge3, except that we use pretty-printed merge,
// // thus allowing much more lenient line-based merging.
//
// this.checkModelID($oldContent->getModel());
// this.checkModelID($myContent->getModel());
// this.checkModelID($yourContent->getModel());
//
// $format = self::CONTENT_FORMAT_JSON_PRETTY;
//
// $old = this.serializeContent($oldContent, $format);
// $mine = this.serializeContent($myContent, $format);
// $yours = this.serializeContent($yourContent, $format);
//
// $ok = wfMerge($old, $mine, $yours, $result);
//
// if (!$ok) {
// return false;
// }
//
// if (!$result) {
// return this.makeEmptyContent();
// }
//
// $mergedContent = this.unserializeContent($result, $format);
//
// return $mergedContent;
// }
//
// /**
// * Returns the name of the diff engine to use.
// *
// * @since 1.21
// *
// * @return String
// */
// protected function getDiffEngineClass() {
// return JCJsonDifferenceEngine::class;
// }
//
/**
* Unserializes a JsonSchemaContent Object.
*
* @param String $text Serialized form of the content
* @param null|String $format The format used for serialization
* @param boolean $isSaving Perform extra validation
* @return JCContent the JsonSchemaContent Object wrapping $text
*/
public JCContent unserializeContent(byte[] text) {return unserializeContent(text, null, true);}
public JCContent unserializeContent(byte[] text, String format, boolean isSaving) {
this.checkFormat(format);
String modelId = this.getModelID();
XophpClassBldr factory = singleton.getContentClass(modelId);
return (JCContent)factory.Make(text, modelId, isSaving);
}
// /**
// * Returns the name of the associated Content class, to
// * be used when creating new objects. Override expected
// * by subclasses.
// *
// * @return String
// */
// protected function getContentClass() {
// $modelId = this.getModelID();
// return JCSingleton::getContentClass($modelId);
// }
//
// /**
// * Creates an empty JsonSchemaContent Object.
// *
// * @return JCContent
// */
// public function makeEmptyContent() {
// // Each model could have its own default JSON value
// // null notifies that default should be used
// return this.unserializeContent(null);
// }
}

View File

@@ -0,0 +1,150 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.mediawiki.*;
import gplx.xowa.langs.*; import gplx.xowa.langs.msgs.*;
public class JCDataContent extends JCObjContent { // /**
// * Derived classes must implement this method to perform custom validation
// * using the check(...) calls
// */
// public function validateContent() {
// if (!$this.thorough()) {
// // We are not doing any modifications to the original, so no need to validate it
// return;
// }
//
// $this.test('license', JCValidators::isStringLine(), self::isValidLicense());
// $this.testOptional('description', [ 'en' => '' ], JCValidators::isLocalizedString());
// $this.testOptional('sources', '', JCValidators::isString());
// }
//
// /** Returns a validator function to check if the value is a valid String
// * @return callable
// */
// public static function isValidLicense() {
// return function (JCValue $v, array $path) {
// global $wgJsonConfigAllowedLicenses, $wgLang;
// if (!in_array($v.getValue(), $wgJsonConfigAllowedLicenses, true)) {
// $v.error('jsonconfig-err-license', $path,
// $wgLang.commaList($wgJsonConfigAllowedLicenses));
// return false;
// }
// return true;
// };
// }
/**
* Get data as localized for the given language
* @param Language $lang
* @return mixed
*/
public XophpStdClass getLocalizedData(Xol_lang_itm lang) {
if (!this.isValid()) {
return null;
}
XophpStdClass result = new XophpStdClass();
this.localizeData(result, lang);
return result;
}
/**
* Resolve @Override any specific localizations, and add it to $result
* @param Object $result
* @param Language $lang
*/
@gplx.Virtual protected void localizeData(XophpStdClass result, Xol_lang_itm lang) {
XophpStdClass data = this.getData();
if (data.Has("description")) {
result.Set_by_as_str("description", JCUtils.pickLocalizedString(data.Get_by_as_itm("description"), lang));
}
XophpStdClass license = this.getLicenseObject();
if (license != null) {
// Xol_msg_itm msg = license.Get_by_as_obj("text");
// String text = msg.inLanguage($lang).plain();
// $result.license = (Object)[
// 'code' => $license['code'],
// 'text' => $text,
// 'url' => $license['url'].inLanguage($lang).plain(),
// ];
}
if (data.Has("sources")) {
result.Set_by_as_itm("sources", data.Get_by_as_itm("sources"));
}
}
//
// public function renderDescription( $lang ) {
// $description = $this->getField( 'description' );
//
// if ( $description && !$description->error() ) {
// $description = JCUtils::pickLocalizedString( $description->getValue(), $lang );
// $html = Html::element( 'p', [ 'class' => 'mw-jsonconfig-description' ], $description );
// } else {
// $html = '';
// }
//
// return $html;
// }
//
// /**
// * Renders license HTML, including optional "or later version" clause
// * <a href="...">Creative Commons 1.0</a>, or later version
// * @return String
// */
// public function renderLicense() {
// $license = $this->getLicenseObject();
// if ( $license ) {
// $text = Html::element( 'a', [
// 'href' => $license['url']->plain()
// ], $license['text']->plain() );
//
// $text = wfMessage( 'jsonconfig-license' )->rawParams( $text )->parse();
//
// $html = Html::rawElement( 'p', [ 'class' => 'mw-jsonconfig-license' ], $text );
// } else {
// $html = '';
// }
//
// return $html;
// }
private XophpStdClass getLicenseObject() {
// XophpStdClass license = this.getField("license");
// if ( $license && !$license->error() ) {
// $code = $license->getValue();
//
// return [
// 'code' => $code,
// 'text' => wfMessage( 'jsonconfig-license-name-' . $code ),
// 'url' => wfMessage( 'jsonconfig-license-url-' . $code ),
// ];
// }
return null;
}
// public function renderSources( Parser $parser, Title $title, $revId, ParserOptions $options ) {
// $sources = $this->getField( 'sources' );
//
// if ( $sources && !$sources->error() ) {
// $markup = $sources->getValue();
// $html = Html::rawElement( 'p', [ 'class' => 'mw-jsonconfig-sources' ],
// $parser->parse( $markup, $title, $options, true, true, $revId )->getRawText() );
// } else {
// $html = '';
// }
//
// return $html;
// }
}

View File

@@ -0,0 +1,566 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.mediawiki.*;
public class JCObjContent extends JCContent { // /**
// * @var boolean if false, prevents multiple fields from having identical names that differ
// * only by casing
// */
// protected $isCaseSensitive = false;
//
// /** @var boolean if false, ensure the root to be an stdClass, otherwise - an array */
// protected $isRootArray = false;
//
// /**
// * @var JCValue contains raw validation results. At first it is a parsed JSON value, with the
// * root element wrapped into JCValue. As validation progresses, all visited values become
// * wrapped with JCValue.
// */
// protected $validationData;
//
// /** @var mixed */
// protected $dataWithDefaults;
//
// /** @var boolean|null validation status - null=before, true=during, false=done */
// protected $isValidating = null;
//
// /**
// * Override default behavior to include defaults if validation succeeded.
// *
// * @return String|boolean The raw text, or false if the conversion failed.
// */
// public function getWikitextForTransclusion() {
// if ( !$this->getStatus()->isGood() ) {
// // If validation failed, return original text
// return parent::getWikitextForTransclusion();
// }
// if ( !$this->thorough() && $this->validationData !== null ) {
// // ensure that data is sorted in the right order
// self::markUnchecked( $this->validationData );
// }
// return \FormatJson::encode( $this->getDataWithDefaults(), true, \FormatJson::ALL_OK );
// }
//
// protected function createDefaultView() {
// return new JCDefaultObjContentView();
// }
//
// /**
// * Get configuration data with custom defaults
// * @throws \Exception in case validation is not complete
// * @return mixed
// */
// public function getDataWithDefaults() {
// if ( $this->isValidating !== false ) {
// throw new Exception( 'This method may only be called after validation is complete' );
// }
// if ( $this->dataWithDefaults === null ) {
// $this->dataWithDefaults = JCUtils::sanitize( $this->validationData );
// }
// return $this->dataWithDefaults;
// }
//
// /**
// * Get status array that recursively describes dataWithDefaults
// * @throws \Exception
// * @return JCValue
// */
// public function getValidationData() {
// if ( $this->isValidating === null ) {
// throw new Exception(
// 'This method may only be called during or after validation has started'
// );
// }
// return $this->validationData;
// }
//
// /**
// * Call this function before performing data validation inside the derived validate()
// * @param array|Object $data
// * @throws \Exception
// * @return boolean if true, validation should be performed, otherwise all checks will be ignored
// */
// protected function initValidation( $data ) {
// if ( $this->isValidating !== null ) {
// throw new Exception( 'This method may only be called before validation has started' );
// }
// $this->isValidating = true;
// if ( !$this->isRootArray && !is_object( $data ) ) {
// $this->getStatus()->fatal( 'jsonconfig-err-root-Object-expected' );
// } elseif ( $this->isRootArray && !is_array( $data ) ) {
// $this->getStatus()->fatal( 'jsonconfig-err-root-array-expected' );
// } else {
// $this->validationData = new JCValue( JCValue::UNCHECKED, $data );
// return true;
// }
// return false;
// }
//
// /**
// * Derived validate() must return the result of this function
// * @throws \Exception
// * @return array
// */
// protected function finishValidation() {
// if ( !$this->getStatus()->isGood() ) {
// return $this->getRawData(); // validation failed, do not modify
// }
// return null; // Data will be filter-cloned on demand inside self::getData()
// }
//
// /**
// * Populate this data on-demand for efficiency
// * @return array
// */
// public function getData() {
// if ( $this->data === null ) {
// $this->data = JCUtils::sanitize( $this->validationData, true );
// }
// return $this->data;
// }
//
// public function validate( $data ) {
// if ( $this->initValidation( $data ) ) {
// $this->validateContent();
// $data = $this->finishValidation();
// }
// if ( $this->thorough() && $this->validationData !== null ) {
// self::markUnchecked( $this->validationData );
// }
// $this->isValidating = false;
// return $data;
// }
//
// /**
// * Derived classes must implement this method to perform custom validation
// * using the test(...) calls
// */
// abstract public function validateContent();
//
// /**
// * Use this function to test a value, or if the value is missing, use the default value.
// * The value will be tested with validator(s) if provided, even if it was the default.
// * @param String|array $path name of the root field to check, or a path to the field in a nested
// * structure. Nested path should be in the form of
// * [ 'field-level1', 'field-level2', ... ]. For example, if client needs to check
// * validity of the 'value1' in the structure {'key':{'sub-key':['value0','value1']}},
// * $field should be set to [ 'key', 'sub-key', 1 ].
// * @param mixed $default value to be used in case field is not found. $default is passed to the
// * validator if validation fails. If validation of the default passes,
// * the value is considered optional.
// * @param callable $validator callback function as defined in JCValidators::run(). More than one
// * validator may be given. If validators are not provided, any value is accepted
// * @return boolean true if ok, false otherwise
// * @throws \Exception if $this->initValidation() was not called.
// */
// public function testOptional( $path, $default, $validator = null ) {
// $vld = self::convertValidators( $validator, func_get_args(), 2 );
// // first validator will replace missing with the default
// array_unshift( $vld, JCValidators::useDefault( $default ) );
// return $this->testInt( $path, $vld );
// }
//
// /**
// * Use this function to test a field in the data. If missing, the validator(s) will receive
// * JCMissing singleton as a value, and it will be up to the validator(s) to accept it or not.
// * @param String|array $path name of the root field to check, or a path to the field in a nested
// * structure. Nested path should be in the form of
// * [ 'field-level1', 'field-level2', ... ]. For example, if client needs to check
// * validity of the 'value1' in the structure {'key':{'sub-key':['value0','value1']}},
// * $field should be set to [ 'key', 'sub-key', 1 ].
// * @param callable $validator callback function as defined in JCValidators::run().
// * More than one validator may be given.
// * If validators are not provided, any value is accepted
// * @throws \Exception
// * @return boolean true if ok, false otherwise
// */
// public function test( $path, $validator /*...*/ ) {
// $vld = self::convertValidators( $validator, func_get_args(), 1 );
// return $this->testInt( $path, $vld );
// }
//
// /**
// * Use this function to test all values inside an array or an Object at a given path.
// * All validators will be called for each of the sub-values. If there is no value
// * at the given $path, or it is not a container, no action will be taken and no errors reported
// * @param String|array $path path to the container field in a nested structure.
// * Nested path should be in the form of [ 'field-level1', 'field-level2', ... ].
// * For example, if client needs to check validity of the 'value1' in the structure
// * {'key':{'sub-key':['value0','value1']}},
// * $field should be set to [ 'key', 'sub-key', 1 ].
// * @param callable $validator callback function as defined in JCValidators::run().
// * More than one validator may be given.
// * If validators are not provided, any value is accepted
// * @throws \Exception
// * @return boolean true if all values tested ok, false otherwise
// */
// public function testEach( $path, $validator = null /*...*/ ) {
// $vld = self::convertValidators( $validator, func_get_args(), 1 );
// $isOk = true;
// $path = (array)$path;
// $containerField = $this->getField( $path );
// if ( $containerField ) {
// $container = $containerField->getValue();
// if ( is_array( $container ) || is_object( $container ) ) {
// $lastIdx = count( $path );
// if ( is_object( $container ) ) {
// $container = get_object_vars( $container );
// }
// foreach ( array_keys( $container ) as $k ) {
// $path[$lastIdx] = $k;
// $isOk &= $this->testInt( $path, $vld );
// }
// }
// }
// return $isOk;
// }
//
// /**
// * @param array|String $path
// * @param array $validators
// * @return boolean
// * @throws \Exception
// */
// private function testInt( $path, $validators ) {
// if ( !$this->getStatus()->isOK() ) {
// return false; // skip all validation in case of a fatal error
// }
// if ( $this->isValidating !== true ) {
// throw new Exception(
// 'This function should only be called inside the validateContent() override'
// );
// }
// return $this->testRecursive( (array)$path, [], $this->validationData, $validators );
// }
//
// /**
// * @param array $path
// * @param array $fldPath For error reporting, path to the current field
// * @param JCValue $jcv
// * @param mixed $validators
// * @throws \Exception
// * @@gplx.Internal protected param JCValue $status
// * @return boolean
// */
// private function testRecursive( array $path, array $fldPath, JCValue $jcv, $validators ) {
// // Go recursively through all fields in path until empty, and validate last
// if ( !$path ) {
// // keep this branch here since we allow validation of the whole Object ($path==[])
// return $this->testValue( $fldPath, $jcv, $validators );
// }
// $fld = array_shift( $path );
// if ( is_array( $jcv->getValue() ) && ctype_digit( $fld ) ) {
// $fld = (int)$fld;
// }
// if ( !is_int( $fld ) && !is_string( $fld ) ) {
// throw new Exception( 'Unexpected field type, only strings and integers are allowed' );
// }
// $fldPath[] = $fld;
//
// $subJcv = $this->getField( $fld, $jcv );
// if ( $subJcv === null ) {
// $msg =
// is_int( $fld ) && !is_array( $jcv->getValue() ) ? 'jsonconfig-err-array-expected'
// : 'jsonconfig-err-Object-expected';
// $this->addValidationError( wfMessage( $msg, JCUtils::fieldPathToString( $fldPath ) ) );
// return false;
// }
//
// /** @var boolean $reposition - should the field be deleted and re-added at the end
// * this is only needed for viewing and saving */
// $reposition = $this->thorough() && is_string( $fld ) && $subJcv !== false;
// if ( $subJcv === false || $subJcv->isUnchecked() ) {
// // We never went down this path before
// // Check that field exists, and is not case-duplicated
// if ( is_int( $fld ) ) {
// if ( count( $jcv->getValue() ) < $fld ) {
// // Allow existing index or index+1 for appending last item
// throw new Exception( "List index is too large at '" .
// JCUtils::fieldPathToString( $fldPath ) .
// "'. Index may not exceed list size." );
// }
// } elseif ( !$this->isCaseSensitive ) {
// // if we didn't find it before, it could have been misnamed
// $norm = $this->normalizeField( $jcv, $fld, $fldPath );
// if ( $norm === null ) {
// return false;
// } elseif ( $norm ) {
// $subJcv = $this->getField( $fld, $jcv );
// $reposition = false; // normalization already does that
// }
// }
// if ( $subJcv === null ) {
// throw new Exception( 'Logic error - subJcv must be valid here' );
// } elseif ( $subJcv === false ) {
// // field does not exist
// $initValue = !$path ? null : ( is_string( $path[0] ) ? new stdClass() : [] );
// $subJcv = new JCValue( JCValue::MISSING, $initValue );
// }
// }
// $isOk = $this->testRecursive( $path, $fldPath, $subJcv, $validators );
//
// // Always remove and re-append the field
// if ( $subJcv->isMissing() ) {
// $jcv->deleteField( $fld );
// } else {
// if ( $reposition ) {
// $jcv->deleteField( $fld );
// }
// $jcv->setField( $fld, $subJcv );
// if ( $jcv->isMissing() || $jcv->isUnchecked() ) {
// $jcv->status( JCValue::VISITED );
// }
// }
// return $isOk;
// }
//
// /**
// * @param array $fldPath
// * @param JCValue $jcv
// * @param array $validators
// * @return boolean
// */
// private function testValue( array $fldPath, JCValue $jcv, $validators ) {
// // We have reached the last level of the path, test the actual value
// if ( $validators !== null ) {
// $isRequired = $jcv->defaultUsed();
// JCValidators::run( $validators, $jcv, $fldPath, $this );
// $err = $jcv->error();
// if ( $err ) {
// if ( is_object( $err ) ) {
// // if ( !$isRequired ) {
// // // User supplied value, so we don't know if the value is required or not
// // // if $default passes validation, original value was optional
// // $isRequired = !JCValidators::run(
// // $validators, $fldPath, JCValue::getMissing(), $this
// // );
// // }
// $this->addValidationError( $err, !$isRequired );
// }
// return false;
// } elseif ( $jcv->isUnchecked() ) {
// $jcv->status( JCValue::CHECKED );
// }
// }
// // if ( $this->thorough() && $jcv->status() === JCValue::CHECKED ) {
// // // Check if the value is the same as default - use a cast to array
// // // hack to compare objects
// // $isRequired = (boolean)JCValidators::run( $validators, $fldPath, JCMissing::get(), $this );
// // if ( ( is_object( $jcv ) && is_object( $default ) && (array)$jcv === (array)$default )
// // || ( !is_object( $default ) && $jcv === $default )
// // ) {
// // $newStatus = JCValue::SAME_AS_DEFAULT;
// // }
// // }
// return true;
// }
//
// /**
// * Recursively reorder all sub-elements - checked first, followed by unchecked.
// * Also, convert all sub-elements to JCValue(UNCHECKED) if at least one of them was JCValue
// * This is useful for HTML rendering to indicate unchecked items
// * @param JCValue $data
// */
// private static function markUnchecked( JCValue $data ) {
// $val = $data->getValue();
// $isObject = is_object( $val );
// if ( !$isObject && !is_array( $val ) ) {
// return;
// }
// $result = null;
// $firstPass = true;
// $hasJcv = false;
// // Two pass loop - first pass moves all checked values to the result,
// // second pass moves the rest of of the values, possibly converting them to JCValue
// while ( true ) {
// foreach ( $val as $key => $subVal ) {
// /** @var JCValue|mixed $subVal */
// $isJcv = is_a( $subVal, '\JsonConfig\JCValue' );
// if ( $firstPass && $isJcv ) {
// // On the first pass, recursively process subelements if they were visited
// self::markUnchecked( $subVal );
// $move = $isObject && !$subVal->isUnchecked();
// $hasJcv = true;
// } else {
// $move = false;
// }
// if ( $move || !$firstPass ) {
// if ( !$isJcv ) {
// $subVal = new JCValue( JCValue::UNCHECKED, $subVal );
// }
// if ( $result === null ) {
// $result = $isObject ? new stdClass() : [];
// }
// if ( $isObject ) {
// $result->$key = $subVal;
// unset( $val->$key );
// } else {
// // No need to unset - all values in an array are moved in the second pass
// $result[] = $subVal;
// }
// }
// }
//
// if ( ( $result === null && !$hasJcv ) || !$firstPass ) {
// // either nothing was found, or we are done with the second pass
// if ( $result !== null ) {
// $data->setValue( $result );
// }
// return;
// }
// $firstPass = false;
// }
// }
//
// /**
// * @param Message $error
// * @param boolean $isOptional
// */
// public function addValidationError( Message $error, $isOptional = false ) {
// $text = $error->plain();
// // @TODO fixme - need to re-enable optional field detection & reporting
// // if ( $isOptional ) {
// // $text .= ' ' . wfMessage( 'jsonconfig-optional-field' )->plain();
// // }
// $this->getStatus()->error( $text );
// }
/** Get field from data Object/array
* @param String|int|array $field
* @param stdClass|array|JCValue $data
* @throws \Exception
* @return false|null|JCValue search result:
* false if not found
* null if error (argument type does not match storage)
* JCValue if the value is found
*/
public JCValue getField(int field) {return getFieldWkr(field, null, null, null);}
public JCValue getField(int field, Object data) {return getFieldWkr(field, null, null, data);}
public JCValue getField(String field) {return getFieldWkr(-1, field, null, null);}
public JCValue getField(String field, Object data) {return getFieldWkr(-1, field, null, data);}
public JCValue getField(String[] fields) {return getFieldWkr(-1, null, fields, null);}
public JCValue getField(String[] fields, Object data) {return getFieldWkr(-1, null, fields, data);}
public JCValue getFieldWkr(int fldInt, String fldStr, String[] fldAry, Object data) {
if (data == null) {
// data = this.getValidationData();
}
if (fldAry == null) {
data = getFieldByItem(fldInt, fldStr, data);
if (data == null)
return null;
}
else {
for (String fld : fldAry) {
data = getFieldByItem(-1, fld, data);
if (data == null)
return null;
}
}
if (Type_.Eq_by_obj(data, JCValue.class)) {
return (JCValue)data;
} else {
// return new JCValue(JCValue.UNCHECKED, data);
return null;
}
}
private Object getFieldByItem(int fldInt, String fldStr, Object data) {
if (fldInt == -1 && fldStr == null) {
throw Err_.new_wo_type("Field must be either int or String");
}
if (Type_.Eq_by_obj(data, JCValue.class)) {
data = ((JCValue)data).getValue();
}
int typeId = XomwTypeUtl.To_type_id(data);
boolean isObject = typeId == Type_ids_.Id__obj;
boolean isArray = typeId == Type_ids_.Id__array;
if (fldStr != null ? !(isObject || isArray) : !isArray) {
return null;
}
if (isObject) {
XophpStdClass dataAsMap = (XophpStdClass)data;
return dataAsMap.Get_by_as_itm(fldStr);
} else if (isArray) {
Object dataAsAry = Array_.cast(data);
if (fldInt < Array_.Len(dataAsAry))
return Array_.Get_at(dataAsAry, fldInt);
}
return null;
}
// /**
// * @param JCValue $jcv
// * @param int|String $fld
// * @param array $fldPath
// * @throws \Exception
// * @return boolean|null true if renamed, false if not found or original unchanged,
// * null if duplicate (error)
// */
// private function normalizeField( JCValue $jcv, $fld, array $fldPath ) {
// $valueRef = $jcv->getValue();
// $foundFld = false;
// $isError = false;
// foreach ( $valueRef as $k => $v ) {
// if ( 0 === strcasecmp( $k, $fld ) ) {
// if ( $foundFld !== false ) {
// $isError = true;
// break;
// }
// $foundFld = $k;
// }
// }
// if ( $isError ) {
// $this->addValidationError( wfMessage( 'jsonconfig-duplicate-field',
// JCUtils::fieldPathToString( $fldPath ) ) );
// if ( $this->thorough() ) {
// // Mark all duplicate fields as errors
// foreach ( $valueRef as $k => $v ) {
// if ( 0 === strcasecmp( $k, $fld ) ) {
// if ( !is_a( $v, '\JsonConfig\JCValue' ) ) {
// $v = new JCValue( JCValue::UNCHECKED, $v );
// $jcv->setField( $k, $v );
// }
// $v->error( true );
// }
// }
// }
// return null;
// } elseif ( $foundFld !== false && $foundFld !== $fld ) {
// // key had different casing, rename it to canonical
// $jcv->setField( $fld, $jcv->deleteField( $foundFld ) );
// return true;
// }
// return false;
// }
//
// /**
// * @param null|callable|array $param first validator parameter
// * @param array $funcArgs result of func_get_args() call
// * @param int $skipArgs how many non-validator arguments to remove
// * from the beginning of the $funcArgs
// * @return array of validators
// */
// private static function convertValidators( $param, $funcArgs, $skipArgs ) {
// if ( $param === null ) {
// return []; // no validators given
// } elseif ( is_array( $param ) && !is_callable( $param, true ) ) {
// return $param; // first argument is an array of validators
// } else {
// return array_slice( $funcArgs, $skipArgs ); // remove fixed params from the beginning
// }
// }
}

View File

@@ -0,0 +1,995 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
public class JCSingleton {
// /**
// * @var array describes how a title should be handled by JsonConfig extension.
// * The structure is an array of array of ...:
// * { int_namespace => { name => { allows-sub-namespaces => configuration_array } } }
// */
// public static $titleMap = [];
//
// /**
// * @var String[]|false[] containing all the namespaces handled by JsonConfig
// * Maps namespace id (int) => namespace name (String).
// * If false, presumes the namespace has been registered by core or another extension
// */
// public static $namespaces = [];
//
// /**
// * @var MapCacheLRU[] contains a cache of recently resolved JCTitle's
// * as namespace => MapCacheLRU
// */
// public static $titleMapCacheLru = [];
/**
* @var MapCacheLRU[] contains a cache of recently requested content objects
* as namespace => MapCacheLRU
*/
private final Ordered_hash mapCacheLru = Ordered_hash_.New();
public Xomw_page_fetcher Store() {return store;} public void Store_(Xomw_page_fetcher v) {this.store = v;} private Xomw_page_fetcher store;
public Xophp_ary ConfigModels() {return configModels;} private final Xophp_ary configModels = new Xophp_ary();
// /**
// * @var TitleParser cached invariant title parser
// */
// public static $titleParser;
//
// /**
// * Initializes singleton state by parsing $wgJsonConfig* values
// * @throws Exception
// */
// private static function init() {
// static $isInitialized = false;
// if ($isInitialized) {
// return;
// }
// $isInitialized = true;
// global $wgNamespaceContentModels, $wgContentHandlers, $wgJsonConfigs, $wgJsonConfigModels;
// list(self::$titleMap, self::$namespaces) = self::parseConfiguration(
// $wgNamespaceContentModels,
// $wgContentHandlers,
// array_replace_recursive(
// \ExtensionRegistry::getInstance()->getAttribute('JsonConfigs'), $wgJsonConfigs
// ),
// array_replace_recursive(
// \ExtensionRegistry::getInstance()->getAttribute('JsonConfigModels'),
// $wgJsonConfigModels
// )
// );
// }
//
// /**
// * @param array $namespaceContentModels $wgNamespaceContentModels
// * @param array $contentHandlers $wgContentHandlers
// * @param array $configs $wgJsonConfigs
// * @param array $models $wgJsonConfigModels
// * @param boolean $warn if true, calls wfLogWarning() for all errors
// * @return array [ $titleMap, $namespaces ]
// */
// public static function parseConfiguration(
// array $namespaceContentModels, array $contentHandlers,
// array $configs, array $models, $warn = true
// ) {
// $defaultModelId = 'JsonConfig';
// // @codingStandardsIgnoreStart - T154789
// $warnFunc = $warn ? 'wfLogWarning' : function() {};
// // @codingStandardsIgnoreEnd
//
// $namespaces = [];
// $titleMap = [];
// foreach ($configs as $confId => &$conf) {
// if (!is_string($confId)) {
// $warnFunc(
// "JsonConfig: Invalid \$wgJsonConfigs['$confId'], the key must be a String"
// );
// continue;
// }
// if (null == self::getConfObject($warnFunc, $conf, $confId)) {
// continue; // warned inside the function
// }
//
// $modelId = property_exists($conf, 'model')
// ? ($conf->model ? : $defaultModelId) : $confId;
// if (!array_key_exists($modelId, $models)) {
// if ($modelId == $defaultModelId) {
// $models[$defaultModelId] = null;
// } else {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs['$confId']: " .
// "Model '$modelId' is not defined in \$wgJsonConfigModels");
// continue;
// }
// }
// if (array_key_exists($modelId, $contentHandlers)) {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs['$confId']: Model '$modelId' is " .
// "already registered in \$contentHandlers to {$contentHandlers[$modelId]}");
// continue;
// }
// $conf->model = $modelId;
//
// $ns = self::getConfVal($conf, 'namespace', NS_CONFIG);
// if (!is_int($ns) || $ns % 2 !== 0) {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs['$confId']: " .
// "Namespace $ns should be an even number");
// continue;
// }
// // Even though we might be able to override default content model for namespace,
// // lets keep things clean
// if (array_key_exists($ns, $namespaceContentModels)) {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs['$confId']: Namespace $ns is " .
// "already set to handle model '$namespaceContentModels[$ns]'");
// continue;
// }
//
// // nsName & nsTalk are handled later
// self::getConfVal($conf, 'pattern', '');
// self::getConfVal($conf, 'cacheExp', 24 * 60 * 60);
// self::getConfVal($conf, 'cacheKey', '');
// self::getConfVal($conf, 'flaggedRevs', false);
// self::getConfVal($conf, 'license', false);
// $islocal = self::getConfVal($conf, 'isLocal', true);
//
// // Decide if matching configs should be stored on this wiki
// $storeHere = $islocal || property_exists($conf, 'store');
// if (!$storeHere) {
// // 'store' does not exist, use it as a flag to indicate remote storage
// $conf->store = false;
// $remote = self::getConfObject($warnFunc, $conf, 'remote', $confId, 'url');
// if (null == $remote) {
// continue; // warned inside the function
// }
// if (self::getConfVal($remote, 'url', '') == '') {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs['$confId']['remote']['url']: " .
// "API URL is not set, and this config is not being stored locally");
// continue;
// }
// self::getConfVal($remote, 'username', '');
// self::getConfVal($remote, 'password', '');
// } else {
// if (property_exists($conf, 'remote')) {
// // non-fatal -- simply ignore the 'remote' setting
// $warnFunc("JsonConfig: In \$wgJsonConfigs['$confId']['remote'] is set for " .
// "the config that will be stored on this wiki. " .
// "'remote' parameter will be ignored."
// );
// }
// $conf->remote = null;
// $store = self::getConfObject($warnFunc, $conf, 'store', $confId);
// if (null == $store) {
// continue; // warned inside the function
// }
// self::getConfVal($store, 'cacheNewValue', true);
// self::getConfVal($store, 'notifyUrl', '');
// self::getConfVal($store, 'notifyUsername', '');
// self::getConfVal($store, 'notifyPassword', '');
// }
//
// // Too lazy to write proper error messages for all parameters.
// if ((isset($conf->nsTalk) && !is_string($conf->nsTalk)) ||
// !is_string($conf->pattern) ||
// !is_bool($islocal) || !is_int($conf->cacheExp) || !is_string($conf->cacheKey)
// || !is_bool($conf->flaggedRevs)
// ) {
// $warnFunc("JsonConfig: Invalid type of one of the parameters in " .
// "\$wgJsonConfigs['$confId'], please check documentation");
// continue;
// }
// if (isset($remote)) {
// if (!is_string($remote->url) || !is_string($remote->username) ||
// !is_string($remote->password)
// ) {
// $warnFunc("JsonConfig: Invalid type of one of the parameters in " .
// "\$wgJsonConfigs['$confId']['remote'], please check documentation");
// continue;
// }
// }
// if (isset($store)) {
// if (!is_bool($store->cacheNewValue) || !is_string($store->notifyUrl) ||
// !is_string($store->notifyUsername) || !is_string($store->notifyPassword)
// ) {
// $warnFunc("JsonConfig: Invalid type of one of the parameters in " .
// " \$wgJsonConfigs['$confId']['store'], please check documentation");
// continue;
// }
// }
// if ($storeHere) {
// // If nsName is given, add it to the list, together with the talk page
// // Otherwise, create a placeholder for it
// if (property_exists($conf, 'nsName')) {
// if ($conf->nsName == false) {
// // Non JC-specific namespace, don't register it
// if (!array_key_exists($ns, $namespaces)) {
// $namespaces[$ns] = false;
// }
// } elseif ($ns == NS_CONFIG) {
// $warnFunc("JsonConfig: Parameter 'nsName' in \$wgJsonConfigs['$confId'] " .
// "is not supported for namespace == NS_CONFIG ($ns)");
// } else {
// $nsName = $conf->nsName;
// $nsTalk = isset($conf->nsTalk) ? $conf->nsTalk : ($nsName . '_talk');
// if (!is_string($nsName) || $nsName == '') {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs['$confId']: " .
// "if given, nsName must be a String");
// continue;
// } elseif (array_key_exists($ns, $namespaces) &&
// $namespaces[$ns] !== null
// ) {
// if ($namespaces[$ns] !== $nsName ||
// $namespaces[$ns + 1] !== $nsTalk
// ) {
// $warnFunc("JsonConfig: \$wgJsonConfigs['$confId'] - " .
// "nsName has already been set for namespace $ns");
// }
// } else {
// $namespaces[$ns] = $nsName;
// $namespaces[$ns + 1] =
// isset($conf->nsTalk) ? $conf->nsTalk : ($nsName . '_talk');
// }
// }
// } elseif (!array_key_exists($ns, $namespaces) || $namespaces[$ns] == false) {
// $namespaces[$ns] = null;
// }
// }
//
// if (!array_key_exists($ns, $titleMap)) {
// $titleMap[$ns] = [ $conf ];
// } else {
// $titleMap[$ns][] = $conf;
// }
// }
//
// // Add all undeclared namespaces
// $missingNs = 1;
// foreach ($namespaces as $ns => $nsName) {
// if ($nsName == null) {
// $nsName = 'Config';
// if ($ns !== NS_CONFIG) {
// $nsName .= $missingNs;
// $warnFunc(
// "JsonConfig: Namespace $ns does not have 'nsName' defined, using '$nsName'"
// );
// $missingNs += 1;
// }
// $namespaces[$ns] = $nsName;
// $namespaces[$ns + 1] = $nsName . '_talk';
// }
// }
//
// return [ $titleMap, $namespaces ];
// }
//
// /**
// * Helper function to check if configuration has a field set, and if not, set it to default
// * @param stdClass $conf
// * @param String $field
// * @param mixed $default
// * @return mixed
// */
// private static function getConfVal(& $conf, $field, $default) {
// if (property_exists($conf, $field)) {
// return $conf->$field;
// }
// $conf->$field = $default;
// return $default;
// }
//
// /**
// * Helper function to check if configuration has a field set, and if not, set it to default
// * @param $warnFunc
// * @param $value
// * @param String $field
// * @param String $confId
// * @param String $treatAsField
// * @return null|Object|stdClass
// */
// private static function getConfObject(
// $warnFunc, & $value, $field, $confId = null, $treatAsField = null
// ) {
// if (!$confId) {
// $val = & $value;
// } else {
// if (!property_exists($value, $field)) {
// $value->$field = null;
// }
// $val = & $value->$field;
// }
// if ($val == null || $val == true) {
// $val = new stdClass();
// } elseif (is_array($val)) {
// $val = (Object)$val;
// } elseif (is_string($val) && $treatAsField !== null) {
// // treating this String value as a sub-field
// $val = (Object)[ $treatAsField => $val ];
// } elseif (!is_object($val)) {
// $warnFunc("JsonConfig: Invalid \$wgJsonConfigs" . ($confId ? "['$confId']" : "") .
// "['$field'], the value must be either an array or an Object");
// return null;
// }
// return $val;
// }
/**
* Get content Object from the local LRU cache, or null if doesn't exist
* @param TitleValue $titleValue
* @return null|JCContent
*/
public JCContent getContentFromLocalCache(String wiki, String ns, String page) {
// Some of the titleValues are remote, and their namespace might not be declared
// in the current wiki. Since TitleValue is a content Object, it does not validate
// the existence of namespace, hence we use it as a simple storage.
// Producing an artificial String key by appending (namespaceID . ':' . titleDbKey)
// seems wasteful and redundant, plus most of the time there will be just a single
// namespace declared, so this structure seems efficient and easy enough.
String mapCacheLruKey = String_.Concat(wiki, "|", ns);
Ordered_hash cache = (Ordered_hash)mapCacheLru.Get_by(mapCacheLruKey);
if (cache == null) {
cache = Ordered_hash_.New();
mapCacheLru.Add(mapCacheLruKey, cache);
}
return (JCContent)cache.Get_by(page);
}
/**
* Get content Object for the given title.
* Namespace ID does not need to be defined in the current wiki,
* as long as it is defined in $wgJsonConfigs.
* @param TitleValue|JCTitle $titleValue
* @return boolean|JCContent Returns false if the title is not handled by the settings
*/
public JCContent getContent(String wiki, String ns, String page) {
JCContent content = getContentFromLocalCache(wiki, ns, page);
if (content == null) {
byte[] content_bry = store.Get_wtxt(Bry_.new_u8(wiki), Bry_.new_u8(page));
if (content_bry != null) {
JCContentHandler handler = new JCContentHandler();
handler.__construct(JCTabularContent.Model_id, this);
content = handler.unserializeContent(content_bry, null, false);
}
// $jct = self::parseTitle($titleValue);
// if ($jct) {
// $store = new JCCache($jct);
// $content = $store->get();
// if (is_string($content)) {
// // Convert String to the content Object if needed
// $handler = new JCContentHandler($jct->getConfig()->model);
// $content = $handler->unserializeContent($content, null, false);
// }
// } else {
// $content = false;
// }
// self::mapCacheLru[$titleValue->getNamespace()]
// ->set($titleValue->getDBkey(), $content);
}
return content;
}
// /**
// * Parse json text into a content Object for the given title.
// * Namespace ID does not need to be defined in the current wiki,
// * as long as it is defined in $wgJsonConfigs.
// * @param TitleValue $titleValue
// * @param String $jsonText json content
// * @param boolean $isSaving if true, performs extensive validation during unserialization
// * @return boolean|JCContent Returns false if the title is not handled by the settings
// * @throws Exception
// */
// public static function parseContent(TitleValue $titleValue, $jsonText, $isSaving = false) {
// $jct = self::parseTitle($titleValue);
// if ($jct) {
// $handler = new JCContentHandler($jct->getConfig()->model);
// return $handler->unserializeContent($jsonText, null, $isSaving);
// }
//
// return false;
// }
//
// /**
// * Mostly for debugging purposes, this function returns initialized @gplx.Internal protected JsonConfig settings
// * @return array[] map of namespaceIDs to list of configurations
// */
// public static function getTitleMap() {
// self::init();
// return self::$titleMap;
// }
/**
* Get the name of the class for a given content model
* @param String $modelId
* @return null|String
*/
public XophpClassBldr getContentClass(String modelId) {
// global $wgJsonConfigModels;
// $configModels = array_replace_recursive(
// \ExtensionRegistry::getInstance()->getAttribute('JsonConfigModels'),
// $wgJsonConfigModels
// );
String clz = null;
if (configModels.Has(modelId)) {
Object val = configModels.Get(modelId);
if (Type_.Type_by_obj(val) == Xophp_ary.class) {
Xophp_ary val_as_ary = (Xophp_ary)val;
if (val_as_ary.Has("class")) {
Gfo_usr_dlg_.Instance.Warn_many("", "", "JsonConfig: Invalid +$wgJsonConfigModels['modelId'] array " + "value, 'cl"+ "ass' not found");
} else {
clz = (String)val_as_ary.Get("class");
}
} else {
clz = (String)val;
}
}
if (clz == null) {
clz = "JCContent"; // __NAMESPACE__ . '\JCContent';
}
return XophpEnv.Instance.ClassBldrs().Get_by_or_null(clz);
}
// /**
// * Given a title (either a user-given String, or as an Object), return JCTitle
// * @param Title|TitleValue|String $value
// * @param int|null $namespace Only used when title is a String
// * @return JCTitle|null|false false if unrecognized namespace,
// * and null if namespace is handled but does not match this title
// * @throws Exception
// */
// public static function parseTitle($value, $namespace = null) {
// if ($value == null || $value == '' || $value == false) {
// // In some weird cases $value is null
// return false;
// } elseif ($value instanceof JCTitle) {
// // Nothing to do
// return $value;
// } elseif ($namespace !== null && !is_integer($namespace)) {
// throw new Exception('$namespace parameter must be either null or an integer');
// }
//
// // figure out the namespace ID (int) - we don't need to parse the String if ns is unknown
// if ($value instanceof LinkTarget) {
// if ($namespace == null) {
// $namespace = $value->getNamespace();
// }
// } elseif (is_string($value)) {
// if ($namespace == null) {
// throw new Exception('$namespace parameter is missing for String $value');
// }
// } else {
// wfLogWarning('Unexpected title param type ' . gettype($value));
// return false;
// }
//
// // Search title map for the matching configuration
// $map = self::getTitleMap();
// if (array_key_exists($namespace, $map)) {
// // Get appropriate LRU cache Object
// if (!array_key_exists($namespace, self::$titleMapCacheLru)) {
// self::$titleMapCacheLru[$namespace] = $cache = new MapCacheLRU(20);
// } else {
// $cache = self::$titleMapCacheLru[$namespace];
// }
//
// // Parse String if needed
// // TODO: should the String parsing also be cached?
// if (is_string($value)) {
// $language = Language::factory('en');
// if (!self::$titleParser) {
// self::$titleParser =
// new MediaWikiTitleCodec(
// $language,
// new GenderCache(),
// [],
// new FauxInterwikiLookup());
// }
// // Interwiki prefixes are a special case for title parsing:
// // first letter is not capitalized, namespaces are not resolved, etc.
// // So we prepend an interwiki prefix to fool title codec, and later remove it.
// try {
// $value = FauxInterwikiLookup::INTERWIKI_PREFIX . ':' . $value;
// $parts = self::$titleParser->splitTitleString($value);
//
// // Defensive coding - ensure the parsing has proceeded as expected
// if ($parts['dbkey'] == '' || $parts['namespace'] !== 0 ||
// $parts['fragment'] !== '' || $parts['local_interwiki'] !== false ||
// $parts['interwiki'] !== FauxInterwikiLookup::INTERWIKI_PREFIX
// ) {
// return null;
// }
// } catch (MalformedTitleException $e) {
// return null;
// }
//
// // At this point, only support wiki namespaces that capitalize title's first char,
// // but do not enable sub-pages.
// // This way data can already be stored on Mediawiki namespace everywhere, or
// // places like commons and zerowiki.
// // Another implicit limitation: there might be an issue if data is stored on a wiki
// // with the non-default ucfirst(), e.g. az, kaa, kk, tr -- they convert "i" to "I"
// $dbKey = $language->ucfirst($parts['dbkey']);
// } else {
// $dbKey = $value->getDBkey();
// }
//
// // A bit weird here: cache will store JCTitle objects or false if the namespace
// // is known to JsonConfig but the dbkey does not match. But in case the title is not
// // handled, this function returns null instead of false if the namespace is known,
// // and false otherwise
// $result = $cache->get($dbKey);
// if ($result == null) {
// $result = false;
// foreach ($map[$namespace] as $conf) {
// $re = $conf->pattern;
// if (!$re || preg_match($re, $dbKey)) {
// $result = new JCTitle($namespace, $dbKey, $conf);
// break;
// }
// }
//
// $cache->set($dbKey, $result);
// }
//
// // return null if the given namespace is mentioned in the config,
// // but title doesn't match
// return $result ?: null;
//
// } else {
// // return false if JC doesn't know anything about this namespace
// return false;
// }
// }
//
// /**
// * Returns an array with settings if the $titleValue Object is handled by the JsonConfig
// * extension, false if unrecognized namespace,
// * and null if namespace is handled but not this title
// * @param TitleValue $titleValue
// * @return stdClass|false|null
// * @deprecated use JCSingleton::parseTitle() instead
// */
// public static function getMetadata($titleValue) {
// $jct = self::parseTitle($titleValue);
// return $jct ? $jct->getConfig() : $jct;
// }
//
// /**
// * Only register NS_CONFIG if running on the MediaWiki instance which houses
// * the JSON configs (i.e. META)
// * @TODO FIXME: Always return true
// * @param array &$namespaces
// * @return true|void
// */
// public static function onCanonicalNamespaces(array &$namespaces) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// self::init();
// foreach (self::$namespaces as $ns => $name) {
// if ($name == false) { // must be already declared
// if (!array_key_exists($ns, $namespaces)) {
// wfLogWarning("JsonConfig: Invalid \$wgJsonConfigs: Namespace $ns " .
// "has not been declared by core or other extensions");
// }
// } elseif (array_key_exists($ns, $namespaces)) {
// wfLogWarning("JsonConfig: Invalid \$wgJsonConfigs: Namespace $ns => '$name' " .
// "is already declared as '$namespaces[$ns]'");
// } else {
// $key = array_search($name, $namespaces);
// if ($key !== false) {
// wfLogWarning("JsonConfig: Invalid \$wgJsonConfigs: Namespace $ns => '$name' " .
// "has identical name with the namespace #$key");
// } else {
// $namespaces[$ns] = $name;
// }
// }
// }
// }
//
// /**
// * Initialize state
// * @param Title $title
// * @param String &$modelId
// * @return boolean
// */
// public static function onContentHandlerDefaultModelFor($title, &$modelId) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// $jct = self::parseTitle($title);
// if ($jct) {
// $modelId = $jct->getConfig()->model;
// return false;
// }
// return true;
// }
//
// /**
// * Instantiate JCContentHandler if we can handle this modelId
// * @param String $modelId
// * @param \ContentHandler &$handler
// * @return boolean
// */
// public static function onContentHandlerForModelID($modelId, &$handler) {
// global $wgJsonConfigModels;
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// self::init();
// $models = array_replace_recursive(
// \ExtensionRegistry::getInstance()->getAttribute('JsonConfigModels'),
// $wgJsonConfigModels
// );
// if (array_key_exists($modelId, $models)) {
// // This is one of our model IDs
// $handler = new JCContentHandler($modelId);
// return false;
// }
// return true;
// }
//
// /**
// * CustomEditor hook handler
// * @see https://www.mediawiki.org/wiki/Manual:Hooks/CustomEditor
// *
// * @param Article $article
// * @param User $user
// * @return boolean
// */
// public static function onCustomEditor($article, $user) {
// if (!$article || !self::jsonConfigIsStorage()) {
// return true;
// }
// $jct = self::parseTitle($article->getTitle());
// if (!$jct) {
// return true;
// }
//
// $editor = new \EditPage($article);
// $editor->contentFormat = JCContentHandler::CONTENT_FORMAT_JSON_PRETTY;
// $editor->edit();
//
// return false;
// }
//
// /**
// * Declares JSON as the code editor language for Config: pages.
// * This hook only runs if the CodeEditor extension is enabled.
// * @param Title $title
// * @param String &$lang Page language.
// * @return boolean
// */
// public static function onCodeEditorGetPageLanguage($title, &$lang) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// // todo/fixme? We should probably add 'json' lang to only those pages that pass parseTitle()
// $handler = ContentHandler::getForModelID($title->getContentModel());
// if ($handler->getDefaultFormat() == CONTENT_FORMAT_JSON || self::parseTitle($title)) {
// $lang = 'json';
// }
// return true;
// }
//
// /**
// * Validates that the revised contents are valid JSON.
// * If not valid, rejects edit with error message.
// * @param \IContextSource $context
// * @param JCContent $content
// * @param \Status $status
// * @param String $summary Edit summary provided for edit.
// * @param \User $user
// * @param boolean $minoredit
// * @return boolean
// */
// public static function onEditFilterMergedContent(
// /** @noinspection PhpUnusedParameterInspection */
// $context, $content, $status, $summary, $user, $minoredit
// ) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// if (is_a($content, 'JsonConfig\JCContent')) {
// $status->merge($content->getStatus());
// if (!$status->isGood()) {
// $status->setResult(false, $status->getValue());
// }
// }
// return true;
// }
//
// /**
// * Override a per-page specific edit page copyright warning
// *
// * @param Title $title
// * @param String[] &$msg
// *
// * @return boolean
// */
// public static function onEditPageCopyrightWarning($title, &$msg) {
// if (self::jsonConfigIsStorage()) {
// $jct = self::parseTitle($title);
// if ($jct) {
// $code = $jct->getConfig()->license;
// if ($code) {
// $msg = [ 'jsonconfig-license-copyrightwarning-' . $code ];
// return false; // Do not allow any other hook handler to override this
// }
// }
// }
// return true;
// }
//
// /**
// * Display a page-specific edit notice
// *
// * @param Title $title
// * @param int $oldid
// * @param array &$notices
// * @return boolean
// */
// public static function onTitleGetEditNotices(Title $title, $oldid, array &$notices) {
// if (self::jsonConfigIsStorage()) {
// $jct = self::parseTitle($title);
// if ($jct) {
// $code = $jct->getConfig()->license;
// if ($code) {
// $noticeText = wfMessage('jsonconfig-license-notice-' . $code)->parse();
// $notices['jsonconfig'] =
// wfMessage('jsonconfig-license-notice-box-' . $code)
// ->rawParams($noticeText)
// ->parseAsBlock();
// }
// }
// }
// return true;
// }
//
// /**
// * Override with per-page specific copyright message
// *
// * @param Title $title
// * @param String $type
// * @param String &$msg
// * @param String &$link
// *
// * @return boolean
// */
// public static function onSkinCopyrightFooter($title, $type, &$msg, &$link) {
// if (self::jsonConfigIsStorage()) {
// $jct = self::parseTitle($title);
// if ($jct) {
// $code = $jct->getConfig()->license;
// if ($code) {
// $msg = 'jsonconfig-license';
// $link = Html::element('a', [
// 'href' => wfMessage('jsonconfig-license-url-' . $code)->plain()
// ], wfMessage('jsonconfig-license-name-' . $code)->plain());
// return false;
// }
// }
// }
// return true;
// }
//
// /**
// * Adds CSS for pretty-printing configuration on NS_CONFIG pages.
// * @param \OutputPage &$out
// * @param \Skin &$skin
// * @return boolean
// */
// public static function onBeforePageDisplay(
// /** @noinspection PhpUnusedParameterInspection */ &$out, &$skin
// ) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// $title = $out->getTitle();
// // todo/fixme? We should probably add ext.jsonConfig style to only those pages
// // that pass parseTitle()
// $handler = ContentHandler::getForModelID($title->getContentModel());
// if ($handler->getDefaultFormat() == CONTENT_FORMAT_JSON ||
// self::parseTitle($title)
// ) {
// $out->addModuleStyles('ext.jsonConfig');
// }
// return true;
// }
//
// public static function onMovePageIsValidMove(
// Title $oldTitle, Title $newTitle, Status $status
// ) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// $jctOld = self::parseTitle($oldTitle);
// if ($jctOld) {
// $jctNew = self::parseTitle($newTitle);
// if (!$jctNew) {
// $status->fatal('jsonconfig-move-aborted-ns');
// return false;
// } elseif ($jctOld->getConfig()->model !== $jctNew->getConfig()->model) {
// $status->fatal('jsonconfig-move-aborted-model', $jctOld->getConfig()->model,
// $jctNew->getConfig()->model);
// return false;
// }
// }
//
// return true;
// }
//
// public static function onAbortMove(
// /** @noinspection PhpUnusedParameterInspection */
// Title $title, Title $newTitle, $wgUser, &$err, $reason
// ) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// $status = new \Status();
// self::onMovePageIsValidMove($title, $newTitle, $status);
// if (!$status->isOK()) {
// $err = $status->getHTML();
// return false;
// }
//
// return true;
// }
//
// /**
// * Conditionally load API module 'jsondata' depending on whether or not
// * this wiki stores any jsonconfig data
// *
// * @param ApiModuleManager $moduleManager Module manager instance
// * @return boolean
// */
// public static function onApiMainModuleManager(ApiModuleManager $moduleManager) {
// global $wgJsonConfigEnableLuaSupport;
// if ($wgJsonConfigEnableLuaSupport) {
// $moduleManager->addModule('jsondata', 'action', 'JsonConfig\\JCDataApi');
// }
// return true;
// }
//
// public static function onPageContentSaveComplete(
// /** @noinspection PhpUnusedParameterInspection */
// \WikiPage $wikiPage, $user, $content, $summary, $isMinor, $isWatch,
// $section, $flags, $revision, $status, $baseRevId
// ) {
// return self::onArticleChangeComplete($wikiPage, $content);
// }
//
// public static function onArticleDeleteComplete(
// /** @noinspection PhpUnusedParameterInspection */
// $article, &$user, $reason, $id, $content, $logEntry
// ) {
// return self::onArticleChangeComplete($article);
// }
//
// public static function onArticleUndelete(
// /** @noinspection PhpUnusedParameterInspection */
// $title, $created, $comment, $oldPageId
// ) {
// return self::onArticleChangeComplete($title);
// }
//
// public static function onTitleMoveComplete(
// /** @noinspection PhpUnusedParameterInspection */
// $title, $newTitle, $wgUser, $pageid, $redirid, $reason
// ) {
// return self::onArticleChangeComplete($title) ||
// self::onArticleChangeComplete($newTitle);
// }
//
// /**
// * Prohibit creation of the pages that are part of our namespaces but have not been explicitly
// * allowed. Bad capitalization is due to "userCan" hook name
// * @param Title &$title
// * @param User &$user
// * @param String $action
// * @param null &$result
// * @return boolean
// */
// public static function onuserCan(
// /** @noinspection PhpUnusedParameterInspection */
// &$title, &$user, $action, &$result = null
// ) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// if ($action == 'create' && self::parseTitle($title) == null) {
// // prohibit creation of the pages for the namespace that we handle,
// // if the title is not matching declared rules
// $result = false;
// return false;
// }
// return true;
// }
//
// /**
// * @param Object $value
// * @param JCContent $content
// * @return boolean
// */
// private static function onArticleChangeComplete($value, $content = null) {
// if (!self::jsonConfigIsStorage()) {
// return true;
// }
//
// if ($value && (!$content || is_a($content, 'JsonConfig\JCContent'))) {
// if (method_exists($value, 'getTitle')) {
// $value = $value->getTitle();
// }
// $jct = self::parseTitle($value);
// if ($jct && $jct->getConfig()->store) {
// $store = new JCCache($jct, $content);
// $store->resetCache();
//
// // Handle remote site notification
// $store = $jct->getConfig()->store;
// if ($store->notifyUrl) {
// $req =
// JCUtils::initApiRequestObj($store->notifyUrl, $store->notifyUsername,
// $store->notifyPassword);
// if ($req) {
// $query = [
// 'format' => 'json',
// 'action' => 'jsonconfig',
// 'command' => 'reload',
// 'title' => $jct->getNamespace() . ':' . $jct->getDBkey(),
// ];
// JCUtils::callApi($req, $query, 'notify remote JsonConfig client');
// }
// }
// }
// }
// return true;
// }
//
// /**
// * Quick check if the current wiki will store any configurations.
// * Faster than doing a full parsing of the $wgJsonConfigs in the JCSingleton::init()
// * @return boolean
// */
// private static function jsonConfigIsStorage() {
// static $isStorage = null;
// if ($isStorage == null) {
// global $wgJsonConfigs;
// $isStorage = false;
// $configs = array_replace_recursive(
// \ExtensionRegistry::getInstance()->getAttribute('JsonConfigs'),
// $wgJsonConfigs
// );
// foreach ($configs as $jc) {
// if ((!array_key_exists('isLocal', $jc) || $jc['isLocal']) ||
// (array_key_exists('store', $jc))
// ) {
// $isStorage = true;
// break;
// }
// }
// }
// return $isStorage;
// }
public static String Singleton_Id = "JCSingleton";
}

View File

@@ -0,0 +1,66 @@
/*
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.extensions.JsonConfig.includes {
// import org.junit.*;
// using gplx.langs.jsons;
// using gplx.xowa.mediawiki;
// using gplx.xowa.xtns.jsonConfigs.scribunto;
// public class JCSingleton_tst {
// private final JCSingleton_fxt fxt = new JCSingleton_fxt();
// @Test public void Get() {
// fxt.Init__store("en.wikipedia.org", "Page1"
// , Json_doc.Make_str_by_apos
// ( "{"
// , " 'data':"
// , " ["
// , " ["
// , " 'Q1'"
// , " , 'Data:Q1'"
// , " ]"
// , " ,"
// , " ["
// , " 'Q2'"
// , " , 'Data:Q2'"
// , " ]"
// , " ]"
// , "}"
// ));
// JCContent actl = fxt.Exec__getContent("en.wikipedia.org", "Page1");
// Object o = ((JCTabularContent)actl).getField("data");
// Tfds.Write(o);
// /*
// fxt.Test__get(actl, "data", "Q1")
// */
// }
// }
// class JCSingleton_fxt {
// private final JCSingleton singleton;
// private final Xomw_page_fetcher__mock store = new Xomw_page_fetcher__mock();
// public JCSingleton_fxt() {
// Jscfg_xtn_mgr xtn_mgr = new Jscfg_xtn_mgr();
// xtn_mgr.Init_xtn();
//
// singleton = (JCSingleton)XophpEnv.Instance.Singletons().Get_by(JCSingleton.Singleton_Id);
// singleton.Store_(store);
// }
// public void Init__store(String wiki, String page, String json) {
// store.Set_wtxt(Bry_.new_u8(wiki), Bry_.new_u8(page), Bry_.new_u8(json));
// }
// public JCContent Exec__getContent(String wiki, String page) {
// return singleton.getContent(wiki, "unknown_ns", page);
// }
// }
//}

View File

@@ -0,0 +1,209 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.core.primitives.*;
import gplx.xowa.mediawiki.*;
import gplx.xowa.langs.*;
public class JCTabularContent extends JCDataContent {// protected function createDefaultView() {
// return new JCTabularContentView();
// }
// /**
// * Returns wiki-table representation of the tabular data
// *
// * @return String|boolean The raw text, or false if the conversion failed.
// */
// public function getWikitextForTransclusion() {
// $toWiki = function ( $value ) {
// if ( is_object( $value ) ) {
// global $wgLang;
// $value = JCUtils::pickLocalizedString( $value, $wgLang );
// }
// if ( preg_match( '/^[ .\pL\pN]*$/i', $value ) ) {
// // Optimization: spaces, letters, numbers, and dots are returned without <nowiki>
// return $value;
// }
// return '<nowiki>' . htmlspecialchars( $value ) . '</nowiki>';
// };
//
// $data = $this->getData();
// $result = "{| class='wikitable sortable'\n";
//
// // Create header
// $result .= '!' . implode( "!!",
// array_map(
// function ( $field ) use ( $toWiki ) {
// return $toWiki( $field->title ? : $field->name );
// },
// $data->schema->fields
// )
// ) . "\n";
//
// // Create table content
// foreach ( $data->data as $row ) {
// $result .= "|-\n|" . implode( '||', array_map( $toWiki, $row ) ) . "\n";
// }
//
// $result .= "\n|}\n";
//
// return $result;
// }
/**
* Derived classes must implement this method to perform custom validation
* using the check(...) calls
*/
public void validateContent() {
// parent::validateContent();
//
// $validators = [ JCValidators::isList() ];
// $typeValidators = [];
// $fieldsPath = [ 'schema', 'fields' ];
// if ( $this->test( 'schema', JCValidators::isDictionary() ) &&
// $this->test( $fieldsPath, JCValidators::isList() ) &&
// $this->testEach( $fieldsPath, JCValidators::isDictionary() )
// ) {
// $hasError = false;
// $allHeaders = [];
// $fieldCount = count( $this->getField( $fieldsPath )->getValue() );
// for ( $idx = 0; $idx < $fieldCount; $idx++ ) {
// $header = false;
// $hasError |= !$this->test( [ 'schema', 'fields', $idx, 'name' ],
// JCValidators::isHeaderString( $allHeaders ),
// function ( JCValue $jcv ) use ( &$header ) {
// $header = $jcv->getValue();
// return true;
// } );
// $hasError |= !$this->test( [ 'schema', 'fields', $idx, 'type' ],
// JCValidators::validateDataType( $typeValidators ) );
// if ( $header ) {
// $hasError |= !$this->testOptional( [ 'schema', 'fields', $idx, 'title' ],
// function () use ( $header ) {
// return (Object)[ 'en' => $header ];
// }, JCValidators::isLocalizedString() );
// }
// }
// $countValidator = JCValidators::checkListSize( $fieldCount, 'schema/fields' );
// $validators[] = $countValidator;
//
// if ( !$hasError ) {
// $this->testEach( $fieldsPath, JCValidators::noExtraValues() );
// }
// }
// $this->test( 'schema', JCValidators::noExtraValues() );
//
// if ( !$this->thorough() ) {
// // We are not doing any modifications to the data, so no need to validate it
// return;
// }
//
// $this->test( 'data', JCValidators::isList() );
// $this->test( [], JCValidators::noExtraValues() );
// $this->testEach( 'data', $validators );
// if ( $typeValidators ) {
// /** @noinspection PhpUnusedParameterInspection */
// $this->testEach( 'data', function ( JCValue $v, array $path ) use ( $typeValidators ) {
// $isOk = true;
// $lastIdx = count( $path );
// foreach ( array_keys( $typeValidators ) as $k ) {
// $path[$lastIdx] = $k;
// $isOk &= $this->test( $path, $typeValidators[$k] );
// }
// return $isOk;
// } );
// }
}
/**
* Resolve @Override any specific localizations, and add it to $result
* @param Object $result
* @param Language $lang
*/
@Override protected void localizeData(XophpStdClass result, Xol_lang_itm lang) {
super.localizeData(result, lang);
XophpStdClass data = this.getData();
JCLocalizeItmFunc localize = new JCLocalizeItmFunc(lang);
Int_list isLocalized = new Int_list();
result.Set_by_as_itm("schema", new XophpStdClass());
XophpStdClass result_schema_flds = new XophpStdClass();
result.Set_by_as_itm(String_.Ary("schema", "fields"), result_schema_flds);
XophpStdClass flds = data.Get_by_ary_as_itm("schema", "fields");
int flds_len = flds.Len();
for (int ind = 0; ind < flds_len; ind++) {
XophpStdClass fld = flds.Get_at_as_itm(ind);
if (fld.Comp_str("type", "localized")) {
isLocalized.Add(ind);
}
XophpStdClass rslt_fld = new XophpStdClass();
rslt_fld.Set_by_as_str("name", fld.Get_by_as_str("name"));
rslt_fld.Set_by_as_str("type", fld.Get_by_as_str("type"));
rslt_fld.Set_by_as_str("title", fld.Has("title") ? localize.Localize(fld.Get_by_as_itm("title")) : fld.Get_by_as_str("name"));
result_schema_flds.Add_at_as_itm(rslt_fld);
}
if (isLocalized.Len() == 0) {
// There are no localized strings in the data, optimize
result.Set_by_as_itm("data", data.Get_by_as_itm("data"));
}
else {
JCArrayFunc array_map_func = new JCArrayFunc();
result.Set_by_as_itm("data", array_map_func.Array_map(new JCLocalizeAryFunc(localize, isLocalized), data.Get_by_as_itm("data")));
}
}
public static final String Model_id = "JCTabularContent";
}
class JCArrayFunc {
public XophpStdClass Array_map(JCLocalizeAryFunc func, XophpStdClass src) {
XophpStdClass trg = new XophpStdClass();
int len = src.Len();
for (int i = 0; i < len; i++) {
XophpStdClass src_sub = src.Get_at_as_itm(i);
XophpStdClass trg_sub = func.Array_map(src_sub);
trg.Add_at_as_itm(trg_sub);
}
return trg;
}
}
class JCLocalizeAryFunc {
private final JCLocalizeItmFunc localize;
private final Int_list isLocalized;
public JCLocalizeAryFunc(JCLocalizeItmFunc localize, Int_list isLocalized) {
this.localize = localize;
this.isLocalized = isLocalized;
}
public XophpStdClass Array_map(XophpStdClass row) {
int len = isLocalized.Len();
for (int ind = 0; ind < len; ind++) {
XophpStdClass val = row.Get_at_as_itm(ind);
if (val != null) {
row.Set_at_as_str(ind, localize.Localize(val)); // NOTE: will reduce a map to a String; EX: name={en='a',fr='b'} => name={'a'}
}
}
return row;
}
}
class JCLocalizeItmFunc {
private final Xol_lang_itm lang;
public JCLocalizeItmFunc(Xol_lang_itm lang) {
this.lang = lang;
}
public String Localize(XophpStdClass val) {
return JCUtils.pickLocalizedString(val, lang);
}
}

View File

@@ -0,0 +1,28 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.mediawiki.*;
public class JCTabularContentFactory implements XophpClassBldr {
public String Id() {return JCTabularContent.Model_id;}
public Object Make(Object... args) {
JCTabularContent rv = new JCTabularContent();
byte[] text = (byte[])args[0];
String modelId = (String)args[1];
boolean thorough = Bool_.Cast(args[2]);
rv.__construct(text, modelId, thorough);
return rv;
}
}

View File

@@ -0,0 +1,56 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.langs.*;
import gplx.xowa.mediawiki.*;
class JCUtils {
/**
* Find a message in a dictionary for the given language,
* or use language fallbacks if message is not defined.
* @param stdClass map Dictionary of languageCode => String
* @param Language|StubUserLang lang language Object
* @param boolean|String $defaultValue if non-false, use this value in case no fallback and no 'en'
* @return String message from the dictionary or "" if nothing found
*/
public static String pickLocalizedString(XophpStdClass map, Xol_lang_itm lang) {return pickLocalizedString(map, lang, null);}
public static String pickLocalizedString(XophpStdClass map, Xol_lang_itm lang, String defaultValue) {
String langCode = lang.Key_str();
if (map.Has(langCode)) {
return map.Get_by_as_str(langCode);
}
/*
for+each (lang.getFallbackLanguages() as l) {
if (property_exists(map, l)) {
return map.l;
}
}
*/
// If fallbacks fail, check if english is defined
if (map.Has("en") ) {
return map.Get_by_as_str("en");
}
// We have a custom default, return that
if (defaultValue != null) {
return null;
}
// Return first available value, or an empty String
// There might be a better way to get the first value from an Object
return map.Len() == 0 ? "" : map.Get_at_as_str(0);
}
}

View File

@@ -0,0 +1,252 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
import gplx.xowa.xtns.scribunto.*;
import gplx.xowa.mediawiki.*;
public class JCValue {
private Ordered_hash value = Ordered_hash_.New();
private int statusVal;
private XophpStdClass value_as_obj;
private Xophp_ary value_as_ary;
private Object value_as_prim;
private int value_tid;
private boolean sameAsDefaultVal = false;
private boolean defaultUsedVal = false;
private Object errorVal = null;
private static final int NULL = -1;
/** Value has not been checked */
public static final int UNCHECKED = 0;
/** Value was explicitly checked (might be an error) */
// private static final int CHECKED = 1;
/** field is missing in the data, but is being explicitly tested for.
* This value should never be stored in JCObjContent::validationData.
* Setting this value for any field in validator will delete it. */
private static final int MISSING = 2;
/** field was not explicitly tested, but it was listed as a parent of one of the tested fields */
// private static final int VISITED = 3;
private static final int Value_tid__obj = 1, Value_tid__ary = 2, Value_tid__prim = 3;
/** @param int status
* @param mixed value
*/
public JCValue(int status, XophpStdClass value_as_obj, Xophp_ary value_as_ary, Object value_as_prim) {
this.statusVal = status;
this.value_as_obj = value_as_obj;
this.value_as_ary = value_as_ary;
this.value_as_prim = value_as_prim;
if (value_as_obj != null) {
value_tid = Value_tid__obj;
}
else if (value_as_ary != null) {
value_tid = Value_tid__ary;
}
else {
value_tid = Value_tid__prim;
}
}
/** @return mixed */
public Object getValue() {
switch (value_tid) {
case Value_tid__obj:
return value_as_obj;
case Value_tid__ary:
return value_as_ary;
case Value_tid__prim:
return value_as_prim;
default:
throw Err_.new_unhandled_default(value_tid);
}
}
public void setValue(Ordered_hash value) {this.setValue(value, NULL);}
public void setValue(Ordered_hash value, int status) {
this.value = value;
if (status != NULL) {
this.status(status);
} else if (this.isMissing()) {
// Convenience - if we are setting a new value, assume we are setting a default
this.status(JCValue.UNCHECKED);
this.defaultUsed(true);
}
}
public int status() {return status(NULL);}
public int status(int o) {
int val = this.statusVal;
if (o != NULL) {
this.statusVal = o;
}
return val;
}
public boolean sameAsDefault() {return sameAsDefault(null);}
public boolean sameAsDefault(Object o) {
boolean val = this.sameAsDefaultVal;
if (o != null) {
this.sameAsDefaultVal = Bool_.Cast(o);
}
return val;
}
public boolean defaultUsed() {return defaultUsed(null);}
public boolean defaultUsed(Object o) {
boolean val = this.defaultUsedVal;
if (o != null) {
this.defaultUsedVal = Bool_.Cast(o);
}
return val;
}
public boolean isMissing() {
return this.statusVal == JCValue.MISSING;
}
public boolean isUnchecked() {
return this.statusVal == JCValue.UNCHECKED;
}
/** Helper function - same arguments as wfMessage, or true if message was already added.
* false clears this message status, and null returns current state without changing it
* @param null|boolean|String $key message id, or if boolean, sets/removes error status
* @param array $fieldPath path to the erroneous field. Will be converted to a a/b/c[0]/d style
* @return boolean|Message
*/
// public String error($key = null, $fieldPath = null /*...*/)
public Object error(Object key, String... fieldPath) {
if (Type_.Type_by_obj(key) == Bool_.Cls_ref_type) {
this.errorVal = Bool_.Cast(key);
}
else if (key != null) {
// $args = func_get_args();
// if (is_array($fieldPath)) {
// // Convert field path to a printable String
// $args[1] = JCUtils::fieldPathToString($fieldPath);
// }
// $this.errorVal = call_user_func_array('wfMessage', $args);
}
return this.errorVal;
}
/**
* @param String|int $fld
* @param mixed value
* @throws Exception
*/
public void setField(Object fld, Object o) {
int fld_type = To_type_id(fld);
if (value_tid == Value_tid__obj && fld_type == Type_ids_.Id__str) {
value_as_obj.Add_by_as_obj((String)fld, o);
}
else if (value_tid == Value_tid__ary && (fld_type == Type_ids_.Id__str || fld_type == Type_ids_.Id__int)) {
if (fld_type == Type_ids_.Id__str)
value_as_ary.Add((String)fld, o);
else
value_as_ary.Add(Int_.Cast(fld), o);
}
else {
throw Err_.new_wo_type("Type mismatch for field " + fld);
}
}
/**
* @param String|int $fld
* @throws \Exception
* @return mixed
*/
public Object deleteField(Object fld) {
int fld_type = To_type_id(fld);
Object tmp = null;
if (value_tid == Value_tid__obj && fld_type == Type_ids_.Id__str) {
String key = (String)fld;
tmp = value_as_obj.Get_by_as_obj(key);
value_as_obj.Del_by(key);
}
else if (value_tid == Value_tid__ary && (fld_type == Type_ids_.Id__str || fld_type == Type_ids_.Id__int)) {
tmp = value_as_ary.Get(fld);
if (fld_type == Type_ids_.Id__str)
value_as_ary.Unset((String)fld);
else
value_as_ary.Unset(Int_.Cast(fld));
value.Del(fld);
}
else {
throw Err_.new_wo_type("Type mismatch for field " + fld);
}
return tmp;
}
/**
* @param String|int $fld
* @throws \Exception
* @return boolean
*/
public boolean fieldExists(Object fld) {
int fld_type = To_type_id(fld);
if (value_tid == Value_tid__obj && fld_type == Type_ids_.Id__str) {
return value_as_obj.Has((String)fld);
}
else if (value_tid == Value_tid__ary && (fld_type == Type_ids_.Id__str || fld_type == Type_ids_.Id__int)) {
return value_as_ary.Has(fld);
}
throw Err_.new_wo_type("Type mismatch for field " + fld);
}
/**
* @param String|int $fld
* @throws \Exception
* @return mixed
*/
public Object getField(Object fld) {
int fld_type = To_type_id(fld);
if (value_tid == Value_tid__obj && fld_type == Type_ids_.Id__str) {
return value_as_obj.Get_by_as_obj((String)fld);
}
else if (value_tid == Value_tid__ary && (fld_type == Type_ids_.Id__str || fld_type == Type_ids_.Id__int)) {
return value_as_ary.Get(fld);
}
throw Err_.new_wo_type("Type mismatch for field " + fld);
}
public static int To_type_id(Object o) {
Class<?> type = Type_.Type_by_obj(o);
if (Type_.Eq(type, String.class))
return Type_ids_.Id__str;
else if (Type_.Eq(type, int.class))
return Type_ids_.Id__int;
else
return Type_ids_.Id__null;
}
}
class XomwTypeUtl {
public static int To_type_id(Object o) {
if (o == null)
return Type_ids_.Id__null;
Class<?> type = Type_.Type_by_obj(o);
if (Type_.Eq(type, String.class))
return Type_ids_.Id__str;
else if (Type_.Eq(type, int.class))
return Type_ids_.Id__int;
else if (Type_.Is_array(type))
return Type_ids_.Id__array;
else
return Type_ids_.Id__obj;
}
}

View File

@@ -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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
public class XomwStatus {
public boolean isGood() {
return false;
}
}

View File

@@ -0,0 +1,31 @@
/*
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.extensions.JsonConfig.includes; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.extensions.*; import gplx.xowa.mediawiki.extensions.JsonConfig.*;
public interface Xomw_page_fetcher {
byte[] Get_wtxt(byte[] wiki, byte[] page);
}
class Xomw_page_fetcher__mock implements Xomw_page_fetcher {
private final Ordered_hash hash = Ordered_hash_.New_bry();
public void Set_wtxt(byte[] wiki, byte[] page, byte[] wtxt) {
hash.Add(Make_key(wiki, page), wtxt);
}
public byte[] Get_wtxt(byte[] wiki, byte[] page) {
return (byte[])hash.Get_by(Make_key(wiki, page));
}
private static byte[] Make_key(byte[] wiki, byte[] page) {
return Bry_.Add(wiki, Byte_ascii.Pipe_bry, page);
}
}

View File

@@ -261,20 +261,20 @@ public class XomwDefines {
// define( 'PROTO_INTERNAL', 2 );
// /**@}*/
//
// /**@{
// * Content model ids, used by Content and ContentHandler.
// * These IDs will be exposed in the API and XML dumps.
// *
// * Extensions that define their own content model IDs should take
// * care to avoid conflicts. Using the extension name as a prefix is recommended,
// * for example 'myextension-somecontent'.
// */
// define( 'CONTENT_MODEL_WIKITEXT', 'wikitext' );
// define( 'CONTENT_MODEL_JAVASCRIPT', 'javascript' );
// define( 'CONTENT_MODEL_CSS', 'css' );
// define( 'CONTENT_MODEL_TEXT', 'text' );
// define( 'CONTENT_MODEL_JSON', 'json' );
// /**@}*/
/**@{
* Content model ids, used by Content and ContentHandler.
* These IDs will be exposed in the API and XML dumps.
*
* Extensions that define their own content model IDs should take
* care to avoid conflicts. Using the extension name as a prefix is recommended,
* for example 'myextension-somecontent'.
*/
public static final String CONTENT_MODEL_WIKITEXT = "wikitext";
public static final String CONTENT_MODEL_JAVASCRIPT = "javascript";
public static final String CONTENT_MODEL_CSS = "css";
public static final String CONTENT_MODEL_TEXT = "text";
public static final String CONTENT_MODEL_JSON = "json";
/**@}*/
/**@{
* Content formats, used by Content and ContentHandler.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,321 @@
/*
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.content; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
public class TextContent {
// AbstractContent
/**
* @since 1.21
*
* @return boolean
*
* @see Content::isRedirect
*/
// public function isRedirect() {
// return $this->getRedirectTarget() !== null;
// }
public boolean isRedirect() {
return false;
}
private byte[] mText;
/**
* @param String $text
* @param String $model_id
* @throws MWException
*/
public void __construct(byte[] text, String model_id) { // = CONTENT_MODEL_TEXT
// parent::__construct( $model_id );
//
// if ( $text === null || $text === false ) {
// wfWarn( "TextContent constructed with \$text = " . var_export( $text, true ) . "! "
// . "This may indicate an error in the caller's scope.", 2 );
//
// $text = '';
// }
//
// if ( !is_string( $text ) ) {
// throw new MWException( "TextContent expects a String in the constructor." );
// }
//
this.mText = text;
}
//
// /**
// * @note Mutable subclasses MUST override this to return a copy!
// *
// * @return Content this
// */
// public function copy() {
// return this; # NOTE: this is ok since TextContent are immutable.
// }
//
// public function getTextForSummary( $maxlength = 250 ) {
// global $wgContLang;
//
// $text = this.getNativeData();
//
// $truncatedtext = $wgContLang.truncate(
// preg_replace( "/[\n\r]/", ' ', $text ),
// max( 0, $maxlength ) );
//
// return $truncatedtext;
// }
//
// /**
// * Returns the text's size in bytes.
// *
// * @return int
// */
// public function getSize() {
// $text = this.getNativeData();
//
// return strlen( $text );
// }
//
// /**
// * Returns true if this content is not a redirect, and $wgArticleCountMethod
// * is "any".
// *
// * @param boolean|null $hasLinks If it is known whether this content contains links,
// * provide this information here, to avoid redundant parsing to find out.
// *
// * @return boolean
// */
// public function isCountable( $hasLinks = null ) {
// global $wgArticleCountMethod;
//
// if ( this.isRedirect() ) {
// return false;
// }
//
// if ( $wgArticleCountMethod === 'any' ) {
// return true;
// }
//
// return false;
// }
/**
* Returns the text represented by this Content Object, as a String.
*
* @return String The raw text.
*/
public byte[] getNativeData() {
return this.mText;
}
// /**
// * Returns the text represented by this Content Object, as a String.
// *
// * @return String The raw text.
// */
// public function getTextForSearchIndex() {
// return this.getNativeData();
// }
//
// /**
// * Returns attempts to convert this content Object to wikitext,
// * and then returns the text String. The conversion may be lossy.
// *
// * @note this allows any text-based content to be transcluded as if it was wikitext.
// *
// * @return String|boolean The raw text, or false if the conversion failed.
// */
// public function getWikitextForTransclusion() {
// $wikitext = this.convert( CONTENT_MODEL_WIKITEXT, 'lossy' );
//
// if ( $wikitext ) {
// return $wikitext.getNativeData();
// } else {
// return false;
// }
// }
//
// /**
// * Do a "\r\n" . "\n" and "\r" . "\n" transformation
// * as well as trim trailing whitespace
// *
// * This was formerly part of Parser::preSaveTransform, but
// * for non-wikitext content models they probably still want
// * to normalize line endings without all of the other PST
// * changes.
// *
// * @since 1.28
// * @param $text
// * @return String
// */
// public static function normalizeLineEndings( $text ) {
// return str_replace( [ "\r\n", "\r" ], "\n", rtrim( $text ) );
// }
//
// /**
// * Returns a Content Object with pre-save transformations applied.
// *
// * At a minimum, subclasses should make sure to call TextContent::normalizeLineEndings()
// * either directly or part of Parser::preSaveTransform().
// *
// * @param Title $title
// * @param User $user
// * @param ParserOptions $popts
// *
// * @return Content
// */
// public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
// $text = this.getNativeData();
// $pst = self::normalizeLineEndings( $text );
//
// return ( $text === $pst ) ? this : new static( $pst, this.getModel() );
// }
//
// /**
// * Diff this content Object with another content Object.
// *
// * @since 1.21
// *
// * @param Content $that The other content Object to compare this content Object to.
// * @param Language $lang The language Object to use for text segmentation.
// * If not given, $wgContentLang is used.
// *
// * @return Diff A diff representing the changes that would have to be
// * made to this content Object to make it equal to $that.
// */
// public function diff( Content $that, Language $lang = null ) {
// global $wgContLang;
//
// this.checkModelID( $that.getModel() );
//
// // @todo could implement this in DifferenceEngine and just delegate here?
//
// if ( !$lang ) {
// $lang = $wgContLang;
// }
//
// $otext = this.getNativeData();
// $ntext = $that.getNativeData();
//
// # Note: Use native PHP diff, external engines don't give us abstract output
// $ota = explode( "\n", $lang.segmentForDiff( $otext ) );
// $nta = explode( "\n", $lang.segmentForDiff( $ntext ) );
//
// $diff = new Diff( $ota, $nta );
//
// return $diff;
// }
//
// /**
// * Fills the provided ParserOutput Object with information derived from the content.
// * Unless $generateHtml was false, this includes an HTML representation of the content
// * provided by getHtml().
// *
// * For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
// * wikitext parser on the text to extract any (wikitext) links, magic words, etc.
// *
// * Subclasses may override this to provide custom content processing.
// * For custom HTML generation alone, it is sufficient to override getHtml().
// *
// * @param Title $title Context title for parsing
// * @param int $revId Revision ID (for {{REVISIONID}})
// * @param ParserOptions $options Parser options
// * @param boolean $generateHtml Whether or not to generate HTML
// * @param ParserOutput $output The output Object to fill (reference).
// */
// protected function fillParserOutput( Title $title, $revId,
// ParserOptions $options, $generateHtml, ParserOutput &$output
// ) {
// global $wgParser, $wgTextModelsToParse;
//
// if ( in_array( this.getModel(), $wgTextModelsToParse ) ) {
// // parse just to get links etc into the database, HTML is replaced below.
// $output = $wgParser.parse( this.getNativeData(), $title, $options, true, true, $revId );
// }
//
// if ( $generateHtml ) {
// $html = this.getHtml();
// } else {
// $html = '';
// }
//
// $output.setText( $html );
// }
//
// /**
// * Generates an HTML version of the content, for display. Used by
// * fillParserOutput() to provide HTML for the ParserOutput Object.
// *
// * Subclasses may override this to provide a custom HTML rendering.
// * If further information is to be derived from the content (such as
// * categories), the fillParserOutput() method can be overridden instead.
// *
// * For backwards-compatibility, this default implementation just calls
// * getHighlightHtml().
// *
// * @return String An HTML representation of the content
// */
// protected function getHtml() {
// return this.getHighlightHtml();
// }
//
// /**
// * Generates an HTML version of the content, for display.
// *
// * This default implementation returns an HTML-escaped version
// * of the raw text content.
// *
// * @note The functionality of this method should really be implemented
// * in getHtml(), and subclasses should override getHtml() if needed.
// * getHighlightHtml() is kept around for backward compatibility with
// * extensions that already override it.
// *
// * @deprecated since 1.24. Use getHtml() instead. In particular, subclasses overriding
// * getHighlightHtml() should override getHtml() instead.
// *
// * @return String An HTML representation of the content
// */
// protected function getHighlightHtml() {
// return htmlspecialchars( this.getNativeData() );
// }
//
// /**
// * This implementation provides lossless conversion between content models based
// * on TextContent.
// *
// * @param String $toModel The desired content model, use the CONTENT_MODEL_XXX flags.
// * @param String $lossy Flag, set to "lossy" to allow lossy conversion. If lossy conversion is not
// * allowed, full round-trip conversion is expected to work without losing information.
// *
// * @return Content|boolean A content Object with the content model $toModel, or false if that
// * conversion is not supported.
// *
// * @see Content::convert()
// */
// public function convert( $toModel, $lossy = '' ) {
// $converted = parent::convert( $toModel, $lossy );
//
// if ( $converted !== false ) {
// return $converted;
// }
//
// $toHandler = ContentHandler::getForModelID( $toModel );
//
// if ( $toHandler instanceof TextContentHandler ) {
// // NOTE: ignore content serialization format - it's just text anyway.
// $text = this.getNativeData();
// $converted = $toHandler.unserializeContent( $text );
// }
//
// return $converted;
// }
}

View File

@@ -0,0 +1,145 @@
/*
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.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) {
super.__construct(modelId, formats);
}
// @codingStandardsIgnoreEnd
//
// /**
// * Returns the content's text as-is.
// *
// * @param Content $content
// * @param String $format The serialization format to check
// *
// * @return mixed
// */
// public function serializeContent(Content $content, $format = null) {
// $this->checkFormat($format);
//
// return $content->getNativeData();
// }
//
// /**
// * Attempts to merge differences between three versions. Returns a new
// * Content Object for a clean merge and false for failure or a conflict.
// *
// * All three Content objects passed as parameters must have the same
// * content model.
// *
// * This text-based implementation uses wfMerge().
// *
// * @param Content $oldContent The page's previous content.
// * @param Content $myContent One of the page's conflicting contents.
// * @param Content $yourContent One of the page's conflicting contents.
// *
// * @return Content|boolean
// */
// public function merge3(Content $oldContent, Content $myContent, Content $yourContent) {
// $this->checkModelID($oldContent->getModel());
// $this->checkModelID($myContent->getModel());
// $this->checkModelID($yourContent->getModel());
//
// $format = $this->getDefaultFormat();
//
// $old = $this->serializeContent($oldContent, $format);
// $mine = $this->serializeContent($myContent, $format);
// $yours = $this->serializeContent($yourContent, $format);
//
// $ok = wfMerge($old, $mine, $yours, $result);
//
// if (!$ok) {
// return false;
// }
//
// if (!$result) {
// return $this->makeEmptyContent();
// }
//
// $mergedContent = $this->unserializeContent($result, $format);
//
// return $mergedContent;
// }
//
// /**
// * Returns the name of the associated Content class, to
// * be used when creating new objects. Override expected
// * by subclasses.
// *
// * @since 1.24
// *
// * @return String
// */
// protected function getContentClass() {
// return TextContent::class;
// }
//
// /**
// * Unserializes a Content Object of the type supported by this ContentHandler.
// *
// * @since 1.21
// *
// * @param String $text Serialized form of the content
// * @param String $format The format used for serialization
// *
// * @return Content The TextContent Object wrapping $text
// */
// public function unserializeContent($text, $format = null) {
// $this->checkFormat($format);
//
// $class = $this->getContentClass();
// return new $class($text);
// }
//
// /**
// * Creates an empty TextContent Object.
// *
// * @since 1.21
// *
// * @return Content A new TextContent Object with empty text.
// */
// public function makeEmptyContent() {
// $class = $this->getContentClass();
// return new $class('');
// }
//
// /**
// * @see ContentHandler::supportsDirectEditing
// *
// * @return boolean Default is true for TextContent and derivatives.
// */
// public function supportsDirectEditing() {
// return true;
// }
//
// public function getFieldsForSearchIndex(SearchEngine $engine) {
// $fields = parent::getFieldsForSearchIndex($engine);
// $fields['language'] =
// $engine->makeSearchFieldMapping('language', SearchIndexField::INDEX_TYPE_KEYWORD);
//
// return $fields;
// }
//
// public function getDataForSearchIndex(WikiPage $page, ParserOutput $output,
// SearchEngine $engine) {
// $fields = parent::getDataForSearchIndex($page, $output, $engine);
// $fields['language'] =
// $this->getPageLanguage($page->getTitle(), $page->getContent())->getCode();
// return $fields;
// }
}

View File

@@ -41,7 +41,7 @@ public class XomwInterwikiLookupAdapter implements XomwInterwikiLookup {
* @return boolean Whether it exists
*/
public boolean isValidInterwiki(byte[] prefix) {
return XophpArray.array_key_exists(prefix, this.getInterwikiMap());
return XophpArrayUtl.array_key_exists(prefix, this.getInterwikiMap());
}
/**
@@ -71,7 +71,7 @@ public class XomwInterwikiLookupAdapter implements XomwInterwikiLookup {
*/
public byte[][] getAllPrefixes(boolean local) {
if (!local) {
XophpArray.array_keys_bry(this.getInterwikiMap());
XophpArrayUtl.array_keys_bry(this.getInterwikiMap());
}
List_adp res = List_adp_.New();
Ordered_hash hash = this.getInterwikiMap();

View File

@@ -48,13 +48,13 @@ public class Xomw_table_wkr implements gplx.core.brys.Bry_split_wkr {// THREAD.U
// Closing open td, tr && table
while (td_history.Len() > 0) {
if (XophpArray.popBoolOrN(td_history)) {
if (XophpArrayUtl.popBoolOrN(td_history)) {
bfr.Add_str_a7("</td>\n");
}
if (XophpArray.popBoolOrN(tr_history)) {
if (XophpArrayUtl.popBoolOrN(tr_history)) {
bfr.Add_str_a7("</tr>\n");
}
if (!XophpArray.popBoolOrN(has_opened_tr)) {
if (!XophpArrayUtl.popBoolOrN(has_opened_tr)) {
bfr.Add_str_a7("<tr><td></td></tr>\n");
}
bfr.Add_str_a7("</table>\n");
@@ -123,20 +123,20 @@ public class Xomw_table_wkr implements gplx.core.brys.Bry_split_wkr {// THREAD.U
else if (Bry_.Eq(first_2, Wtxt__tb__end)) {
// We are ending a table
line = tmp.Add_str_a7("</table>").Add_mid(line, 2, line.length).To_bry_and_clear();
byte[] last_tag = XophpArray.popBryOrNull(last_tag_history);
byte[] last_tag = XophpArrayUtl.popBryOrNull(last_tag_history);
if (!XophpArray.popBoolOrN(has_opened_tr)) {
if (!XophpArrayUtl.popBoolOrN(has_opened_tr)) {
line = tmp.Add_str_a7("<tr><td></td></tr>").Add(line).To_bry_and_clear();
}
if (XophpArray.popBoolOrN(tr_history)) {
if (XophpArrayUtl.popBoolOrN(tr_history)) {
line = tmp.Add_str_a7("</tr>").Add(line).To_bry_and_clear();
}
if (XophpArray.popBoolOrN(td_history)) {
if (XophpArrayUtl.popBoolOrN(td_history)) {
line = tmp.Add_str_a7("</").Add(last_tag).Add_byte(Byte_ascii.Angle_end).Add(line).To_bry_and_clear();
}
XophpArray.popBryOrNull(tr_attributes);
XophpArrayUtl.popBryOrNull(tr_attributes);
// PORTED:$outLine = $line . str_repeat( '</dd></dl>', $indent_level );
tmp.Add(line);
for (int j = 0; j < indent_level; j++)
@@ -152,19 +152,19 @@ public class Xomw_table_wkr implements gplx.core.brys.Bry_split_wkr {// THREAD.U
sanitizer.fixTagAttributes(tmp, Name__tr, atrs);
atrs = tmp.To_bry_and_clear();
XophpArray.popBryOrNull(tr_attributes);
XophpArrayUtl.popBryOrNull(tr_attributes);
tr_attributes.Add(atrs);
line = Bry_.Empty;
byte[] last_tag = XophpArray.popBryOrNull(last_tag_history);
XophpArray.popBoolOrN(has_opened_tr);
byte[] last_tag = XophpArrayUtl.popBryOrNull(last_tag_history);
XophpArrayUtl.popBoolOrN(has_opened_tr);
has_opened_tr.Add(true);
if (XophpArray.popBoolOrN(tr_history)) {
if (XophpArrayUtl.popBoolOrN(tr_history)) {
line = Html__tr__end;
}
if (XophpArray.popBoolOrN(td_history)) {
if (XophpArrayUtl.popBoolOrN(td_history)) {
line = tmp.Add_str_a7("</").Add(last_tag).Add_byte(Byte_ascii.Gt).Add(line).To_bry_and_clear();
}
@@ -205,19 +205,19 @@ public class Xomw_table_wkr implements gplx.core.brys.Bry_split_wkr {// THREAD.U
byte[] cell = cells[j];
previous = Bry_.Empty;
if (first_char != Byte_ascii.Plus) {
byte[] tr_after = XophpArray.popBryOrNull(tr_attributes);
if (!XophpArray.popBoolOrN(tr_history)) {
byte[] tr_after = XophpArrayUtl.popBryOrNull(tr_attributes);
if (!XophpArrayUtl.popBoolOrN(tr_history)) {
previous = tmp.Add_str_a7("<tr").Add(tr_after).Add_str_a7(">\n").To_bry_and_clear();
}
tr_history.Add(true);
tr_attributes.Add(Bry_.Empty);
XophpArray.popBoolOrN(has_opened_tr);
XophpArrayUtl.popBoolOrN(has_opened_tr);
has_opened_tr.Add(true);
}
byte[] last_tag = XophpArray.popBryOrNull(last_tag_history);
byte[] last_tag = XophpArrayUtl.popBryOrNull(last_tag_history);
if (XophpArray.popBoolOrN(td_history)) {
if (XophpArrayUtl.popBoolOrN(td_history)) {
previous = tmp.Add_str_a7("</").Add(last_tag).Add_str_a7(">\n").Add(previous).To_bry_and_clear();
}

View File

@@ -103,14 +103,14 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret
*/
XomwSite site = (XomwSite)this.offsetGet(index);
XophpArray.unset(this.byGlobalId, site.getGlobalId());
XophpArray.unset(this.byInternalId, site.getInternalId());
XophpArrayUtl.unset(this.byGlobalId, site.getGlobalId());
XophpArrayUtl.unset(this.byInternalId, site.getInternalId());
Ordered_hash ids = site.getNavigationIds();
int len = ids.Len();
for (int i = 0; i < len; i++) {
int navId = Int_.Cast(ids.Get_at(i));
XophpArray.unset(this.byNavigationId, navId);
XophpArrayUtl.unset(this.byNavigationId, navId);
}
}
@@ -126,7 +126,7 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret
* @return array
*/
public String[] getGlobalIdentifiers() {
return XophpArray.array_keys_str(this.byGlobalId);
return XophpArrayUtl.array_keys_str(this.byGlobalId);
}
/**
@@ -137,7 +137,7 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret
* @return boolean
*/
public boolean hasSite(String globalSiteId) {
return XophpArray.array_key_exists(globalSiteId, this.byGlobalId);
return XophpArrayUtl.array_key_exists(globalSiteId, this.byGlobalId);
}
/**
@@ -174,7 +174,7 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret
* @return boolean
*/
@Override public boolean isEmpty() {
return XophpArray.array_is_empty(this.byGlobalId);
return XophpArrayUtl.array_is_empty(this.byGlobalId);
}
/**
@@ -185,7 +185,7 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret
* @return boolean
*/
public boolean hasInternalId(int id) {
return XophpArray.array_key_exists(id, this.byInternalId);
return XophpArrayUtl.array_key_exists(id, this.byInternalId);
}
/**
@@ -222,7 +222,7 @@ public class XomwSiteList extends XomwGenericArrayObject { public int Len() {ret
* @return boolean
*/
public boolean hasNavigationId(String id) {
return XophpArray.array_key_exists(id, this.byNavigationId);
return XophpArrayUtl.array_key_exists(id, this.byNavigationId);
}
/**