From 19a315b8cd5816016b2aa911aab2af41e4700d3f Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Sun, 18 Oct 2020 10:52:54 -0400 Subject: [PATCH] Skin: Change to mustache-backed Skin.Vector [#797] --- 100_core/src/gplx/Bry_.java | 2448 ++-- 100_core/src/gplx/Bry__tst.java | 600 +- .../src/gplx/core/gfobjs/Gfobj_rdr__json.java | 168 +- .../src/gplx/langs/jsons/JsonDocBldr.java | 69 + 400_xowa/src/gplx/langs/jsons/Json_ary.java | 137 +- 400_xowa/src/gplx/langs/jsons/Json_doc.java | 153 +- .../src/gplx/langs/jsons/Json_doc_bldr.java | 40 - .../src/gplx/langs/jsons/Json_doc_wtr.java | 291 +- .../src/gplx/langs/jsons/Json_factory.java | 28 - 400_xowa/src/gplx/langs/jsons/Json_itm.java | 40 +- .../src/gplx/langs/jsons/Json_itm_base.java | 31 +- .../src/gplx/langs/jsons/Json_itm_bool.java | 32 +- .../gplx/langs/jsons/Json_itm_decimal.java | 73 +- .../src/gplx/langs/jsons/Json_itm_int.java | 69 +- .../src/gplx/langs/jsons/Json_itm_long.java | 68 +- .../src/gplx/langs/jsons/Json_itm_str.java | 232 +- .../src/gplx/langs/jsons/Json_itm_tmp.java | 29 - 400_xowa/src/gplx/langs/jsons/Json_kv.java | 50 +- .../gplx/langs/jsons/Json_kv_ary_srl_tst.java | 75 +- 400_xowa/src/gplx/langs/jsons/Json_nde.java | 408 +- .../src/gplx/langs/jsons/Json_parser.java | 395 +- .../jsons/Json_parser__list_nde__base.java | 113 +- .../src/gplx/langs/jsons/Json_parser_tst.java | 179 +- .../gplx/langs/mustaches/JsonMustacheNde.java | 112 + .../langs/mustaches/Mustache_doc_itm.java | 14 +- .../mustaches/Mustache_itm_render_tst.java | 323 +- .../langs/mustaches/Mustache_render_ctx.java | 182 +- .../langs/mustaches/Mustache_tkn_def.java | 66 +- .../langs/mustaches/Mustache_tkn_itm.java | 208 +- .../langs/mustaches/Mustache_tkn_parser.java | 298 +- .../htmls/sidebars/Db_Nav_template.java | 275 + .../htmls/sidebars/Xoh_sidebar_htmlr.java | 100 +- .../htmls/sidebars/Xoh_sidebar_mgr_tst.java | 461 +- .../Xoa_site_cfg_itm__interwikimap.java | 239 +- .../wms/revs/Xowm_rev_wkr__meta__wm.java | 89 +- .../src/gplx/xowa/langs/msgs/Xow_msg_mgr.java | 226 +- .../gplx/xowa/parsers/tmpls/Xot_invk_tkn.java | 34 +- .../xowa/wikis/caches/Xow_defn_cache.java | 53 +- .../gplx/xowa/xtns/lst/Db_Section_list.java | 403 + .../xowa/xtns/pagebanners/Pgbnr_func.java | 432 +- .../xowa/xtns/pagebanners/Pgbnr_func_tst.java | 167 +- .../xowa/xtns/pagebanners/Pgbnr_icon.java | 63 +- .../gplx/xowa/xtns/pagebanners/Pgbnr_itm.java | 337 +- .../xowa/xtns/pagebanners/Pgbnr_xtn_mgr.java | 9 +- .../libs/Scrib_lib_text__json_util.java | 469 +- .../gplx/xowa/xtns/wbases/Wdata_wiki_mgr.java | 24 +- .../xowa/xtns/wbases/Wdata_wiki_mgr_fxt.java | 423 +- .../wbases/parsers/Wbase_claim_factory.java | 20 +- .../parsers/Wdata_claims_parser_v2.java | 289 +- .../parsers/Wdata_doc_parser_fxt_base.java | 12 +- .../wbases/parsers/Wdata_doc_parser_v1.java | 84 +- .../wbases/parsers/Wdata_doc_parser_v2.java | 283 +- .../tst/gplx/langs/jsons/JsonDocBldrTest.java | 47 + .../langs/mustaches/JsonMustacheNdeTest.java | 71 + res/bin/any/xowa/cfg/app/xowa.gfs | 62 +- res/bin/any/xowa/cfg/lang/core/en.gfs | 11933 ++++++++-------- .../any/xowa/html/res/src/xowa/core/core.js | 12 + .../xtns/Skin-Vector/templates/Menu.mustache | 22 + .../Skin-Vector/templates/Navigation.mustache | 16 + .../templates/legacy/Sidebar.mustache | 13 + .../templates/skin-legacy.mustache | 69 + .../templates/banner.mustache | 4 +- 62 files changed, 12827 insertions(+), 10845 deletions(-) create mode 100644 400_xowa/src/gplx/langs/jsons/JsonDocBldr.java delete mode 100644 400_xowa/src/gplx/langs/jsons/Json_doc_bldr.java delete mode 100644 400_xowa/src/gplx/langs/jsons/Json_factory.java delete mode 100644 400_xowa/src/gplx/langs/jsons/Json_itm_tmp.java create mode 100644 400_xowa/src/gplx/langs/mustaches/JsonMustacheNde.java create mode 100644 400_xowa/src/gplx/xowa/addons/htmls/sidebars/Db_Nav_template.java create mode 100644 400_xowa/src/gplx/xowa/xtns/lst/Db_Section_list.java create mode 100644 400_xowa/tst/gplx/langs/jsons/JsonDocBldrTest.java create mode 100644 400_xowa/tst/gplx/langs/mustaches/JsonMustacheNdeTest.java create mode 100644 res/bin/any/xowa/xtns/Skin-Vector/templates/Menu.mustache create mode 100644 res/bin/any/xowa/xtns/Skin-Vector/templates/Navigation.mustache create mode 100644 res/bin/any/xowa/xtns/Skin-Vector/templates/legacy/Sidebar.mustache create mode 100644 res/bin/any/xowa/xtns/Skin-Vector/templates/skin-legacy.mustache diff --git a/100_core/src/gplx/Bry_.java b/100_core/src/gplx/Bry_.java index d10cda57c..d9f7743c8 100644 --- a/100_core/src/gplx/Bry_.java +++ b/100_core/src/gplx/Bry_.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,1219 +13,1233 @@ 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; -import java.lang.*; -import gplx.core.brys.*; import gplx.core.primitives.*; import gplx.core.ios.*; -import gplx.langs.htmls.entitys.*; -public class Bry_ { - public static final String Cls_val_name = "byte[]"; - public static final byte[] Empty = new byte[0]; - public static final byte[][] Ary_empty = new byte[0][]; - public static final Class Cls_ref_type = byte[].class; - public static byte[] cast(Object val) {return (byte[])val;} - public static byte[] New_by_byte(byte b) {return new byte[] {b};} - public static byte[] New_by_ints(int... ary) { - int len = ary.length; - byte[] rv = new byte[len]; - for (int i = 0; i < len; i++) - rv[i] = (byte)ary[i]; - return rv; - } - public static byte[] New_by_objs(Bry_bfr bfr, Object... ary) { - int len = ary.length; - for (int i = 0; i < len; ++i) { - Object itm = ary[i]; - Class type = Type_.Type_by_obj(itm); - if (Type_.Eq(type, int.class)) bfr.Add_byte((byte)Int_.Cast(itm)); - else if (Type_.Eq(type, String.class)) bfr.Add_str_u8((String)itm); - else if (Type_.Eq(type, byte[].class)) bfr.Add((byte[])itm); - else throw Err_.new_unhandled(Type_.Canonical_name(type)); - } - return bfr.To_bry_and_clear(); - } - public static byte[] Coalesce_to_empty(byte[] v) {return v == null ? Bry_.Empty : v;} - public static byte[] Coalesce(byte[] v, byte[] or) {return v == null ? or : v;} - public static byte[] new_a7(String str) { - if (str == null) return null; - int str_len = str.length(); - if (str_len == 0) return Bry_.Empty; - byte[] rv = new byte[str_len]; - for (int i = 0; i < str_len; ++i) { - char c = str.charAt(i); - if (c > 128) c = '?'; - rv[i] = (byte)c; - } - return rv; - } - public static byte[] new_u8_safe(String str) {return str == null ? null : new_u8(str);} - public static byte[] new_u8(String str) { - try { - int str_len = str.length(); - if (str_len == 0) return Bry_.Empty; - int bry_len = new_u8__by_len(str, str_len); - byte[] rv = new byte[bry_len]; - new_u8__write(str, str_len, rv, 0); - return rv; - } - catch (Exception e) {throw Err_.new_exc(e, "core", "invalid UTF-8 sequence", "s", str);} - } - public static int new_u8__by_len(String s, int s_len) { - int rv = 0; - for (int i = 0; i < s_len; ++i) { - char c = s.charAt(i); - int c_len = 0; - if ( c < 128) c_len = 1; // 1 << 7 - else if ( c < 2048) c_len = 2; // 1 << 11 - else if ( (c > 55295) // 0xD800 - && (c < 56320)) c_len = 4; // 0xDFFF - else c_len = 3; // 1 << 16 - if (c_len == 4) ++i; // surrogate is 2 wide, not 1 - rv += c_len; - } - return rv; - } - public static byte[] New_u8_nl_apos(String... lines) { - Bry_bfr bfr = Bry_bfr_.Get(); - try { - New_u8_nl_apos(bfr, lines); - return bfr.To_bry_and_clear(); - } - finally {bfr.Mkr_rls();} - } - public static void New_u8_nl_apos(Bry_bfr bfr, String... lines) { - int lines_len = lines.length; - for (int i = 0; i < lines_len; ++i) { - if (i != 0) bfr.Add_byte_nl(); - byte[] line = Bry_.new_u8(lines[i]); - boolean dirty = false; - int prv = 0; - int line_len = line.length; - for (int j = 0; j < line_len; ++j) { - byte b = line[j]; - if (b == Byte_ascii.Apos) { - bfr.Add_mid(line, prv, j); - bfr.Add_byte(Byte_ascii.Quote); - dirty = true; - prv = j + 1; - } - } - if (dirty) - bfr.Add_mid(line, prv, line_len); - else - bfr.Add(line); - } - } - public static void new_u8__write(String str, int str_len, byte[] bry, int bry_pos) { - for (int i = 0; i < str_len; ++i) { - char c = str.charAt(i); - if ( c < 128) { - bry[bry_pos++] = (byte)c; - } - else if ( c < 2048) { - bry[bry_pos++] = (byte)(0xC0 | (c >> 6)); - bry[bry_pos++] = (byte)(0x80 | (c & 0x3F)); - } - else if ( (c > 55295) // 0xD800 - && (c < 56320)) { // 0xDFFF - if (i >= str_len) throw Err_.new_wo_type("incomplete surrogate pair at end of String", "char", c); - char nxt_char = str.charAt(i + 1); - int v = 0x10000 + (c - 0xD800) * 0x400 + (nxt_char - 0xDC00); - bry[bry_pos++] = (byte)(0xF0 | (v >> 18)); - bry[bry_pos++] = (byte)(0x80 | (v >> 12) & 0x3F); - bry[bry_pos++] = (byte)(0x80 | (v >> 6) & 0x3F); - bry[bry_pos++] = (byte)(0x80 | (v & 0x3F)); - ++i; - } - else { - bry[bry_pos++] = (byte)(0xE0 | (c >> 12)); - bry[bry_pos++] = (byte)(0x80 | (c >> 6) & 0x3F); - bry[bry_pos++] = (byte)(0x80 | (c & 0x3F)); - } - } - } - public static byte[] Copy(byte[] src) { - int src_len = src.length; - byte[] trg = new byte[src_len]; - for (int i = 0; i < src_len; ++i) - trg[i] = src[i]; - return trg; - } - public static byte[] Resize(byte[] src, int trg_len) {return Resize(src, 0, trg_len);} - public static byte[] Resize(byte[] src, int src_bgn, int trg_len) { - byte[] trg = new byte[trg_len]; - int src_len = src.length; if (src_len > trg_len) src_len = trg_len; // trg_len can be less than src_len - Copy_to(src, src_bgn, src_len, trg, 0); - return trg; - } - public static byte[] Repeat_space(int len) {return Repeat(Byte_ascii.Space, len);} - public static byte[] Repeat(byte b, int len) { - byte[] rv = new byte[len]; - for (int i = 0; i < len; i++) - rv[i] = b; - return rv; - } - public static byte[] Repeat_bry(byte[] bry, int len) { - int bry_len = bry.length; - int rv_len = len * bry_len; - byte[] rv = new byte[rv_len]; - for (int i = 0; i < len; i++) { - for (int j = 0; j < bry_len; j++) { - rv[(i * bry_len) + j] = bry[j]; - } - } - return rv; - } - public static byte[] Add(byte[] src, byte b) { - int src_len = src.length; - byte[] rv = new byte[src_len + 1]; - Copy_to(src, 0, src_len, rv, 0); - rv[src_len] = b; - return rv; - } - public static byte[] Add(byte b, byte[] src) { - int src_len = src.length; - byte[] rv = new byte[src_len + 1]; - Copy_to(src, 0, src_len, rv, 1); - rv[0] = b; - return rv; - } - public static byte[] Add(byte[]... all) { - int all_len = all.length, rv_len = 0; - for (int i = 0; i < all_len; ++i) { - byte[] cur = all[i]; if (cur == null) continue; - rv_len += cur.length; - } - byte[] rv = new byte[rv_len]; - int rv_idx = 0; - for (int i = 0; i < all_len; ++i) { - byte[] cur = all[i]; if (cur == null) continue; - int cur_len = cur.length; - for (int j = 0; j < cur_len; ++j) - rv[rv_idx++] = cur[j]; - } - return rv; - } - public static byte[] Add_w_dlm(byte[] dlm, byte[]... ary) { - int ary_len = ary.length; - if (ary_len == 0) return Bry_.Empty; - int dlm_len = dlm.length; - int rv_len = dlm_len * (ary_len - 1); // rv will have at least as many dlms as itms - 1 - for (int i = 0; i < ary_len; i++) { - byte[] itm = ary[i]; - if (itm != null) rv_len += itm.length; - } - int rv_pos = 0; - byte[] rv = new byte[rv_len]; - for (int i = 0; i < ary_len; i++) { - byte[] itm = ary[i]; - if (i != 0) { - for (int j = 0; j < dlm_len; j++) { - rv[rv_pos++] = dlm[j]; - } - } - if (itm == null) continue; - int itm_len = itm.length; - for (int j = 0; j < itm_len; j++) { - rv[rv_pos++] = itm[j]; - } - } - return rv; - } - public static byte[] Add_w_dlm(byte dlm, byte[]... ary) { - int ary_len = ary.length; - if (ary_len == 0) return Bry_.Empty; - int rv_len = ary_len - 1; // rv will have at least as many dlms as itms - 1 - for (int i = 0; i < ary_len; i++) { - byte[] itm = ary[i]; - if (itm != null) rv_len += itm.length; - } - int rv_pos = 0; - byte[] rv = new byte[rv_len]; - for (int i = 0; i < ary_len; i++) { - byte[] itm = ary[i]; - if (i != 0) rv[rv_pos++] = dlm; - if (itm == null) continue; - int itm_len = itm.length; - for (int j = 0; j < itm_len; j++) { - rv[rv_pos++] = itm[j]; - } - } - return rv; - } - public static int Len(byte[] v) {return v == null ? 0 : v.length;} - public static boolean Len_gt_0(byte[] v) {return v != null && v.length > 0;} - public static boolean Len_eq_0(byte[] v) {return v == null || v.length == 0;} - public static byte Get_at_end(byte[] bry) {return bry[bry.length - 1];} // don't bother checking for errors; depend on error trace - public static boolean Has_at(byte[] src, int src_len, int pos, byte b) {return (pos < src_len) && (src[pos] == b);} - public static boolean Has(byte[] src, byte[] lkp) {return Bry_find_.Find_fwd(src, lkp) != Bry_find_.Not_found;} - public static boolean Has(byte[] src, byte lkp) { - if (src == null) return false; - int len = src.length; - for (int i = 0; i < len; i++) - if (src[i] == lkp) return true; - return false; - } - public static boolean Has_at_bgn(byte[] src, byte lkp) {return Has_at_bgn(src, lkp, 0);} - public static boolean Has_at_bgn(byte[] src, byte lkp, int src_bgn) {return src_bgn < src.length ? src[src_bgn] == lkp : false;} - public static boolean Has_at_bgn(byte[] src, byte[] lkp) {return Has_at_bgn(src, lkp, 0, src.length);} - public static boolean Has_at_bgn(byte[] src, byte[] lkp, int src_bgn, int src_end) { - int lkp_len = lkp.length; - if (lkp_len + src_bgn > src_end) return false; // lkp is longer than src - for (int i = 0; i < lkp_len; i++) { - if (lkp[i] != src[i + src_bgn]) return false; - } - return true; - } - public static boolean Has_at_end(byte[] src, byte lkp) { - if (src == null) return false; - int src_len = src.length; - if (src_len == 0) return false; - return src[src_len - 1] == lkp; - } - public static boolean Has_at_end(byte[] src, byte[] lkp) {int src_len = src.length; return Has_at_end(src, lkp, src_len - lkp.length, src_len);} - public static boolean Has_at_end(byte[] src, byte[] lkp, int src_bgn, int src_end) { - int lkp_len = lkp.length; - if (src_bgn < 0) return false; - int pos = src_end - lkp_len; if (pos < src_bgn) return false; // lkp is longer than src - for (int i = 0; i < lkp_len; i++) { - if (lkp[i] != src[i + pos]) return false; - } - return true; - } - public static void Set(byte[] src, int bgn, int end, byte[] repl) { - int repl_len = repl.length; - for (int i = 0; i < repl_len; i++) - src[i + bgn] = repl[i]; - } - public static void Copy_to(byte[] src, int src_bgn, int src_end, byte[] trg, int trg_bgn) { - int trg_adj = trg_bgn - src_bgn; - for (int i = src_bgn; i < src_end; i++) - trg[i + trg_adj] = src[i]; - } - public static void Copy_to_reversed(byte[] src, int src_bgn, int src_end, byte[] trg, int trg_bgn) { - // copies src to trg, but in reverse order; EX: trg="1" src="432." -> "1.234" - int len = src_end - src_bgn; - for (int i = 0; i < len; i++) - trg[trg_bgn + i] = src[src_end - i - 1]; - } - public static byte[] Replace_one(byte[] orig, byte[] find, byte[] repl) { - // find val - int orig_len = orig.length; - int find_pos = Bry_find_.Find(orig, find, 0, orig_len, true); - if (find_pos == Bry_find_.Not_found) return orig; // nothing found; exit - - // do copy - int find_len = find.length, repl_len = repl.length; - int rv_len = orig_len + repl_len - find_len; - byte[] rv = new byte[rv_len]; - Copy_to(orig, 0 , find_pos, rv, 0 ); // copy orig before repl - Copy_to(repl, 0 , repl_len, rv, find_pos ); // copy repl - Copy_to(orig, find_pos + find_len, orig_len, rv, find_pos + repl_len); // copy orig after repl - return rv; - } - public static void Replace_all_direct(byte[] src, byte find, byte repl) {Replace_all_direct(src, find, repl, 0, src.length);} - public static void Replace_all_direct(byte[] src, byte find, byte repl, int bgn, int end) { - for (int i = bgn; i < end; i++) { - byte b = src[i]; - if (b == find) src[i] = repl; - } - } - public static byte[] Limit(byte[] src, int len) { - if (src == null) return null; - int src_len = src.length; - return len < src_len ? Bry_.Mid(src, 0, len) : src; - } - public static byte[] Mid_by_nearby(byte[] src, int pos, int around) { - int bgn = pos - around; if (bgn < 0) bgn = 0; - int src_len = src.length; - int end = pos + around; if (end > src_len) end = src_len; - return Mid(src, bgn, end); - } - public static byte[] Mid_by_len(byte[] src, int bgn, int len) {return Mid(src, bgn, bgn + len);} - public static byte[] Mid_by_len_safe(byte[] src, int bgn, int len) { - int src_len = src.length; - if (bgn < 0) bgn = 0; - if (len + bgn > src_len) len = (src_len - bgn); - return Mid(src, bgn, bgn + len); - } - public static String MidByLenToStr(byte[] src, int bgn, int len) { - int end = bgn + len; end = Int_.BoundEnd(end, src.length); - byte[] ary = Bry_.Mid(src, bgn, end); - return String_.new_u8(ary); - } - public static byte[] Mid_safe(byte[] src, int bgn, int end) { - if (src == null) return null; - int src_len = src.length; - if (bgn < 0) - bgn = 0; - else if (bgn >= src_len) - bgn = src_len; - - if (end < 0) - end = 0; - else if (end >= src_len) - end = src_len; - - if (bgn > end) - bgn = end; - else if (end < bgn) - end = bgn; - - return Mid(src, bgn, end); - } - public static byte[] Mid(byte[] src, int bgn) {return Mid(src, bgn, src.length);} - public static byte[] Mid_or(byte[] src, int bgn, int end, byte[] or) { - int src_len = src.length; - if ( src == null - || (bgn < 0 || bgn > src_len) - || (end < 0 || end > src_len) - || (end < bgn) - ) - return or; - return bgn == src_len ? Bry_.Empty : Mid(src, bgn, src_len); - } - public static byte[] Mid(byte[] src, int bgn, int end) { - try { - int len = end - bgn; if (len == 0) return Bry_.Empty; - byte[] rv = new byte[len]; - for (int i = bgn; i < end; i++) - rv[i - bgn] = src[i]; - return rv; - } catch (Exception e) {Err_.Noop(e); throw Err_.new_("Bry_", "mid failed", "bgn", bgn, "end", end);} - } - public static byte[] Mid_w_trim(byte[] src, int bgn, int end) { - int len = end - bgn; if (len == 0) return Bry_.Empty; - int actl_bgn = bgn, actl_end = end; - // trim at bgn - boolean chars_seen = false; - for (int i = bgn; i < end; ++i) { - switch (src[i]) { - case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: - break; - default: - chars_seen = true; - actl_bgn = i; - i = end; - break; - } - } - if (!chars_seen) return Bry_.Empty; // all ws - // trim at end - for (int i = end - 1; i >= actl_bgn; --i) { - switch (src[i]) { - case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: - break; - default: - actl_end = i + 1; - i = -1; - break; - } - } - // extract mid - len = actl_end - actl_bgn; if (len == 0) return Bry_.Empty; - byte[] rv = new byte[len]; - for (int i = actl_bgn; i < actl_end; ++i) - rv[i - actl_bgn] = src[i]; - return rv; - } - public static byte[] mask_(int len, byte... itms) { - byte[] rv = new byte[len]; - int itms_len = itms.length; - for (int i = 0; i < itms_len; i++) { - byte itm = itms[i]; - rv[itm & 0xFF] = itm; // PATCH.JAVA:need to convert to unsigned byte - } - return rv; - } - public static final byte[] Trim_ary_ws = mask_(256, Byte_ascii.Tab, Byte_ascii.Nl, Byte_ascii.Cr, Byte_ascii.Space); - public static byte[] Trim(byte[] src) {return Trim(src, 0, src.length, true, true, Trim_ary_ws, true);} - public static byte[] Trim(byte[] src, int bgn, int end) {return Trim(src, bgn, end, true, true, Trim_ary_ws, true);} - public static byte[] Trim(byte[] src, int bgn, int end, boolean trim_bgn, boolean trim_end, byte[] trim_ary, boolean reuse_bry_if_noop) { - int txt_bgn = bgn, txt_end = end; - boolean all_ws = true; - if (trim_bgn) { - for (int i = bgn; i < end; i++) { - byte b = src[i]; - if (trim_ary[b & 0xFF] == Byte_ascii.Null) { - txt_bgn = i; - i = end; - all_ws = false; - } - } - if (all_ws) return Bry_.Empty; - } - if (trim_end) { - for (int i = end - 1; i > -1; i--) { - byte b = src[i]; - if (trim_ary[b & 0xFF] == Byte_ascii.Null) { - txt_end = i + 1; - i = -1; - all_ws = false; - } - } - if (all_ws) return Bry_.Empty; - } - - if ( reuse_bry_if_noop - && bgn == 0 && end == src.length // Trim is called on entire bry, not subset - && bgn == txt_bgn && end == txt_end // Trim hasn't trimmed anything - ) { - return src; - } - else - return Bry_.Mid(src, txt_bgn, txt_end); - } - public static byte[] Trim_end(byte[] v, byte trim, int end) { - boolean trimmed = false; - int pos = end - 1; // NOTE: -1 b/c callers will always be passing pos + 1; EX: src, src_len - for (; pos > -1; pos--) { - if (v[pos] == trim) { - trimmed = true; - } - else - break; - } - return trimmed ? Bry_.Mid(v, 0, pos + 1) : v; - } - public static int Compare(byte[] lhs, byte[] rhs) { - if (lhs == null) return CompareAble_.More; - else if (rhs == null) return CompareAble_.Less; - else return Compare(lhs, 0, lhs.length, rhs, 0, rhs.length); - } - public static int Compare(byte[] lhs, int lhs_bgn, int lhs_end, byte[] rhs, int rhs_bgn, int rhs_end) { - int lhs_len = lhs_end - lhs_bgn, rhs_len = rhs_end - rhs_bgn; - int min = lhs_len < rhs_len ? lhs_len : rhs_len; - int rv = CompareAble_.Same; - for (int i = 0; i < min; i++) { - rv = (lhs[i + lhs_bgn] & 0xff) - (rhs[i + rhs_bgn] & 0xff); // PATCH.JAVA:need to convert to unsigned byte - if (rv != CompareAble_.Same) return rv > CompareAble_.Same ? CompareAble_.More : CompareAble_.Less; // NOTE: changed from if (rv != CompareAble_.Same) return rv; DATE:2013-04-25 - } - return Int_.Compare(lhs_len, rhs_len); // lhs and rhs share same beginning bytes; return len comparisons - } - public static boolean Eq(byte[] src, byte[] val) {return Eq(src, 0, src == null ? 0 : src.length, val);} - public static boolean Eq(byte[] src, int src_bgn, int src_end, byte[] val) { - if (src == null && val == null) return true; - else if (src == null || val == null) return false; - if (src_bgn < 0) return false; - int val_len = val.length; - if (val_len != src_end - src_bgn) return false; - int src_len = src.length; - for (int i = 0; i < val_len; i++) { - int src_pos = i + src_bgn; - if (src_pos == src_len) return false; - if (val[i] != src[src_pos]) return false; - } - return true; - } - public static boolean Eq_ci_a7(byte[] lhs, byte[] rhs, int rhs_bgn, int rhs_end) { - if (lhs == null && rhs == null) return true; - else if (lhs == null || rhs == null) return false; - int lhs_len = lhs.length; - int rhs_len = rhs_end - rhs_bgn; - if (lhs_len != rhs_len) return false; - for (int i = 0; i < lhs_len; i++) { - byte lhs_b = lhs[i]; if (lhs_b > 64 && lhs_b < 91) lhs_b += 32; // lowercase - byte rhs_b = rhs[i + rhs_bgn]; if (rhs_b > 64 && rhs_b < 91) rhs_b += 32; // lowercase - if (lhs_b != rhs_b) return false; - } - return true; - } - public static boolean Match(byte[] src, byte[] find) {return Match(src, 0, src.length, find, 0, find.length);} - public static boolean Match(byte[] src, int src_bgn, byte[] find) {return Match(src, src_bgn, src.length, find, 0, find.length);} - public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find) {return Match(src, src_bgn, src_end, find, 0, find.length);} - public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end) { - if (src_bgn == -1) return false; - int src_len = src.length; - if (src_end > src_len) src_end = src_len; // must limit src_end to src_len, else ArrayIndexOutOfBounds below; DATE:2015-01-31 - int find_len = find_end - find_bgn; - if (find_len != src_end - src_bgn) return false; - if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" - for (int i = 0; i < find_len; i++) { - int pos = src_bgn + i; - if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc - if (src[pos] != find[i + find_bgn]) return false; - } - return true; - } - public static boolean Match_w_swap(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end, byte swap_src, byte swap_trg) {// same as above, but used by XOWA for ttl matches; - int src_len = src.length; - if (src_end > src_len) src_end = src_len; // must limit src_end to src_len, else ArrayIndexOutOfBounds below; DATE:2015-01-31 - int find_len = find_end - find_bgn; - if (find_len != src_end - src_bgn) return false; - if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" - for (int i = 0; i < find_len; i++) { - int pos = src_bgn + i; - if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc - byte src_byte = src[pos]; if (src_byte == swap_src) src_byte = swap_trg; - byte trg_byte = find[i + find_bgn]; if (trg_byte == swap_src) trg_byte = swap_trg; - if (src_byte != trg_byte) return false; - } - return true; - } - public static boolean Match_bwd_any(byte[] src, int src_end, int src_bgn, byte[] find) { // NOTE: utf8 doesn't matter (matching byte for byte) - int find_len = find.length; - for (int i = 0; i < find_len; i++) { - int src_pos = src_end - i; - int find_pos = find_len - i - 1; - if (src_pos < src_bgn) return false; // ran out of src; exit; EX: src=ab; find=abc - if (src[src_pos] != find[find_pos]) return false; - } - return true; - } - public static int To_int_by_a7(byte[] v) { - int v_len = v.length; - int mod = 8 * (v_len - 1); - int rv = 0; - for (int i = 0; i < v_len; i++) { - rv |= v[i] << mod; - mod -= 8; - } - return rv; -// return ((0xFF & v[0]) << 24) -// | ((0xFF & v[1]) << 16) -// | ((0xFF & v[2]) << 8) -// | (0xFF & v[3]); - } - public static byte[] To_a7_bry(int val, int pad_len) {return To_a7_bry(val, null, 0, pad_len);} - public static byte[] To_a7_bry(int val, byte[] ary, int aryPos, int pad_len) { - int neg = 0; - if (val < 0) { - val *= -1; - neg = 1; - } - int digits = val == 0 ? 0 : Math_.Log10(val); - digits += 1; // digits = log + 1; EX: Log(1-9) = 0, Log(10-99) = 1 - int ary_len = digits + neg, aryBgn = aryPos, pad = 0; - if (ary_len < pad_len) { // padding specified - pad = pad_len - ary_len; - ary_len = pad_len; - } - if (ary == null) ary = new byte[ary_len]; - long factor = 1; // factor needs to be long to handle 1 billion (for which factor would be 10 billion) - for (int i = 0; i < digits; i++) // calc maxFactor - factor *= 10; - if (neg == 1) ary[0] = Byte_NegSign; - - for (int i = 0; i < pad; i++) // fill ary with pad - ary[i + aryBgn] = Byte_ascii.To_a7_str(0); - aryBgn += pad; // advance aryBgn by pad - for (int i = neg; i < ary_len - pad; i++) { - int denominator = (int)(factor / 10); // cache denominator to check for divide by 0 - int digit = denominator == 0 ? 0 : (int)((val % factor) / denominator); - ary[aryBgn + i] = Byte_ascii.To_a7_str(digit); - factor /= 10; - } - return ary; - } - public static byte[] new_by_int(int v) { - byte b0 = (byte)(v >> 24); - byte b1 = (byte)(v >> 16); - byte b2 = (byte)(v >> 8); - byte b3 = (byte)(v); - if (b0 != 0) return new byte[] {b0, b1, b2, b3}; - else if (b1 != 0) return new byte[] {b1, b2, b3}; - else if (b2 != 0) return new byte[] {b2, b3}; - else return new byte[] {b3}; - } - public static boolean To_bool_or(byte[] raw, boolean or) { - return Bry_.Eq(raw, Bool_.True_bry) ? true : or; - } - public static boolean To_bool_by_int(byte[] ary) { - int rv = To_int_or(ary, 0, ary.length, Int_.Min_value, Bool_.Y, null); - switch (rv) { - case 0: return false; - case 1: return true; - default: throw Err_.new_wo_type("could not parse to boolean int", "val", String_.new_u8(ary)); - } - } - public static byte To_int_as_byte(byte[] ary, int bgn, int end, byte or) {return (byte)To_int_or(ary, bgn, end, or);} - public static int To_int(byte[] ary) {return To_int_or_fail(ary, 0, ary.length);} - public static int To_int_or_fail(byte[] ary, int bgn, int end) { - int rv = To_int_or(ary, bgn, end, Int_.Min_value, Bool_.Y, null); - if (rv == Int_.Min_value) throw Err_.new_wo_type("could not parse to int", "val", String_.new_u8(ary, bgn, end)); - return rv; - } - public static int To_int_or_neg1(byte[] ary) {return To_int_or(ary, 0 , ary.length, -1, Bool_.Y, null);} - public static int To_int_or(byte[] ary, int or) {return To_int_or(ary, 0 , ary.length, or, Bool_.Y, null);} - public static int To_int_or(byte[] ary, int bgn, int end, int or) {return To_int_or(ary, bgn , end , or, Bool_.Y, null);} - public static int To_int_or__strict(byte[] ary, int or) {return To_int_or(ary, 0 , ary.length, or, Bool_.N, null);} - private static int To_int_or(byte[] ary, int bgn, int end, int or, boolean sign_is_valid, byte[] ignore_ary) { - if ( ary == null - || end == bgn // null-len - ) return or; - int rv = 0, multiple = 1; - for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 - byte b = ary[i]; - switch (b) { - case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: - case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: - rv += multiple * (b - Byte_ascii.Num_0); - multiple *= 10; - break; - case Byte_ascii.Dash: - return i == bgn && sign_is_valid ? rv * -1 : or; - case Byte_ascii.Plus: - return i == bgn && sign_is_valid ? rv : or; - default: - boolean invalid = true; - if (ignore_ary != null) { - int ignore_ary_len = ignore_ary.length; - for (int j = 0; j < ignore_ary_len; j++) { - if (b == ignore_ary[j]) { - invalid = false; - break; - } - } - } - if (invalid) return or; - break; - } - } - return rv; - } - public static int To_int_or__trim_ws(byte[] ary, int bgn, int end, int or) { // NOTE: same as To_int_or, except trims ws at bgn / end; DATE:2014-02-09 - if (end == bgn) return or; // null len - int rv = 0, multiple = 1; - boolean numbers_seen = false, ws_seen = false; - for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 - byte b = ary[i]; - switch (b) { - case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: - case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: - rv += multiple * (b - Byte_ascii.Num_0); - multiple *= 10; - if (ws_seen) // "number ws number" pattern; invalid ws in middle; see tests - return or; - numbers_seen = true; - break; - case Byte_ascii.Dash: - return i == bgn ? rv * -1 : or; - case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: - if (numbers_seen) - ws_seen = true; - break; - default: return or; - } - } - return rv; - } - public static int To_int_or__lax(byte[] ary, int bgn, int end, int or) { - if (end == bgn) return or; // null-len - int end_num = end; - for (int i = bgn; i < end; i++) { - byte b = ary[i]; - switch (b) { - case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: - case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: - break; - case Byte_ascii.Dash: - if (i != bgn) { - end_num = i; - i = end; - } - break; - default: - end_num = i; - i = end; - break; - } - } - return To_int_or(ary, bgn, end_num, or); - } - public static long To_long_or(byte[] ary, long or) {return To_long_or(ary, null, 0, ary.length, or);} - public static long To_long_or(byte[] ary, byte[] ignore_ary, int bgn, int end, long or) { - if ( ary == null - || end == bgn // null-len - ) return or; - long rv = 0, multiple = 1; - for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 - byte b = ary[i]; - switch (b) { - case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: - case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: - rv += multiple * (b - Byte_ascii.Num_0); - multiple *= 10; - break; - case Byte_ascii.Dash: - return i == bgn ? rv * -1 : or; - case Byte_ascii.Plus: - return i == bgn ? rv : or; - default: - boolean invalid = true; - if (ignore_ary != null) { - int ignore_ary_len = ignore_ary.length; - for (int j = 0; j < ignore_ary_len; j++) { - if (b == ignore_ary[j]) { - invalid = false; - break; - } - } - } - if (invalid) return or; - break; - } - } - return rv; - } - public static double To_double(byte[] ary, int bgn, int end) {return Double_.parse(String_.new_u8(ary, bgn, end));} - public static double To_double_or(byte[] bry, double or) {return Double_.parse_or(String_.new_u8(bry, 0, bry.length), or);} - public static double To_double_or(byte[] ary, int bgn, int end, double or) {return Double_.parse_or(String_.new_u8(ary, bgn, end), or);} - public static Decimal_adp To_decimal(byte[] ary, int bgn, int end) {return Decimal_adp_.parse(String_.new_u8(ary, bgn, end));} - public static byte[][] Ary_add(byte[][] lhs, byte[][] rhs) { - int lhs_len = lhs.length, rhs_len = rhs.length; - if (lhs_len == 0) return rhs; - else if (rhs_len == 0) return lhs; - else { - byte[][] rv = new byte[lhs_len + rhs_len][]; - for (int i = 0; i < lhs_len; i++) - rv[i] = lhs[i]; - for (int i = 0; i < rhs_len; i++) - rv[i + lhs_len] = rhs[i]; - return rv; - } - } - public static byte[][] Ary(byte[]... ary) {return ary;} - public static byte[][] Ary(String... ary) { - int ary_len = ary.length; - byte[][] rv = new byte[ary_len][]; - for (int i = 0; i < ary_len; i++) { - String itm = ary[i]; - rv[i] = itm == null ? null : Bry_.new_u8(itm); - } - return rv; - } - public static byte[][] Ary_obj(Object... ary) { - if (ary == null) return Bry_.Ary_empty; - int ary_len = ary.length; - byte[][] rv = new byte[ary_len][]; - for (int i = 0; i < ary_len; i++) { - Object itm = ary[i]; - rv[i] = itm == null ? null : Bry_.new_u8(Object_.Xto_str_strict_or_empty(itm)); - } - return rv; - } - public static boolean Ary_eq(byte[][] lhs, byte[][] rhs) { - int lhs_len = lhs.length; - int rhs_len = rhs.length; - if (lhs_len != rhs_len) return false; - for (int i = 0; i < lhs_len; ++i) - if (!Bry_.Eq(lhs[i], rhs[i])) return false; - return true; - } - public static final byte Dlm_fld = (byte)'|', Dlm_row = (byte)'\n', Dlm_quote = (byte)'"', Dlm_null = 0, Ascii_zero = 48; - public static final String Fmt_csvDte = "yyyyMMdd HHmmss.fff"; - public static DateAdp ReadCsvDte(byte[] ary, Int_obj_ref posRef, byte lkp) {// ASSUME: fmt = yyyyMMdd HHmmss.fff - int y = 0, M = 0, d = 0, H = 0, m = 0, s = 0, f = 0; - int bgn = posRef.Val(); - y += (ary[bgn + 0] - Ascii_zero) * 1000; - y += (ary[bgn + 1] - Ascii_zero) * 100; - y += (ary[bgn + 2] - Ascii_zero) * 10; - y += (ary[bgn + 3] - Ascii_zero); - M += (ary[bgn + 4] - Ascii_zero) * 10; - M += (ary[bgn + 5] - Ascii_zero); - d += (ary[bgn + 6] - Ascii_zero) * 10; - d += (ary[bgn + 7] - Ascii_zero); - H += (ary[bgn + 9] - Ascii_zero) * 10; - H += (ary[bgn + 10] - Ascii_zero); - m += (ary[bgn + 11] - Ascii_zero) * 10; - m += (ary[bgn + 12] - Ascii_zero); - s += (ary[bgn + 13] - Ascii_zero) * 10; - s += (ary[bgn + 14] - Ascii_zero); - f += (ary[bgn + 16] - Ascii_zero) * 100; - f += (ary[bgn + 17] - Ascii_zero) * 10; - f += (ary[bgn + 18] - Ascii_zero); - if (ary[bgn + 19] != lkp) throw Err_.new_wo_type("csv date is invalid", "txt", String_.new_u8__by_len(ary, bgn, 20)); - posRef.Val_add(19 + 1); // +1=lkp.len - return DateAdp_.new_(y, M, d, H, m, s, f); - } - public static String ReadCsvStr(byte[] ary, Int_obj_ref posRef, byte lkp) {return String_.new_u8(ReadCsvBry(ary, posRef, lkp, true));} - public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp) {return ReadCsvBry(ary, posRef, lkp, true);} - public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp, boolean make) { - int bgn = posRef.Val(), aryLen = ary.length; - Bry_bfr bb = null; - if (aryLen > 0 && ary[0] == Dlm_quote) { - int pos = bgn + 1; // +1 to skip quote - if (make) bb = Bry_bfr_.New(); - while (true) { - if (pos == aryLen) throw Err_.new_wo_type("endOfAry reached, but no quote found", "txt", String_.new_u8__by_len(ary, bgn, pos)); - byte b = ary[pos]; - if (b == Dlm_quote) { - if (pos == aryLen - 1) throw Err_.new_wo_type("endOfAry reached, quote found but lkp not", "txt", String_.new_u8__by_len(ary, bgn, pos)); - byte next = ary[pos + 1]; - if (next == Dlm_quote) { // byte followed by quote - if (make) bb.Add_byte(b); - pos += 2; - } - else if (next == lkp) { - posRef.Val_(pos + 2); // 1=endQuote;1=lkp; - return make ? bb.To_bry() : Bry_.Empty; - } - else throw Err_.new_wo_type("quote found, but not doubled", "txt", String_.new_u8__by_len(ary, bgn, pos + 1)); - } - else { - if (make) bb.Add_byte(b); - pos++; - } - } - } - else { - for (int i = bgn; i < aryLen; i++) { - if (ary[i] == lkp) { - posRef.Val_(i + 1); // +1 = lkp.Len - return make ? Bry_.Mid(ary, bgn, i) : Bry_.Empty; - } - } - throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "txt", String_.new_u8__by_len(ary, bgn, aryLen)); - } - } - public static int ReadCsvInt(byte[] ary, Int_obj_ref posRef, byte lkp) { - int bgn = posRef.Val(); - int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); - if (pos == Bry_find_.Not_found) throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "bgn", bgn); - int rv = Bry_.To_int_or(ary, posRef.Val(), pos, -1); - posRef.Val_(pos + 1); // +1 = lkp.Len - return rv; - } - public static double ReadCsvDouble(byte[] ary, Int_obj_ref posRef, byte lkp) { - int bgn = posRef.Val(); - int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); - if (pos == Bry_find_.Not_found) throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "bgn", bgn); - double rv = Bry_.To_double(ary, posRef.Val(), pos); - posRef.Val_(pos + 1); // +1 = lkp.Len - return rv; - } - public static void ReadCsvNext(byte[] ary, Int_obj_ref posRef, byte lkp) { - int bgn = posRef.Val(); - int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); - posRef.Val_(pos + 1); // +1 = lkp.Len - } - public static byte Byte_NegSign = (byte)'-'; - public static byte[] Replace_create(byte[] src, byte find, byte replace) { - byte[] rv = Bry_.Copy(src); - Replace_reuse(rv, find, replace); - return rv; - } - public static void Replace_reuse(byte[] src, byte find, byte replace) { - int src_len = src.length; - for (int i = 0; i < src_len; i++) { - if (src[i] == find) src[i] = replace; - } - } - public static byte[] Replace(byte[] src, byte find, byte replace) {return Replace(src, 0, src.length, find, replace);} - public static byte[] Replace(byte[] src, int bgn, int end, byte find, byte replace) { - int src_len = src.length; - byte[] rv = new byte[src_len]; - for (int i = bgn; i < end; ++i) { - byte b = src[i]; - rv[i] = b == find ? replace : b; - } - for (int i = end; i < src_len; ++i) - rv[i] = src[i]; - return rv; - } - public static byte[] Replace_safe(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) { - if (src == null || find == null || repl == null) return null; - return Replace(bfr, src, find, repl, 0, src.length); - } - public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) {return Replace(bfr, src, find, repl, 0, src.length);} - public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end) {return Replace(bfr, src, find, repl, src_bgn, src_end, Int_.Max_value);} - public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end, int limit) { - int pos = src_bgn; - boolean dirty = false; - int find_len = find.length; - int bfr_bgn = pos; - int replace_count = 0; - while (pos < src_end) { - int find_pos = Bry_find_.Find_fwd(src, find, pos); - if (find_pos == Bry_find_.Not_found) break; - dirty = true; - bfr.Add_mid(src, bfr_bgn, find_pos); - bfr.Add(repl); - pos = find_pos + find_len; - bfr_bgn = pos; - ++replace_count; - if (replace_count == limit) break; - } - if (dirty) - bfr.Add_mid(src, bfr_bgn, src_end); - return dirty ? bfr.To_bry_and_clear() : src; - } - public static byte[] Replace(byte[] src, byte[] find, byte[] replace) {return Replace_between(src, find, null, replace);} - public static byte[] Replace_between(byte[] src, byte[] bgn, byte[] end, byte[] replace) { - Bry_bfr bfr = Bry_bfr_.New(); - boolean replace_all = end == null; - int src_len = src.length, bgn_len = bgn.length, end_len = replace_all ? 0 : end.length; - int pos = 0; - while (true) { - if (pos >= src_len) break; - int bgn_pos = Bry_find_.Find_fwd(src, bgn, pos); - if (bgn_pos == Bry_find_.Not_found) { - bfr.Add_mid(src, pos, src_len); - break; - } - else { - int bgn_rhs = bgn_pos + bgn_len; - int end_pos = replace_all ? bgn_rhs : Bry_find_.Find_fwd(src, end, bgn_rhs); - if (end_pos == Bry_find_.Not_found) { - bfr.Add_mid(src, pos, src_len); - break; - } - else { - bfr.Add_mid(src, pos, bgn_pos); - bfr.Add(replace); - pos = end_pos + end_len; - } - } - } - return bfr.To_bry_and_clear(); - } - public static byte[] Replace_many(byte[] src, byte[] find, byte[] repl) { - Bry_bfr bfr = null; - int src_len = src.length; - int find_len = find.length; - - int pos = 0; - while (true) { - // find find_bgn - int find_bgn = Bry_find_.Find_fwd(src, find, pos, src_len); - - // exit if nothing found - if (find_bgn == Bry_find_.Not_found) - break; - - // lazy-instantiation - if (bfr == null) - bfr = Bry_bfr_.New(); - - // add everything up to find_bgn - bfr.Add_mid(src, pos, find_bgn); - - // add repl - bfr.Add(repl); - - // move pos forward - pos = find_bgn + find_len; - } - - // nothing found; return src - if (bfr == null) - return src; - else { - // add rest - bfr.Add_mid(src, pos, src_len); - return bfr.To_bry_and_clear(); - } - } - public static int Trim_end_pos(byte[] src, int end) { - for (int i = end - 1; i > -1; i--) { - switch (src[i]) { - case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Space: - break; - default: - return i + 1; - } - } - return 0; - } - public static byte[] Increment_last(byte[] ary) {return Increment_last(ary, ary.length - 1);} - public static byte[] Increment_last(byte[] ary, int end_idx) { - for (int i = end_idx; i > -1; i--) { - byte end_val_old = ary[i]; - byte end_val_new = (byte)(end_val_old + 1); - ary[i] = end_val_new; - if (end_val_new > (end_val_old & 0xff)) break; // PATCH.JAVA:need to convert to unsigned byte - } - return ary; - } - public static byte[] Ucase__all(byte[] src) {return Xcase__all(Bool_.Y, src, 0, -1);} - public static byte[] Lcase__all(byte[] src) {return Xcase__all(Bool_.N, src, 0, -1);} - public static byte[] Lcase__all(byte[] src, int bgn, int end) {return Xcase__all(Bool_.N, src, bgn, end);} - private static byte[] Xcase__all(boolean upper, byte[] src, int bgn, int end) { - if (src == null) return null; - int len = end == -1 ? src.length : end - bgn; if (len == 0) return src; - byte[] rv = new byte[len]; - for (int i = 0; i < len; ++i) { - byte b = src[i + bgn]; - if (upper) { - if (b > 96 && b < 123) b -= 32; - } - else { - if (b > 64 && b < 91) b += 32; - } - rv[i] = b; - } - return rv; - } - public static byte[] Xcase__build__all(Bry_bfr tmp, boolean upper, byte[] src) { - if (src == null) return null; - int src_bgn = 0; - int src_end = src.length; - int lbound = 96, ubound = 123; - if (!upper) { - lbound = 64; ubound = 91; - } - - boolean dirty = false; - for (int i = src_bgn; i < src_end; i++) { - byte b = src[i]; - if (b > lbound && b < ubound) { - if (!dirty) { - dirty = true; - tmp.Add_mid(src, src_bgn, i); - } - if (upper) - b -= 32; - else - b += 32; - } - if (dirty) - tmp.Add_byte(b); - } - return dirty ? tmp.To_bry_and_clear() : src; - } - public static byte[] Ucase__1st(byte[] src) {return Xcase__1st(Bool_.Y, src);} - public static byte[] Lcase__1st(byte[] src) {return Xcase__1st(Bool_.N, src);} - private static byte[] Xcase__1st(boolean upper, byte[] src) { - if (src == null) return null; - int len = src.length; if (len == 0) return src; - byte[] rv = new byte[len]; - byte b = src[0]; - if (upper) { - if (b > 96 && b < 123) b -= 32; - } - else { - if (b > 64 && b < 91) b += 32; - } - rv[0] = b; - for (int i = 1; i < len; ++i) { - rv[i] = src[i]; - } - return rv; - } - public static byte[] Null_if_empty(byte[] v) {return Len_eq_0(v) ? null : v;} - - public static byte[] Escape_ws(byte[] bry) {Bry_bfr bfr = Bry_bfr_.Get(); byte[] rv = Escape_ws(bfr, bry); bfr.Mkr_rls(); return rv;} - public static byte[] Escape_ws(Bry_bfr bfr, byte[] src) { - boolean dirty = false; - int len = src.length; - for (int i = 0; i < len; ++i) { - byte b = src[i]; - byte escape = Byte_.Zero; - switch (b) { - case Byte_ascii.Tab: escape = Byte_ascii.Ltr_t; break; - case Byte_ascii.Nl: escape = Byte_ascii.Ltr_n; break; - case Byte_ascii.Cr: escape = Byte_ascii.Ltr_r; break; - default: if (dirty) bfr.Add_byte(b); break; - } - if (escape != Byte_.Zero) { - if (!dirty) { - dirty = true; - bfr.Add_mid(src, 0, i); - } - bfr.Add_byte_backslash().Add_byte(escape); - } - } - return dirty ? bfr.To_bry_and_clear() : src; - } - public static byte[] Resolve_escape(Bry_bfr bfr, byte escape, byte[] raw, int bgn, int end) { - int pos = bgn; - boolean dirty = false; - while (pos < end) { - byte b = raw[pos]; - if (b == escape) { - if (!dirty) { - dirty = true; - bfr.Add_mid(raw, bgn, pos); - } - ++pos; - if (pos < end) { // check for eos; note that this ignores trailing "\"; EX: "a\" -> "a" - bfr.Add_byte(raw[pos]); - ++pos; - } - } - else { - if (dirty) bfr.Add_byte(b); - ++pos; - } - } - return dirty ? bfr.To_bry_and_clear() : raw; - } - public static void Clear(byte[] bry) { - int len = bry.length; - for (int i = 0; i < len; ++i) - bry[i] = Byte_.Zero; - } - public static byte[] Replace_nl_w_tab(byte[] src, int bgn, int end) { - return Bry_.Replace(Bry_.Mid(src, bgn, end), Byte_ascii.Nl, Byte_ascii.Tab); - } - public static byte[] Escape_html(byte[] src) { - return Escape_html(null, Bool_.N, src, 0, src.length); - } - public static byte[] Escape_html(Bry_bfr bfr, boolean ws, byte[] src, int src_bgn, int src_end) { // uses PHP rules for htmlspecialchars; REF.PHP:http://php.net/manual/en/function.htmlspecialchars.php - boolean dirty = false; - int cur = src_bgn; - int prv = cur; - boolean called_by_bry = bfr == null; - - // loop over chars - while (true) { - // if EOS, exit - if (cur == src_end) { - if (dirty) { - bfr.Add_mid(src, prv, src_end); - } - break; - } - - // check current byte if escaped - byte b = src[cur]; - byte[] escaped = null; - switch (b) { - case Byte_ascii.Amp: escaped = Gfh_entity_.Amp_bry; break; - case Byte_ascii.Quote: escaped = Gfh_entity_.Quote_bry; break; - case Byte_ascii.Apos: escaped = Gfh_entity_.Apos_num_bry; break; - case Byte_ascii.Lt: escaped = Gfh_entity_.Lt_bry; break; - case Byte_ascii.Gt: escaped = Gfh_entity_.Gt_bry; break; - case Byte_ascii.Nl: if (ws) escaped = Gfh_entity_.Nl_bry; break; - case Byte_ascii.Cr: if (ws) escaped = Gfh_entity_.Cr_bry; break; - case Byte_ascii.Tab: if (ws) escaped = Gfh_entity_.Tab_bry; break; - } - - // not escaped; increment and continue - if (escaped == null) { - cur++; - continue; - } - // escaped - else { - dirty = true; - if (bfr == null) bfr = Bry_bfr_.New(); - - if (prv < cur) - bfr.Add_mid(src, prv, cur); - bfr.Add(escaped); - cur++; - prv = cur; - } - } - - if (dirty) { - if (called_by_bry) - return bfr.To_bry_and_clear(); - else - return null; - } - else { - if (called_by_bry) { - if (src_bgn == 0 && src_end == src.length) - return src; - else - return Bry_.Mid(src, src_bgn, src_end); - } - else { - bfr.Add_mid(src, src_bgn, src_end); - return null; - } - } - } -} +package gplx; + +import gplx.core.primitives.Int_obj_ref; +import gplx.langs.htmls.entitys.Gfh_entity_; + +public class Bry_ { + public static final String Cls_val_name = "byte[]"; + public static final byte[] Empty = new byte[0]; + public static final byte[][] Ary_empty = new byte[0][]; + public static final Class Cls_ref_type = byte[].class; + public static byte[] cast(Object val) {return (byte[])val;} + public static byte[] New_by_byte(byte b) {return new byte[] {b};} + public static byte[] New_by_ints(int... ary) { + int len = ary.length; + byte[] rv = new byte[len]; + for (int i = 0; i < len; i++) + rv[i] = (byte)ary[i]; + return rv; + } + public static byte[] New_by_objs(Bry_bfr bfr, Object... ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) { + Object itm = ary[i]; + Class type = Type_.Type_by_obj(itm); + if (Type_.Eq(type, int.class)) bfr.Add_byte((byte)Int_.Cast(itm)); + else if (Type_.Eq(type, String.class)) bfr.Add_str_u8((String)itm); + else if (Type_.Eq(type, byte[].class)) bfr.Add((byte[])itm); + else throw Err_.new_unhandled(Type_.Canonical_name(type)); + } + return bfr.To_bry_and_clear(); + } + public static byte[] Coalesce_to_empty(byte[] v) {return v == null ? Bry_.Empty : v;} + public static byte[] Coalesce(byte[] v, byte[] or) {return v == null ? or : v;} + public static byte[] new_a7(String str) { + if (str == null) return null; + int str_len = str.length(); + if (str_len == 0) return Bry_.Empty; + byte[] rv = new byte[str_len]; + for (int i = 0; i < str_len; ++i) { + char c = str.charAt(i); + if (c > 128) c = '?'; + rv[i] = (byte)c; + } + return rv; + } + public static byte[] new_u8_safe(String str) {return str == null ? null : new_u8(str);} + public static byte[] new_u8(String str) { + try { + int str_len = str.length(); + if (str_len == 0) return Bry_.Empty; + int bry_len = new_u8__by_len(str, str_len); + byte[] rv = new byte[bry_len]; + new_u8__write(str, str_len, rv, 0); + return rv; + } + catch (Exception e) {throw Err_.new_exc(e, "core", "invalid UTF-8 sequence", "s", str);} + } + public static int new_u8__by_len(String s, int s_len) { + int rv = 0; + for (int i = 0; i < s_len; ++i) { + char c = s.charAt(i); + int c_len = 0; + if ( c < 128) c_len = 1; // 1 << 7 + else if ( c < 2048) c_len = 2; // 1 << 11 + else if ( (c > 55295) // 0xD800 + && (c < 56320)) c_len = 4; // 0xDFFF + else c_len = 3; // 1 << 16 + if (c_len == 4) ++i; // surrogate is 2 wide, not 1 + rv += c_len; + } + return rv; + } + public static byte[] New_u8_nl_apos(String... lines) { + Bry_bfr bfr = Bry_bfr_.Get(); + try { + New_u8_nl_apos(bfr, lines); + return bfr.To_bry_and_clear(); + } + finally {bfr.Mkr_rls();} + } + public static void New_u8_nl_apos(Bry_bfr bfr, String... lines) { + int lines_len = lines.length; + for (int i = 0; i < lines_len; ++i) { + if (i != 0) bfr.Add_byte_nl(); + byte[] line = Bry_.new_u8(lines[i]); + boolean dirty = false; + int prv = 0; + int line_len = line.length; + for (int j = 0; j < line_len; ++j) { + byte b = line[j]; + if (b == Byte_ascii.Apos) { + bfr.Add_mid(line, prv, j); + bfr.Add_byte(Byte_ascii.Quote); + dirty = true; + prv = j + 1; + } + } + if (dirty) + bfr.Add_mid(line, prv, line_len); + else + bfr.Add(line); + } + } + public static void new_u8__write(String str, int str_len, byte[] bry, int bry_pos) { + for (int i = 0; i < str_len; ++i) { + char c = str.charAt(i); + if ( c < 128) { + bry[bry_pos++] = (byte)c; + } + else if ( c < 2048) { + bry[bry_pos++] = (byte)(0xC0 | (c >> 6)); + bry[bry_pos++] = (byte)(0x80 | (c & 0x3F)); + } + else if ( (c > 55295) // 0xD800 + && (c < 56320)) { // 0xDFFF + if (i >= str_len) throw Err_.new_wo_type("incomplete surrogate pair at end of String", "char", c); + char nxt_char = str.charAt(i + 1); + int v = 0x10000 + (c - 0xD800) * 0x400 + (nxt_char - 0xDC00); + bry[bry_pos++] = (byte)(0xF0 | (v >> 18)); + bry[bry_pos++] = (byte)(0x80 | (v >> 12) & 0x3F); + bry[bry_pos++] = (byte)(0x80 | (v >> 6) & 0x3F); + bry[bry_pos++] = (byte)(0x80 | (v & 0x3F)); + ++i; + } + else { + bry[bry_pos++] = (byte)(0xE0 | (c >> 12)); + bry[bry_pos++] = (byte)(0x80 | (c >> 6) & 0x3F); + bry[bry_pos++] = (byte)(0x80 | (c & 0x3F)); + } + } + } + public static byte[] Copy(byte[] src) { + int src_len = src.length; + byte[] trg = new byte[src_len]; + for (int i = 0; i < src_len; ++i) + trg[i] = src[i]; + return trg; + } + public static byte[] Resize(byte[] src, int trg_len) {return Resize(src, 0, trg_len);} + public static byte[] Resize(byte[] src, int src_bgn, int trg_len) { + byte[] trg = new byte[trg_len]; + int src_len = src.length; if (src_len > trg_len) src_len = trg_len; // trg_len can be less than src_len + Copy_to(src, src_bgn, src_len, trg, 0); + return trg; + } + public static byte[] Repeat_space(int len) {return Repeat(Byte_ascii.Space, len);} + public static byte[] Repeat(byte b, int len) { + byte[] rv = new byte[len]; + for (int i = 0; i < len; i++) + rv[i] = b; + return rv; + } + public static byte[] Repeat_bry(byte[] bry, int len) { + int bry_len = bry.length; + int rv_len = len * bry_len; + byte[] rv = new byte[rv_len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < bry_len; j++) { + rv[(i * bry_len) + j] = bry[j]; + } + } + return rv; + } + public static byte[] Add(byte[] src, byte b) { + int src_len = src.length; + byte[] rv = new byte[src_len + 1]; + Copy_to(src, 0, src_len, rv, 0); + rv[src_len] = b; + return rv; + } + public static byte[] Add(byte b, byte[] src) { + int src_len = src.length; + byte[] rv = new byte[src_len + 1]; + Copy_to(src, 0, src_len, rv, 1); + rv[0] = b; + return rv; + } + public static byte[] Add(byte[]... all) { + int all_len = all.length, rv_len = 0; + for (int i = 0; i < all_len; ++i) { + byte[] cur = all[i]; if (cur == null) continue; + rv_len += cur.length; + } + byte[] rv = new byte[rv_len]; + int rv_idx = 0; + for (int i = 0; i < all_len; ++i) { + byte[] cur = all[i]; if (cur == null) continue; + int cur_len = cur.length; + for (int j = 0; j < cur_len; ++j) + rv[rv_idx++] = cur[j]; + } + return rv; + } + public static byte[] Add_w_dlm(byte[] dlm, byte[]... ary) { + int ary_len = ary.length; + if (ary_len == 0) return Bry_.Empty; + int dlm_len = dlm.length; + int rv_len = dlm_len * (ary_len - 1); // rv will have at least as many dlms as itms - 1 + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (itm != null) rv_len += itm.length; + } + int rv_pos = 0; + byte[] rv = new byte[rv_len]; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (i != 0) { + for (int j = 0; j < dlm_len; j++) { + rv[rv_pos++] = dlm[j]; + } + } + if (itm == null) continue; + int itm_len = itm.length; + for (int j = 0; j < itm_len; j++) { + rv[rv_pos++] = itm[j]; + } + } + return rv; + } + public static byte[] Add_w_dlm(byte dlm, byte[]... ary) { + int ary_len = ary.length; + if (ary_len == 0) return Bry_.Empty; + int rv_len = ary_len - 1; // rv will have at least as many dlms as itms - 1 + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (itm != null) rv_len += itm.length; + } + int rv_pos = 0; + byte[] rv = new byte[rv_len]; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (i != 0) rv[rv_pos++] = dlm; + if (itm == null) continue; + int itm_len = itm.length; + for (int j = 0; j < itm_len; j++) { + rv[rv_pos++] = itm[j]; + } + } + return rv; + } + public static int Len(byte[] v) {return v == null ? 0 : v.length;} + public static boolean Len_gt_0(byte[] v) {return v != null && v.length > 0;} + public static boolean Len_eq_0(byte[] v) {return v == null || v.length == 0;} + public static byte Get_at_end(byte[] bry) {return bry[bry.length - 1];} // don't bother checking for errors; depend on error trace + public static boolean Has_at(byte[] src, int src_len, int pos, byte b) {return (pos < src_len) && (src[pos] == b);} + public static boolean Has(byte[] src, byte[] lkp) {return Bry_find_.Find_fwd(src, lkp) != Bry_find_.Not_found;} + public static boolean Has(byte[] src, byte lkp) { + if (src == null) return false; + int len = src.length; + for (int i = 0; i < len; i++) + if (src[i] == lkp) return true; + return false; + } + public static boolean Has_at_bgn(byte[] src, byte lkp) {return Has_at_bgn(src, lkp, 0);} + public static boolean Has_at_bgn(byte[] src, byte lkp, int src_bgn) {return src_bgn < src.length ? src[src_bgn] == lkp : false;} + public static boolean Has_at_bgn(byte[] src, byte[] lkp) {return Has_at_bgn(src, lkp, 0, src.length);} + public static boolean Has_at_bgn(byte[] src, byte[] lkp, int src_bgn, int src_end) { + int lkp_len = lkp.length; + if (lkp_len + src_bgn > src_end) return false; // lkp is longer than src + for (int i = 0; i < lkp_len; i++) { + if (lkp[i] != src[i + src_bgn]) return false; + } + return true; + } + public static boolean Has_at_end(byte[] src, byte lkp) { + if (src == null) return false; + int src_len = src.length; + if (src_len == 0) return false; + return src[src_len - 1] == lkp; + } + public static boolean Has_at_end(byte[] src, byte[] lkp) {int src_len = src.length; return Has_at_end(src, lkp, src_len - lkp.length, src_len);} + public static boolean Has_at_end(byte[] src, byte[] lkp, int src_bgn, int src_end) { + int lkp_len = lkp.length; + if (src_bgn < 0) return false; + int pos = src_end - lkp_len; if (pos < src_bgn) return false; // lkp is longer than src + for (int i = 0; i < lkp_len; i++) { + if (lkp[i] != src[i + pos]) return false; + } + return true; + } + public static void Set(byte[] src, int bgn, int end, byte[] repl) { + int repl_len = repl.length; + for (int i = 0; i < repl_len; i++) + src[i + bgn] = repl[i]; + } + public static void Copy_to(byte[] src, int src_bgn, int src_end, byte[] trg, int trg_bgn) { + int trg_adj = trg_bgn - src_bgn; + for (int i = src_bgn; i < src_end; i++) + trg[i + trg_adj] = src[i]; + } + public static void Copy_to_reversed(byte[] src, int src_bgn, int src_end, byte[] trg, int trg_bgn) { + // copies src to trg, but in reverse order; EX: trg="1" src="432." -> "1.234" + int len = src_end - src_bgn; + for (int i = 0; i < len; i++) + trg[trg_bgn + i] = src[src_end - i - 1]; + } + public static byte[] Replace_one(byte[] orig, byte[] find, byte[] repl) { + // find val + int orig_len = orig.length; + int find_pos = Bry_find_.Find(orig, find, 0, orig_len, true); + if (find_pos == Bry_find_.Not_found) return orig; // nothing found; exit + + // do copy + int find_len = find.length, repl_len = repl.length; + int rv_len = orig_len + repl_len - find_len; + byte[] rv = new byte[rv_len]; + Copy_to(orig, 0 , find_pos, rv, 0 ); // copy orig before repl + Copy_to(repl, 0 , repl_len, rv, find_pos ); // copy repl + Copy_to(orig, find_pos + find_len, orig_len, rv, find_pos + repl_len); // copy orig after repl + return rv; + } + public static void Replace_all_direct(byte[] src, byte find, byte repl) {Replace_all_direct(src, find, repl, 0, src.length);} + public static void Replace_all_direct(byte[] src, byte find, byte repl, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + if (b == find) src[i] = repl; + } + } + public static byte[] Limit(byte[] src, int len) { + if (src == null) return null; + int src_len = src.length; + return len < src_len ? Bry_.Mid(src, 0, len) : src; + } + public static byte[] Mid_by_nearby(byte[] src, int pos, int around) { + int bgn = pos - around; if (bgn < 0) bgn = 0; + int src_len = src.length; + int end = pos + around; if (end > src_len) end = src_len; + return Mid(src, bgn, end); + } + public static byte[] Mid_by_len(byte[] src, int bgn, int len) {return Mid(src, bgn, bgn + len);} + public static byte[] Mid_by_len_safe(byte[] src, int bgn, int len) { + int src_len = src.length; + if (bgn < 0) bgn = 0; + if (len + bgn > src_len) len = (src_len - bgn); + return Mid(src, bgn, bgn + len); + } + public static String MidByLenToStr(byte[] src, int bgn, int len) { + int end = bgn + len; end = Int_.BoundEnd(end, src.length); + byte[] ary = Bry_.Mid(src, bgn, end); + return String_.new_u8(ary); + } + public static byte[] Mid_safe(byte[] src, int bgn, int end) { + if (src == null) return null; + int src_len = src.length; + if (bgn < 0) + bgn = 0; + else if (bgn >= src_len) + bgn = src_len; + + if (end < 0) + end = 0; + else if (end >= src_len) + end = src_len; + + if (bgn > end) + bgn = end; + else if (end < bgn) + end = bgn; + + return Mid(src, bgn, end); + } + public static byte[] Mid(byte[] src, int bgn) {return Mid(src, bgn, src.length);} + public static byte[] Mid_or(byte[] src, int bgn, int end, byte[] or) { + int src_len = src.length; + if ( src == null + || (bgn < 0 || bgn > src_len) + || (end < 0 || end > src_len) + || (end < bgn) + ) + return or; + return bgn == src_len ? Bry_.Empty : Mid(src, bgn, src_len); + } + public static byte[] Mid(byte[] src, int bgn, int end) { + try { + int len = end - bgn; if (len == 0) return Bry_.Empty; + byte[] rv = new byte[len]; + for (int i = bgn; i < end; i++) + rv[i - bgn] = src[i]; + return rv; + } catch (Exception e) {Err_.Noop(e); throw Err_.new_("Bry_", "mid failed", "bgn", bgn, "end", end);} + } + public static byte[] Mid_w_trim(byte[] src, int bgn, int end) { + int len = end - bgn; if (len == 0) return Bry_.Empty; + int actl_bgn = bgn, actl_end = end; + // trim at bgn + boolean chars_seen = false; + for (int i = bgn; i < end; ++i) { + switch (src[i]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + chars_seen = true; + actl_bgn = i; + i = end; + break; + } + } + if (!chars_seen) return Bry_.Empty; // all ws + // trim at end + for (int i = end - 1; i >= actl_bgn; --i) { + switch (src[i]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + break; + default: + actl_end = i + 1; + i = -1; + break; + } + } + // extract mid + len = actl_end - actl_bgn; if (len == 0) return Bry_.Empty; + byte[] rv = new byte[len]; + for (int i = actl_bgn; i < actl_end; ++i) + rv[i - actl_bgn] = src[i]; + return rv; + } + public static byte[] mask_(int len, byte... itms) { + byte[] rv = new byte[len]; + int itms_len = itms.length; + for (int i = 0; i < itms_len; i++) { + byte itm = itms[i]; + rv[itm & 0xFF] = itm; // PATCH.JAVA:need to convert to unsigned byte + } + return rv; + } + public static final byte[] Trim_ary_ws = mask_(256, Byte_ascii.Tab, Byte_ascii.Nl, Byte_ascii.Cr, Byte_ascii.Space); + public static byte[] Trim(byte[] src) {return Trim(src, 0, src.length, true, true, Trim_ary_ws, true);} + public static byte[] Trim(byte[] src, int bgn, int end) {return Trim(src, bgn, end, true, true, Trim_ary_ws, true);} + public static byte[] Trim(byte[] src, int bgn, int end, boolean trim_bgn, boolean trim_end, byte[] trim_ary, boolean reuse_bry_if_noop) { + int txt_bgn = bgn, txt_end = end; + boolean all_ws = true; + if (trim_bgn) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Null) { + txt_bgn = i; + i = end; + all_ws = false; + } + } + if (all_ws) return Bry_.Empty; + } + if (trim_end) { + for (int i = end - 1; i > -1; i--) { + byte b = src[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Null) { + txt_end = i + 1; + i = -1; + all_ws = false; + } + } + if (all_ws) return Bry_.Empty; + } + + if ( reuse_bry_if_noop + && bgn == 0 && end == src.length // Trim is called on entire bry, not subset + && bgn == txt_bgn && end == txt_end // Trim hasn't trimmed anything + ) { + return src; + } + else + return Bry_.Mid(src, txt_bgn, txt_end); + } + public static byte[] Trim_bgn(byte[] v, byte trim, int bgn) { + boolean trimmed = false; + int len = v.length; + int pos = bgn; + for (; pos < len; pos++) { + if (v[pos] == trim) { + trimmed = true; + } + else + break; + } + return trimmed ? Bry_.Mid(v, pos, len) : v; + } + public static byte[] Trim_end(byte[] v, byte trim, int end) { + boolean trimmed = false; + int pos = end - 1; // NOTE: -1 b/c callers will always be passing pos + 1; EX: src, src_len + for (; pos > -1; pos--) { + if (v[pos] == trim) { + trimmed = true; + } + else + break; + } + return trimmed ? Bry_.Mid(v, 0, pos + 1) : v; + } + public static int Compare(byte[] lhs, byte[] rhs) { + if (lhs == null) return CompareAble_.More; + else if (rhs == null) return CompareAble_.Less; + else return Compare(lhs, 0, lhs.length, rhs, 0, rhs.length); + } + public static int Compare(byte[] lhs, int lhs_bgn, int lhs_end, byte[] rhs, int rhs_bgn, int rhs_end) { + int lhs_len = lhs_end - lhs_bgn, rhs_len = rhs_end - rhs_bgn; + int min = lhs_len < rhs_len ? lhs_len : rhs_len; + int rv = CompareAble_.Same; + for (int i = 0; i < min; i++) { + rv = (lhs[i + lhs_bgn] & 0xff) - (rhs[i + rhs_bgn] & 0xff); // PATCH.JAVA:need to convert to unsigned byte + if (rv != CompareAble_.Same) return rv > CompareAble_.Same ? CompareAble_.More : CompareAble_.Less; // NOTE: changed from if (rv != CompareAble_.Same) return rv; DATE:2013-04-25 + } + return Int_.Compare(lhs_len, rhs_len); // lhs and rhs share same beginning bytes; return len comparisons + } + public static boolean Eq(byte[] src, byte[] val) {return Eq(src, 0, src == null ? 0 : src.length, val);} + public static boolean Eq(byte[] src, int src_bgn, int src_end, byte[] val) { + if (src == null && val == null) return true; + else if (src == null || val == null) return false; + if (src_bgn < 0) return false; + int val_len = val.length; + if (val_len != src_end - src_bgn) return false; + int src_len = src.length; + for (int i = 0; i < val_len; i++) { + int src_pos = i + src_bgn; + if (src_pos == src_len) return false; + if (val[i] != src[src_pos]) return false; + } + return true; + } + public static boolean Eq_ci_a7(byte[] lhs, byte[] rhs, int rhs_bgn, int rhs_end) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + int lhs_len = lhs.length; + int rhs_len = rhs_end - rhs_bgn; + if (lhs_len != rhs_len) return false; + for (int i = 0; i < lhs_len; i++) { + byte lhs_b = lhs[i]; if (lhs_b > 64 && lhs_b < 91) lhs_b += 32; // lowercase + byte rhs_b = rhs[i + rhs_bgn]; if (rhs_b > 64 && rhs_b < 91) rhs_b += 32; // lowercase + if (lhs_b != rhs_b) return false; + } + return true; + } + public static boolean Match(byte[] src, byte[] find) {return Match(src, 0, src.length, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, byte[] find) {return Match(src, src_bgn, src.length, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find) {return Match(src, src_bgn, src_end, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end) { + if (src_bgn == -1) return false; + int src_len = src.length; + if (src_end > src_len) src_end = src_len; // must limit src_end to src_len, else ArrayIndexOutOfBounds below; DATE:2015-01-31 + int find_len = find_end - find_bgn; + if (find_len != src_end - src_bgn) return false; + if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" + for (int i = 0; i < find_len; i++) { + int pos = src_bgn + i; + if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[pos] != find[i + find_bgn]) return false; + } + return true; + } + public static boolean Match_w_swap(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end, byte swap_src, byte swap_trg) {// same as above, but used by XOWA for ttl matches; + int src_len = src.length; + if (src_end > src_len) src_end = src_len; // must limit src_end to src_len, else ArrayIndexOutOfBounds below; DATE:2015-01-31 + int find_len = find_end - find_bgn; + if (find_len != src_end - src_bgn) return false; + if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" + for (int i = 0; i < find_len; i++) { + int pos = src_bgn + i; + if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + byte src_byte = src[pos]; if (src_byte == swap_src) src_byte = swap_trg; + byte trg_byte = find[i + find_bgn]; if (trg_byte == swap_src) trg_byte = swap_trg; + if (src_byte != trg_byte) return false; + } + return true; + } + public static boolean Match_bwd_any(byte[] src, int src_end, int src_bgn, byte[] find) { // NOTE: utf8 doesn't matter (matching byte for byte) + int find_len = find.length; + for (int i = 0; i < find_len; i++) { + int src_pos = src_end - i; + int find_pos = find_len - i - 1; + if (src_pos < src_bgn) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[src_pos] != find[find_pos]) return false; + } + return true; + } + public static int To_int_by_a7(byte[] v) { + int v_len = v.length; + int mod = 8 * (v_len - 1); + int rv = 0; + for (int i = 0; i < v_len; i++) { + rv |= v[i] << mod; + mod -= 8; + } + return rv; +// return ((0xFF & v[0]) << 24) +// | ((0xFF & v[1]) << 16) +// | ((0xFF & v[2]) << 8) +// | (0xFF & v[3]); + } + public static byte[] To_a7_bry(int val, int pad_len) {return To_a7_bry(val, null, 0, pad_len);} + public static byte[] To_a7_bry(int val, byte[] ary, int aryPos, int pad_len) { + int neg = 0; + if (val < 0) { + val *= -1; + neg = 1; + } + int digits = val == 0 ? 0 : Math_.Log10(val); + digits += 1; // digits = log + 1; EX: Log(1-9) = 0, Log(10-99) = 1 + int ary_len = digits + neg, aryBgn = aryPos, pad = 0; + if (ary_len < pad_len) { // padding specified + pad = pad_len - ary_len; + ary_len = pad_len; + } + if (ary == null) ary = new byte[ary_len]; + long factor = 1; // factor needs to be long to handle 1 billion (for which factor would be 10 billion) + for (int i = 0; i < digits; i++) // calc maxFactor + factor *= 10; + if (neg == 1) ary[0] = Byte_NegSign; + + for (int i = 0; i < pad; i++) // fill ary with pad + ary[i + aryBgn] = Byte_ascii.To_a7_str(0); + aryBgn += pad; // advance aryBgn by pad + for (int i = neg; i < ary_len - pad; i++) { + int denominator = (int)(factor / 10); // cache denominator to check for divide by 0 + int digit = denominator == 0 ? 0 : (int)((val % factor) / denominator); + ary[aryBgn + i] = Byte_ascii.To_a7_str(digit); + factor /= 10; + } + return ary; + } + public static byte[] new_by_int(int v) { + byte b0 = (byte)(v >> 24); + byte b1 = (byte)(v >> 16); + byte b2 = (byte)(v >> 8); + byte b3 = (byte)(v); + if (b0 != 0) return new byte[] {b0, b1, b2, b3}; + else if (b1 != 0) return new byte[] {b1, b2, b3}; + else if (b2 != 0) return new byte[] {b2, b3}; + else return new byte[] {b3}; + } + public static boolean To_bool_or(byte[] raw, boolean or) { + return Bry_.Eq(raw, Bool_.True_bry) ? true : or; + } + public static boolean To_bool_by_int(byte[] ary) { + int rv = To_int_or(ary, 0, ary.length, Int_.Min_value, Bool_.Y, null); + switch (rv) { + case 0: return false; + case 1: return true; + default: throw Err_.new_wo_type("could not parse to boolean int", "val", String_.new_u8(ary)); + } + } + public static byte To_int_as_byte(byte[] ary, int bgn, int end, byte or) {return (byte)To_int_or(ary, bgn, end, or);} + public static int To_int(byte[] ary) {return To_int_or_fail(ary, 0, ary.length);} + public static int To_int_or_fail(byte[] ary, int bgn, int end) { + int rv = To_int_or(ary, bgn, end, Int_.Min_value, Bool_.Y, null); + if (rv == Int_.Min_value) throw Err_.new_wo_type("could not parse to int", "val", String_.new_u8(ary, bgn, end)); + return rv; + } + public static int To_int_or_neg1(byte[] ary) {return To_int_or(ary, 0 , ary.length, -1, Bool_.Y, null);} + public static int To_int_or(byte[] ary, int or) {return To_int_or(ary, 0 , ary.length, or, Bool_.Y, null);} + public static int To_int_or(byte[] ary, int bgn, int end, int or) {return To_int_or(ary, bgn , end , or, Bool_.Y, null);} + public static int To_int_or__strict(byte[] ary, int or) {return To_int_or(ary, 0 , ary.length, or, Bool_.N, null);} + private static int To_int_or(byte[] ary, int bgn, int end, int or, boolean sign_is_valid, byte[] ignore_ary) { + if ( ary == null + || end == bgn // null-len + ) return or; + int rv = 0, multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + break; + case Byte_ascii.Dash: + return i == bgn && sign_is_valid ? rv * -1 : or; + case Byte_ascii.Plus: + return i == bgn && sign_is_valid ? rv : or; + default: + boolean invalid = true; + if (ignore_ary != null) { + int ignore_ary_len = ignore_ary.length; + for (int j = 0; j < ignore_ary_len; j++) { + if (b == ignore_ary[j]) { + invalid = false; + break; + } + } + } + if (invalid) return or; + break; + } + } + return rv; + } + public static int To_int_or__trim_ws(byte[] ary, int bgn, int end, int or) { // NOTE: same as To_int_or, except trims ws at bgn / end; DATE:2014-02-09 + if (end == bgn) return or; // null len + int rv = 0, multiple = 1; + boolean numbers_seen = false, ws_seen = false; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + if (ws_seen) // "number ws number" pattern; invalid ws in middle; see tests + return or; + numbers_seen = true; + break; + case Byte_ascii.Dash: + return i == bgn ? rv * -1 : or; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: + if (numbers_seen) + ws_seen = true; + break; + default: return or; + } + } + return rv; + } + public static int To_int_or__lax(byte[] ary, int bgn, int end, int or) { + if (end == bgn) return or; // null-len + int end_num = end; + for (int i = bgn; i < end; i++) { + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + case Byte_ascii.Dash: + if (i != bgn) { + end_num = i; + i = end; + } + break; + default: + end_num = i; + i = end; + break; + } + } + return To_int_or(ary, bgn, end_num, or); + } + public static long To_long_or(byte[] ary, long or) {return To_long_or(ary, null, 0, ary.length, or);} + public static long To_long_or(byte[] ary, byte[] ignore_ary, int bgn, int end, long or) { + if ( ary == null + || end == bgn // null-len + ) return or; + long rv = 0, multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + break; + case Byte_ascii.Dash: + return i == bgn ? rv * -1 : or; + case Byte_ascii.Plus: + return i == bgn ? rv : or; + default: + boolean invalid = true; + if (ignore_ary != null) { + int ignore_ary_len = ignore_ary.length; + for (int j = 0; j < ignore_ary_len; j++) { + if (b == ignore_ary[j]) { + invalid = false; + break; + } + } + } + if (invalid) return or; + break; + } + } + return rv; + } + public static double To_double(byte[] ary, int bgn, int end) {return Double_.parse(String_.new_u8(ary, bgn, end));} + public static double To_double_or(byte[] bry, double or) {return Double_.parse_or(String_.new_u8(bry, 0, bry.length), or);} + public static double To_double_or(byte[] ary, int bgn, int end, double or) {return Double_.parse_or(String_.new_u8(ary, bgn, end), or);} + public static Decimal_adp To_decimal(byte[] ary, int bgn, int end) {return Decimal_adp_.parse(String_.new_u8(ary, bgn, end));} + public static byte[][] Ary_add(byte[][] lhs, byte[][] rhs) { + int lhs_len = lhs.length, rhs_len = rhs.length; + if (lhs_len == 0) return rhs; + else if (rhs_len == 0) return lhs; + else { + byte[][] rv = new byte[lhs_len + rhs_len][]; + for (int i = 0; i < lhs_len; i++) + rv[i] = lhs[i]; + for (int i = 0; i < rhs_len; i++) + rv[i + lhs_len] = rhs[i]; + return rv; + } + } + public static byte[][] Ary(byte[]... ary) {return ary;} + public static byte[][] Ary(String... ary) { + int ary_len = ary.length; + byte[][] rv = new byte[ary_len][]; + for (int i = 0; i < ary_len; i++) { + String itm = ary[i]; + rv[i] = itm == null ? null : Bry_.new_u8(itm); + } + return rv; + } + public static byte[][] Ary_obj(Object... ary) { + if (ary == null) return Bry_.Ary_empty; + int ary_len = ary.length; + byte[][] rv = new byte[ary_len][]; + for (int i = 0; i < ary_len; i++) { + Object itm = ary[i]; + rv[i] = itm == null ? null : Bry_.new_u8(Object_.Xto_str_strict_or_empty(itm)); + } + return rv; + } + public static boolean Ary_eq(byte[][] lhs, byte[][] rhs) { + int lhs_len = lhs.length; + int rhs_len = rhs.length; + if (lhs_len != rhs_len) return false; + for (int i = 0; i < lhs_len; ++i) + if (!Bry_.Eq(lhs[i], rhs[i])) return false; + return true; + } + public static final byte Dlm_fld = (byte)'|', Dlm_row = (byte)'\n', Dlm_quote = (byte)'"', Dlm_null = 0, Ascii_zero = 48; + public static final String Fmt_csvDte = "yyyyMMdd HHmmss.fff"; + public static DateAdp ReadCsvDte(byte[] ary, Int_obj_ref posRef, byte lkp) {// ASSUME: fmt = yyyyMMdd HHmmss.fff + int y = 0, M = 0, d = 0, H = 0, m = 0, s = 0, f = 0; + int bgn = posRef.Val(); + y += (ary[bgn + 0] - Ascii_zero) * 1000; + y += (ary[bgn + 1] - Ascii_zero) * 100; + y += (ary[bgn + 2] - Ascii_zero) * 10; + y += (ary[bgn + 3] - Ascii_zero); + M += (ary[bgn + 4] - Ascii_zero) * 10; + M += (ary[bgn + 5] - Ascii_zero); + d += (ary[bgn + 6] - Ascii_zero) * 10; + d += (ary[bgn + 7] - Ascii_zero); + H += (ary[bgn + 9] - Ascii_zero) * 10; + H += (ary[bgn + 10] - Ascii_zero); + m += (ary[bgn + 11] - Ascii_zero) * 10; + m += (ary[bgn + 12] - Ascii_zero); + s += (ary[bgn + 13] - Ascii_zero) * 10; + s += (ary[bgn + 14] - Ascii_zero); + f += (ary[bgn + 16] - Ascii_zero) * 100; + f += (ary[bgn + 17] - Ascii_zero) * 10; + f += (ary[bgn + 18] - Ascii_zero); + if (ary[bgn + 19] != lkp) throw Err_.new_wo_type("csv date is invalid", "txt", String_.new_u8__by_len(ary, bgn, 20)); + posRef.Val_add(19 + 1); // +1=lkp.len + return DateAdp_.new_(y, M, d, H, m, s, f); + } + public static String ReadCsvStr(byte[] ary, Int_obj_ref posRef, byte lkp) {return String_.new_u8(ReadCsvBry(ary, posRef, lkp, true));} + public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp) {return ReadCsvBry(ary, posRef, lkp, true);} + public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp, boolean make) { + int bgn = posRef.Val(), aryLen = ary.length; + Bry_bfr bb = null; + if (aryLen > 0 && ary[0] == Dlm_quote) { + int pos = bgn + 1; // +1 to skip quote + if (make) bb = Bry_bfr_.New(); + while (true) { + if (pos == aryLen) throw Err_.new_wo_type("endOfAry reached, but no quote found", "txt", String_.new_u8__by_len(ary, bgn, pos)); + byte b = ary[pos]; + if (b == Dlm_quote) { + if (pos == aryLen - 1) throw Err_.new_wo_type("endOfAry reached, quote found but lkp not", "txt", String_.new_u8__by_len(ary, bgn, pos)); + byte next = ary[pos + 1]; + if (next == Dlm_quote) { // byte followed by quote + if (make) bb.Add_byte(b); + pos += 2; + } + else if (next == lkp) { + posRef.Val_(pos + 2); // 1=endQuote;1=lkp; + return make ? bb.To_bry() : Bry_.Empty; + } + else throw Err_.new_wo_type("quote found, but not doubled", "txt", String_.new_u8__by_len(ary, bgn, pos + 1)); + } + else { + if (make) bb.Add_byte(b); + pos++; + } + } + } + else { + for (int i = bgn; i < aryLen; i++) { + if (ary[i] == lkp) { + posRef.Val_(i + 1); // +1 = lkp.Len + return make ? Bry_.Mid(ary, bgn, i) : Bry_.Empty; + } + } + throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "txt", String_.new_u8__by_len(ary, bgn, aryLen)); + } + } + public static int ReadCsvInt(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); + if (pos == Bry_find_.Not_found) throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "bgn", bgn); + int rv = Bry_.To_int_or(ary, posRef.Val(), pos, -1); + posRef.Val_(pos + 1); // +1 = lkp.Len + return rv; + } + public static double ReadCsvDouble(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); + if (pos == Bry_find_.Not_found) throw Err_.new_wo_type("lkp failed", "lkp", (char)lkp, "bgn", bgn); + double rv = Bry_.To_double(ary, posRef.Val(), pos); + posRef.Val_(pos + 1); // +1 = lkp.Len + return rv; + } + public static void ReadCsvNext(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_find_.Find_fwd(ary, lkp, bgn, ary.length); + posRef.Val_(pos + 1); // +1 = lkp.Len + } + public static byte Byte_NegSign = (byte)'-'; + public static byte[] Replace_create(byte[] src, byte find, byte replace) { + byte[] rv = Bry_.Copy(src); + Replace_reuse(rv, find, replace); + return rv; + } + public static void Replace_reuse(byte[] src, byte find, byte replace) { + int src_len = src.length; + for (int i = 0; i < src_len; i++) { + if (src[i] == find) src[i] = replace; + } + } + public static byte[] Replace(byte[] src, byte find, byte replace) {return Replace(src, 0, src.length, find, replace);} + public static byte[] Replace(byte[] src, int bgn, int end, byte find, byte replace) { + int src_len = src.length; + byte[] rv = new byte[src_len]; + for (int i = bgn; i < end; ++i) { + byte b = src[i]; + rv[i] = b == find ? replace : b; + } + for (int i = end; i < src_len; ++i) + rv[i] = src[i]; + return rv; + } + public static byte[] Replace_safe(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) { + if (src == null || find == null || repl == null) return null; + return Replace(bfr, src, find, repl, 0, src.length); + } + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) {return Replace(bfr, src, find, repl, 0, src.length);} + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end) {return Replace(bfr, src, find, repl, src_bgn, src_end, Int_.Max_value);} + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end, int limit) { + int pos = src_bgn; + boolean dirty = false; + int find_len = find.length; + int bfr_bgn = pos; + int replace_count = 0; + while (pos < src_end) { + int find_pos = Bry_find_.Find_fwd(src, find, pos); + if (find_pos == Bry_find_.Not_found) break; + dirty = true; + bfr.Add_mid(src, bfr_bgn, find_pos); + bfr.Add(repl); + pos = find_pos + find_len; + bfr_bgn = pos; + ++replace_count; + if (replace_count == limit) break; + } + if (dirty) + bfr.Add_mid(src, bfr_bgn, src_end); + return dirty ? bfr.To_bry_and_clear() : src; + } + public static byte[] Replace(byte[] src, byte[] find, byte[] replace) {return Replace_between(src, find, null, replace);} + public static byte[] Replace_between(byte[] src, byte[] bgn, byte[] end, byte[] replace) { + Bry_bfr bfr = Bry_bfr_.New(); + boolean replace_all = end == null; + int src_len = src.length, bgn_len = bgn.length, end_len = replace_all ? 0 : end.length; + int pos = 0; + while (true) { + if (pos >= src_len) break; + int bgn_pos = Bry_find_.Find_fwd(src, bgn, pos); + if (bgn_pos == Bry_find_.Not_found) { + bfr.Add_mid(src, pos, src_len); + break; + } + else { + int bgn_rhs = bgn_pos + bgn_len; + int end_pos = replace_all ? bgn_rhs : Bry_find_.Find_fwd(src, end, bgn_rhs); + if (end_pos == Bry_find_.Not_found) { + bfr.Add_mid(src, pos, src_len); + break; + } + else { + bfr.Add_mid(src, pos, bgn_pos); + bfr.Add(replace); + pos = end_pos + end_len; + } + } + } + return bfr.To_bry_and_clear(); + } + public static byte[] Replace_many(byte[] src, byte[] find, byte[] repl) { + Bry_bfr bfr = null; + int src_len = src.length; + int find_len = find.length; + + int pos = 0; + while (true) { + // find find_bgn + int find_bgn = Bry_find_.Find_fwd(src, find, pos, src_len); + + // exit if nothing found + if (find_bgn == Bry_find_.Not_found) + break; + + // lazy-instantiation + if (bfr == null) + bfr = Bry_bfr_.New(); + + // add everything up to find_bgn + bfr.Add_mid(src, pos, find_bgn); + + // add repl + bfr.Add(repl); + + // move pos forward + pos = find_bgn + find_len; + } + + // nothing found; return src + if (bfr == null) + return src; + else { + // add rest + bfr.Add_mid(src, pos, src_len); + return bfr.To_bry_and_clear(); + } + } + public static int Trim_end_pos(byte[] src, int end) { + for (int i = end - 1; i > -1; i--) { + switch (src[i]) { + case Byte_ascii.Tab: case Byte_ascii.Nl: case Byte_ascii.Cr: case Byte_ascii.Space: + break; + default: + return i + 1; + } + } + return 0; + } + public static byte[] Increment_last(byte[] ary) {return Increment_last(ary, ary.length - 1);} + public static byte[] Increment_last(byte[] ary, int end_idx) { + for (int i = end_idx; i > -1; i--) { + byte end_val_old = ary[i]; + byte end_val_new = (byte)(end_val_old + 1); + ary[i] = end_val_new; + if (end_val_new > (end_val_old & 0xff)) break; // PATCH.JAVA:need to convert to unsigned byte + } + return ary; + } + public static byte[] Ucase__all(byte[] src) {return Xcase__all(Bool_.Y, src, 0, -1);} + public static byte[] Lcase__all(byte[] src) {return Xcase__all(Bool_.N, src, 0, -1);} + public static byte[] Lcase__all(byte[] src, int bgn, int end) {return Xcase__all(Bool_.N, src, bgn, end);} + private static byte[] Xcase__all(boolean upper, byte[] src, int bgn, int end) { + if (src == null) return null; + int len = end == -1 ? src.length : end - bgn; if (len == 0) return src; + byte[] rv = new byte[len]; + for (int i = 0; i < len; ++i) { + byte b = src[i + bgn]; + if (upper) { + if (b > 96 && b < 123) b -= 32; + } + else { + if (b > 64 && b < 91) b += 32; + } + rv[i] = b; + } + return rv; + } + public static byte[] Xcase__build__all(Bry_bfr tmp, boolean upper, byte[] src) { + if (src == null) return null; + int src_bgn = 0; + int src_end = src.length; + int lbound = 96, ubound = 123; + if (!upper) { + lbound = 64; ubound = 91; + } + + boolean dirty = false; + for (int i = src_bgn; i < src_end; i++) { + byte b = src[i]; + if (b > lbound && b < ubound) { + if (!dirty) { + dirty = true; + tmp.Add_mid(src, src_bgn, i); + } + if (upper) + b -= 32; + else + b += 32; + } + if (dirty) + tmp.Add_byte(b); + } + return dirty ? tmp.To_bry_and_clear() : src; + } + public static byte[] Ucase__1st(byte[] src) {return Xcase__1st(Bool_.Y, src);} + public static byte[] Lcase__1st(byte[] src) {return Xcase__1st(Bool_.N, src);} + private static byte[] Xcase__1st(boolean upper, byte[] src) { + if (src == null) return null; + int len = src.length; if (len == 0) return src; + byte[] rv = new byte[len]; + byte b = src[0]; + if (upper) { + if (b > 96 && b < 123) b -= 32; + } + else { + if (b > 64 && b < 91) b += 32; + } + rv[0] = b; + for (int i = 1; i < len; ++i) { + rv[i] = src[i]; + } + return rv; + } + public static byte[] Null_if_empty(byte[] v) {return Len_eq_0(v) ? null : v;} + + public static byte[] Escape_ws(byte[] bry) {Bry_bfr bfr = Bry_bfr_.Get(); byte[] rv = Escape_ws(bfr, bry); bfr.Mkr_rls(); return rv;} + public static byte[] Escape_ws(Bry_bfr bfr, byte[] src) { + boolean dirty = false; + int len = src.length; + for (int i = 0; i < len; ++i) { + byte b = src[i]; + byte escape = Byte_.Zero; + switch (b) { + case Byte_ascii.Tab: escape = Byte_ascii.Ltr_t; break; + case Byte_ascii.Nl: escape = Byte_ascii.Ltr_n; break; + case Byte_ascii.Cr: escape = Byte_ascii.Ltr_r; break; + default: if (dirty) bfr.Add_byte(b); break; + } + if (escape != Byte_.Zero) { + if (!dirty) { + dirty = true; + bfr.Add_mid(src, 0, i); + } + bfr.Add_byte_backslash().Add_byte(escape); + } + } + return dirty ? bfr.To_bry_and_clear() : src; + } + public static byte[] Resolve_escape(Bry_bfr bfr, byte escape, byte[] raw, int bgn, int end) { + int pos = bgn; + boolean dirty = false; + while (pos < end) { + byte b = raw[pos]; + if (b == escape) { + if (!dirty) { + dirty = true; + bfr.Add_mid(raw, bgn, pos); + } + ++pos; + if (pos < end) { // check for eos; note that this ignores trailing "\"; EX: "a\" -> "a" + bfr.Add_byte(raw[pos]); + ++pos; + } + } + else { + if (dirty) bfr.Add_byte(b); + ++pos; + } + } + return dirty ? bfr.To_bry_and_clear() : raw; + } + public static void Clear(byte[] bry) { + int len = bry.length; + for (int i = 0; i < len; ++i) + bry[i] = Byte_.Zero; + } + public static byte[] Replace_nl_w_tab(byte[] src, int bgn, int end) { + return Bry_.Replace(Bry_.Mid(src, bgn, end), Byte_ascii.Nl, Byte_ascii.Tab); + } + public static byte[] Escape_html(byte[] src) { + return Escape_html(null, Bool_.N, src, 0, src.length); + } + public static byte[] Escape_html(Bry_bfr bfr, boolean ws, byte[] src, int src_bgn, int src_end) { // uses PHP rules for htmlspecialchars; REF.PHP:http://php.net/manual/en/function.htmlspecialchars.php + boolean dirty = false; + int cur = src_bgn; + int prv = cur; + boolean called_by_bry = bfr == null; + + // loop over chars + while (true) { + // if EOS, exit + if (cur == src_end) { + if (dirty) { + bfr.Add_mid(src, prv, src_end); + } + break; + } + + // check current byte if escaped + byte b = src[cur]; + byte[] escaped = null; + switch (b) { + case Byte_ascii.Amp: escaped = Gfh_entity_.Amp_bry; break; + case Byte_ascii.Quote: escaped = Gfh_entity_.Quote_bry; break; + case Byte_ascii.Apos: escaped = Gfh_entity_.Apos_num_bry; break; + case Byte_ascii.Lt: escaped = Gfh_entity_.Lt_bry; break; + case Byte_ascii.Gt: escaped = Gfh_entity_.Gt_bry; break; + case Byte_ascii.Nl: if (ws) escaped = Gfh_entity_.Nl_bry; break; + case Byte_ascii.Cr: if (ws) escaped = Gfh_entity_.Cr_bry; break; + case Byte_ascii.Tab: if (ws) escaped = Gfh_entity_.Tab_bry; break; + } + + // not escaped; increment and continue + if (escaped == null) { + cur++; + continue; + } + // escaped + else { + dirty = true; + if (bfr == null) bfr = Bry_bfr_.New(); + + if (prv < cur) + bfr.Add_mid(src, prv, cur); + bfr.Add(escaped); + cur++; + prv = cur; + } + } + + if (dirty) { + if (called_by_bry) + return bfr.To_bry_and_clear(); + else + return null; + } + else { + if (called_by_bry) { + if (src_bgn == 0 && src_end == src.length) + return src; + else + return Bry_.Mid(src, src_bgn, src_end); + } + else { + bfr.Add_mid(src, src_bgn, src_end); + return null; + } + } + } +} diff --git a/100_core/src/gplx/Bry__tst.java b/100_core/src/gplx/Bry__tst.java index 6e531636d..0910f9b1d 100644 --- a/100_core/src/gplx/Bry__tst.java +++ b/100_core/src/gplx/Bry__tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,295 +13,309 @@ 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; -import org.junit.*; import gplx.core.primitives.*; import gplx.core.brys.*; import gplx.core.tests.*; -public class Bry__tst { - private final Bry__fxt fxt = new Bry__fxt(); - @Test public void new_ascii_() { - fxt.Test_new_a7("a" , Bry_.New_by_ints(97)); // one - fxt.Test_new_a7("abc" , Bry_.New_by_ints(97, 98, 99)); // many - fxt.Test_new_a7("" , Bry_.Empty); // none - fxt.Test_new_a7("¢€𤭢" , Bry_.New_by_ints(63, 63, 63, 63)); // non-ascii -> ? - } - @Test public void new_u8() { - fxt.Test_new_u8("a" , Bry_.New_by_ints(97)); // one - fxt.Test_new_u8("abc" , Bry_.New_by_ints(97, 98, 99)); // many - fxt.Test_new_u8("¢" , Bry_.New_by_ints(194, 162)); // bry_len=2; cent - fxt.Test_new_u8("€" , Bry_.New_by_ints(226, 130, 172)); // bry_len=3; euro - fxt.Test_new_u8("𤭢" , Bry_.New_by_ints(240, 164, 173, 162)); // bry_len=4; example from en.w:UTF-8 - } - @Test public void Add__bry_plus_byte() { - fxt.Test_add("a" , Byte_ascii.Pipe , "a|"); // basic - fxt.Test_add("" , Byte_ascii.Pipe , "|"); // empty String - } - @Test public void Add__byte_plus_bry() { - fxt.Test_add(Byte_ascii.Pipe , "a" , "|a"); // basic - fxt.Test_add(Byte_ascii.Pipe , "" , "|"); // empty String - } - @Test public void Add_w_dlm() { - fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", "b", "c") , "a|b|c"); // basic - fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a") , "a"); // one item - fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", null, "c") , "a||c"); // null - } - @Test public void Add_w_dlm_bry() { - fxt.Test_add_w_dlm("<>", String_.Ary("a","b","c"), "a<>b<>c"); - } - @Test public void MidByPos() { - tst_MidByPos("abcba", 0, 1, "a"); - tst_MidByPos("abcba", 0, 2, "ab"); - tst_MidByPos("abcba", 1, 4, "bcb"); - } void tst_MidByPos(String src, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Mid(Bry_.new_u8(src), bgn, end)));} - @Test public void Replace_one() { - tst_ReplaceOne("a" , "b" , "c" , "a"); - tst_ReplaceOne("b" , "b" , "c" , "c"); - tst_ReplaceOne("bb" , "b" , "c" , "cb"); - tst_ReplaceOne("abcd" , "bc" , "" , "ad"); - tst_ReplaceOne("abcd" , "b" , "ee" , "aeecd"); - } void tst_ReplaceOne(String src, String find, String repl, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Replace_one(Bry_.new_u8(src), Bry_.new_u8(find), Bry_.new_u8(repl))));} - @Test public void XtoStrBytesByInt() { - tst_XtoStrBytesByInt(0, 0); - tst_XtoStrBytesByInt(9, 9); - tst_XtoStrBytesByInt(10, 1, 0); - tst_XtoStrBytesByInt(321, 3, 2, 1); - tst_XtoStrBytesByInt(-321, Bry_.Byte_NegSign, 3, 2, 1); - tst_XtoStrBytesByInt(Int_.Max_value, 2,1,4,7,4,8,3,6,4,7); - } - void tst_XtoStrBytesByInt(int val, int... expdAryAsInt) { - byte[] expd = new byte[expdAryAsInt.length]; - for (int i = 0; i < expd.length; i++) { - int expdInt = expdAryAsInt[i]; - expd[i] = expdInt == Bry_.Byte_NegSign ? Bry_.Byte_NegSign : Byte_ascii.To_a7_str(expdAryAsInt[i]); - } - Tfds.Eq_ary(expd, Bry_.To_a7_bry(val, Int_.DigitCount(val))); - } - @Test public void Has_at_end() { - tst_HasAtEnd("a|bcd|e", "d" , 2, 5, true); // y_basic - tst_HasAtEnd("a|bcd|e", "bcd" , 2, 5, true); // y_many - tst_HasAtEnd("a|bcd|e", "|bcd" , 2, 5, false); // n_long - tst_HasAtEnd("a|bcd|e", "|bc" , 2, 5, false); // n_pos - tst_HasAtEnd("abc", "bc", true); // y - tst_HasAtEnd("abc", "bd", false); // n - tst_HasAtEnd("a", "ab", false); // exceeds_len - } - void tst_HasAtEnd(String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_end(Bry_.new_u8(src), Bry_.new_u8(find), bgn, end));} - void tst_HasAtEnd(String src, String find, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_end(Bry_.new_u8(src), Bry_.new_u8(find)));} - @Test public void Has_at_bgn() { - tst_HasAtBgn("y_basic" , "a|bcd|e", "b" , 2, 5, true); - tst_HasAtBgn("y_many" , "a|bcd|e", "bcd" , 2, 5, true); - tst_HasAtBgn("n_long" , "a|bcd|e", "bcde" , 2, 5, false); - tst_HasAtBgn("n_pos" , "a|bcd|e", "|bc" , 2, 5, false); - } void tst_HasAtBgn(String tst, String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_bgn(Bry_.new_u8(src), Bry_.new_u8(find), bgn, end), tst);} - @Test public void Match() { - tst_Match("abc", 0, "abc", true); - tst_Match("abc", 2, "c", true); - tst_Match("abc", 0, "cde", false); - tst_Match("abc", 2, "abc", false); // bounds check - tst_Match("abc", 0, "abcd", false); - tst_Match("a" , 0, "", false); - tst_Match("" , 0, "a", false); - tst_Match("" , 0, "", true); - tst_Match("ab", 0, "a", false); // FIX: "ab" should not match "a" b/c .length is different - } void tst_Match(String src, int srcPos, String find, boolean expd) {Tfds.Eq(expd, Bry_.Match(Bry_.new_u8(src), srcPos, Bry_.new_u8(find)));} - @Test public void ReadCsvStr() { - tst_ReadCsvStr("a|" , "a"); - tst_ReadCsvStr("|a|", 1 , "a"); - Int_obj_ref bgn = Int_obj_ref.New_zero(); tst_ReadCsvStr("a|b|c|", bgn, "a"); tst_ReadCsvStr("a|b|c|", bgn, "b"); tst_ReadCsvStr("a|b|c|", bgn, "c"); - tst_ReadCsvStr("|", ""); - tst_ReadCsvStr_err("a"); - - tst_ReadCsvStr("'a'|" , "a"); - tst_ReadCsvStr("'a''b'|" , "a'b"); - tst_ReadCsvStr("'a|b'|" , "a|b"); - tst_ReadCsvStr("''|", ""); - tst_ReadCsvStr_err("''"); - tst_ReadCsvStr_err("'a'b'"); - tst_ReadCsvStr_err("'a"); - tst_ReadCsvStr_err("'a|"); - tst_ReadCsvStr_err("'a'"); - } - @Test public void XtoIntBy4Bytes() { // test len=1, 2, 3, 4 - tst_XtoIntBy4Bytes(32, (byte)32); // space - tst_XtoIntBy4Bytes(8707, (byte)34, (byte)3); // ∃ - tst_XtoIntBy4Bytes(6382179, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c); - tst_XtoIntBy4Bytes(1633837924, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c, Byte_ascii.Ltr_d); - } - @Test public void XtoInt() { - tst_XtoInt("1", 1); - tst_XtoInt("123", 123); - tst_XtoInt("a", Int_.Min_value, Int_.Min_value); - tst_XtoInt("-1", Int_.Min_value, -1); - tst_XtoInt("-123", Int_.Min_value, -123); - tst_XtoInt("123-1", Int_.Min_value, Int_.Min_value); - tst_XtoInt("+123", Int_.Min_value, 123); - tst_XtoInt("", -1); - } - void tst_XtoInt(String val, int expd) {tst_XtoInt(val, -1, expd);} - void tst_XtoInt(String val, int or, int expd) {Tfds.Eq(expd, Bry_.To_int_or(Bry_.new_u8(val), or));} - void tst_XtoIntBy4Bytes(int expd, byte... ary) {Tfds.Eq(expd, Bry_.To_int_by_a7(ary), "XtoInt"); Tfds.Eq_ary(ary, Bry_.new_by_int(expd), "XbyInt");} - void tst_ReadCsvStr(String raw, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.New_zero() , expd);} - void tst_ReadCsvStr(String raw, int bgn, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.New(bgn), expd);} - void tst_ReadCsvStr(String raw, Int_obj_ref bgnRef, String expd) { - int bgn = bgnRef.Val(); - boolean rawHasQuotes = String_.CharAt(raw, bgn) == '\''; - String actl = String_.Replace(Bry_.ReadCsvStr(Bry_.new_u8(String_.Replace(raw, "'", "\"")), bgnRef, (byte)'|'), "\"", "'"); - Tfds.Eq(expd, actl, "rv"); - if (rawHasQuotes) { - int quoteAdj = String_.Count(actl, "'"); - Tfds.Eq(bgn + 1 + String_.Len(actl) + 2 + quoteAdj, bgnRef.Val(), "pos_quote"); // +1=lkp.Len; +2=bgn/end quotes - } - else - Tfds.Eq(bgn + 1 + String_.Len(actl), bgnRef.Val(), "pos"); // +1=lkp.Len - } - void tst_ReadCsvStr_err(String raw) { - try {Bry_.ReadCsvStr(Bry_.new_u8(String_.Replace(raw, "'", "\"")), Int_obj_ref.New_zero(), (byte)'|');} - catch (Exception e) {Err_.Noop(e); return;} - Tfds.Fail_expdError(); - } - @Test public void ReadCsvDte() { - tst_ReadCsvDte("20110801 221435.987"); - } void tst_ReadCsvDte(String raw) {Tfds.Eq_date(DateAdp_.parse_fmt(raw, Bry_.Fmt_csvDte), Bry_.ReadCsvDte(Bry_.new_u8(raw + "|"), Int_obj_ref.New_zero(), (byte)'|'));} - @Test public void ReadCsvInt() { - tst_ReadCsvInt("1234567890"); - } void tst_ReadCsvInt(String raw) {Tfds.Eq(Int_.Parse(raw), Bry_.ReadCsvInt(Bry_.new_u8(raw + "|"), Int_obj_ref.New_zero(), (byte)'|'));} - @Test public void Trim() { - Trim_tst("a b c", 1, 4, "b"); - Trim_tst("a c", 1, 3, ""); - Trim_tst(" ", 0, 2, ""); - } void Trim_tst(String raw, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Trim(Bry_.new_u8(raw), bgn, end)));} - @Test public void Xto_int_lax() { - tst_Xto_int_lax("12a", 12); - tst_Xto_int_lax("1", 1); - tst_Xto_int_lax("123", 123); - tst_Xto_int_lax("a", 0); - tst_Xto_int_lax("-1", -1); - } - private void tst_Xto_int_lax(String val, int expd) {Tfds.Eq(expd, Bry_.To_int_or__lax(Bry_.new_u8(val), 0, String_.Len(val), 0));} - @Test public void To_int_or__trim_ws() { - tst_Xto_int_trim("123 " , 123); - tst_Xto_int_trim(" 123" , 123); - tst_Xto_int_trim(" 123 " , 123); - tst_Xto_int_trim(" 1 3 " , -1); - } - private void tst_Xto_int_trim(String val, int expd) {Tfds.Eq(expd, Bry_.To_int_or__trim_ws(Bry_.new_u8(val), 0, String_.Len(val), -1));} - @Test public void Compare() { - tst_Compare("abcde", 0, 1, "abcde", 0, 1, CompareAble_.Same); - tst_Compare("abcde", 0, 1, "abcde", 1, 2, CompareAble_.Less); - tst_Compare("abcde", 1, 2, "abcde", 0, 1, CompareAble_.More); - tst_Compare("abcde", 0, 1, "abcde", 0, 2, CompareAble_.Less); - tst_Compare("abcde", 0, 2, "abcde", 0, 1, CompareAble_.More); - tst_Compare("abcde", 2, 3, "abçde", 2, 3, CompareAble_.Less); - } void tst_Compare(String lhs, int lhs_bgn, int lhs_end, String rhs, int rhs_bgn, int rhs_end, int expd) {Tfds.Eq(expd, Bry_.Compare(Bry_.new_u8(lhs), lhs_bgn, lhs_end, Bry_.new_u8(rhs), rhs_bgn, rhs_end));} - @Test public void Increment_last() { - tst_IncrementLast(ary_(0), ary_(1)); - tst_IncrementLast(ary_(0, 255), ary_(1, 0)); - tst_IncrementLast(ary_(104, 111, 112, 101), ary_(104, 111, 112, 102)); - } - byte[] ary_(int... ary) { - byte[] rv = new byte[ary.length]; - for (int i = 0; i < ary.length; i++) - rv[i] = Byte_.By_int(ary[i]); - return rv; - } - void tst_IncrementLast(byte[] ary, byte[] expd) {Tfds.Eq_ary(expd, Bry_.Increment_last(Bry_.Copy(ary)));} - @Test public void Replace_between() { - tst_Replace_between("a[0]b" , "[", "]", "0", "a0b"); - tst_Replace_between("a[0]b[1]c" , "[", "]", "0", "a0b0c"); - tst_Replace_between("a[0b" , "[", "]", "0", "a[0b"); - } public void tst_Replace_between(String src, String bgn, String end, String repl, String expd) {Tfds.Eq(expd, String_.new_a7(Bry_.Replace_between(Bry_.new_a7(src), Bry_.new_a7(bgn), Bry_.new_a7(end), Bry_.new_a7(repl))));} - @Test public void Replace() { - Bry_bfr tmp_bfr = Bry_bfr_.New(); - tst_Replace(tmp_bfr, "a0b" , "0", "00", "a00b"); // 1 -> 1 - tst_Replace(tmp_bfr, "a0b0c" , "0", "00", "a00b00c"); // 1 -> 2 - tst_Replace(tmp_bfr, "a00b00c" , "00", "0", "a0b0c"); // 2 -> 1 - tst_Replace(tmp_bfr, "a0b0" , "0", "00", "a00b00"); // 1 -> 2; EOS - tst_Replace(tmp_bfr, "a00b00" , "00", "0", "a0b0"); // 2 -> 1; EOS - tst_Replace(tmp_bfr, "a0b0" , "1", "2", "a0b0"); // no match - tst_Replace(tmp_bfr, "a0b0" , "b1", "b2", "a0b0"); // false match; EOS - } - public void tst_Replace(Bry_bfr tmp_bfr, String src, String bgn, String repl, String expd) { - Tfds.Eq(expd, String_.new_a7(Bry_.Replace(tmp_bfr, Bry_.new_a7(src), Bry_.new_a7(bgn), Bry_.new_a7(repl)))); - } - @Test public void Split_bry() { - Split_bry_tst("a|b|c|" , "|" , String_.Ary("a", "b", "c")); - Split_bry_tst("a|" , "|" , String_.Ary("a")); - } - void Split_bry_tst(String src, String dlm, String[] expd) { - String[] actl = String_.Ary(Bry_split_.Split(Bry_.new_a7(src), Bry_.new_a7(dlm))); - Tfds.Eq_ary_str(expd, actl); - } - @Test public void Split_lines() { - Tst_split_lines("a\nb" , "a", "b"); // basic - Tst_split_lines("a\nb\n" , "a", "b"); // do not create empty trailing lines - Tst_split_lines("a\r\nb" , "a", "b"); // crlf - Tst_split_lines("a\rb" , "a", "b"); // cr only - } - void Tst_split_lines(String src, String... expd) { - Tfds.Eq_ary(expd, New_ary(Bry_split_.Split_lines(Bry_.new_a7(src)))); - } - String[] New_ary(byte[][] lines) { - int len = lines.length; - String[] rv = new String[len]; - for (int i = 0; i < len; i++) - rv[i] = String_.new_u8(lines[i]); - return rv; - } - @Test public void Match_bwd_any() { - Tst_match_bwd_any("abc", 2, 0, "c", true); - Tst_match_bwd_any("abc", 2, 0, "b", false); - Tst_match_bwd_any("abc", 2, 0, "bc", true); - Tst_match_bwd_any("abc", 2, 0, "abc", true); - Tst_match_bwd_any("abc", 2, 0, "zabc", false); - Tst_match_bwd_any("abc", 1, 0, "ab", true); - } - void Tst_match_bwd_any(String src, int src_end, int src_bgn, String find, boolean expd) { - Tfds.Eq(expd, Bry_.Match_bwd_any(Bry_.new_a7(src), src_end, src_bgn, Bry_.new_a7(find))); - } - @Test public void Trim_end() { - fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.one - fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.many - fxt.Test_trim_end("a" , Byte_ascii.Space, "a"); // trim.none - fxt.Test_trim_end("" , Byte_ascii.Space, ""); // empty - } - @Test public void Mid_w_trim() { - fxt.Test_Mid_w_trim("abc", "abc"); // no ws - fxt.Test_Mid_w_trim(" a b c ", "a b c"); // ws at bgn and end - fxt.Test_Mid_w_trim("\r\n\t a\r\n\t b \r\n\t ", "a\r\n\t b"); // space at bgn and end - fxt.Test_Mid_w_trim("", ""); // handle 0 bytes - fxt.Test_Mid_w_trim(" ", ""); // handle all ws - } - @Test public void New_u8_nl_apos() { - fxt.Test__new_u8_nl_apos(String_.Ary("a"), "a"); - fxt.Test__new_u8_nl_apos(String_.Ary("a", "b"), "a\nb"); - fxt.Test__new_u8_nl_apos(String_.Ary("a", "b'c", "d"), "a\nb\"c\nd"); - } - @Test public void Repeat_bry() { - fxt.Test__repeat_bry("abc" , 3, "abcabcabc"); - } - @Test public void Xcase__build__all() { - fxt.Test__xcase__build__all(Bool_.N, "abc", "abc"); - fxt.Test__xcase__build__all(Bool_.N, "aBc", "abc"); - } -} -class Bry__fxt { - private final Bry_bfr tmp = Bry_bfr_.New(); - public void Test_trim_end(String raw, byte trim, String expd) { - byte[] raw_bry = Bry_.new_a7(raw); - Tfds.Eq(expd, String_.new_u8(Bry_.Trim_end(raw_bry, trim, raw_bry.length))); - } - public void Test_new_u8(String raw, byte[] expd) {Tfds.Eq_ary(expd, Bry_.new_u8(raw));} - public void Test_new_a7(String raw, byte[] expd) {Tfds.Eq_ary(expd, Bry_.new_a7(raw));} - public void Test_add(String s, byte b, String expd) {Tfds.Eq_str(expd, String_.new_u8(Bry_.Add(Bry_.new_u8(s), b)));} - public void Test_add(byte b, String s, String expd) {Tfds.Eq_str(expd, String_.new_u8(Bry_.Add(b, Bry_.new_u8(s))));} - public void Test_add_w_dlm(String dlm, String[] itms, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Add_w_dlm(Bry_.new_u8(dlm), Bry_.Ary(itms))));} - public void Test_add_w_dlm(byte dlm, String[] itms, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Add_w_dlm(dlm, Bry_.Ary(itms))));} - public void Test_Mid_w_trim(String src, String expd) {byte[] bry = Bry_.new_u8(src); Tfds.Eq(expd, String_.new_u8(Bry_.Mid_w_trim(bry, 0, bry.length)));} - public void Test__new_u8_nl_apos(String[] ary, String expd) { - Tfds.Eq_str_lines(expd, String_.new_u8(Bry_.New_u8_nl_apos(ary))); - } - public void Test__repeat_bry(String s, int count, String expd) { - Gftest.Eq__str(expd, Bry_.Repeat_bry(Bry_.new_u8(s), count)); - } - public void Test__xcase__build__all(boolean upper, String src, String expd) { - Gftest.Eq__str(expd, Bry_.Xcase__build__all(tmp, upper, Bry_.new_u8(src))); - } -} +package gplx; + +import gplx.core.primitives.Int_obj_ref; +import gplx.core.tests.Gftest; +import org.junit.Test; + +public class Bry__tst { + private final Bry__fxt fxt = new Bry__fxt(); + @Test public void new_ascii_() { + fxt.Test_new_a7("a" , Bry_.New_by_ints(97)); // one + fxt.Test_new_a7("abc" , Bry_.New_by_ints(97, 98, 99)); // many + fxt.Test_new_a7("" , Bry_.Empty); // none + fxt.Test_new_a7("¢€𤭢" , Bry_.New_by_ints(63, 63, 63, 63)); // non-ascii -> ? + } + @Test public void new_u8() { + fxt.Test_new_u8("a" , Bry_.New_by_ints(97)); // one + fxt.Test_new_u8("abc" , Bry_.New_by_ints(97, 98, 99)); // many + fxt.Test_new_u8("¢" , Bry_.New_by_ints(194, 162)); // bry_len=2; cent + fxt.Test_new_u8("€" , Bry_.New_by_ints(226, 130, 172)); // bry_len=3; euro + fxt.Test_new_u8("𤭢" , Bry_.New_by_ints(240, 164, 173, 162)); // bry_len=4; example from en.w:UTF-8 + } + @Test public void Add__bry_plus_byte() { + fxt.Test_add("a" , Byte_ascii.Pipe , "a|"); // basic + fxt.Test_add("" , Byte_ascii.Pipe , "|"); // empty String + } + @Test public void Add__byte_plus_bry() { + fxt.Test_add(Byte_ascii.Pipe , "a" , "|a"); // basic + fxt.Test_add(Byte_ascii.Pipe , "" , "|"); // empty String + } + @Test public void Add_w_dlm() { + fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", "b", "c") , "a|b|c"); // basic + fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a") , "a"); // one item + fxt.Test_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", null, "c") , "a||c"); // null + } + @Test public void Add_w_dlm_bry() { + fxt.Test_add_w_dlm("<>", String_.Ary("a","b","c"), "a<>b<>c"); + } + @Test public void MidByPos() { + tst_MidByPos("abcba", 0, 1, "a"); + tst_MidByPos("abcba", 0, 2, "ab"); + tst_MidByPos("abcba", 1, 4, "bcb"); + } void tst_MidByPos(String src, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Mid(Bry_.new_u8(src), bgn, end)));} + @Test public void Replace_one() { + tst_ReplaceOne("a" , "b" , "c" , "a"); + tst_ReplaceOne("b" , "b" , "c" , "c"); + tst_ReplaceOne("bb" , "b" , "c" , "cb"); + tst_ReplaceOne("abcd" , "bc" , "" , "ad"); + tst_ReplaceOne("abcd" , "b" , "ee" , "aeecd"); + } void tst_ReplaceOne(String src, String find, String repl, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Replace_one(Bry_.new_u8(src), Bry_.new_u8(find), Bry_.new_u8(repl))));} + @Test public void XtoStrBytesByInt() { + tst_XtoStrBytesByInt(0, 0); + tst_XtoStrBytesByInt(9, 9); + tst_XtoStrBytesByInt(10, 1, 0); + tst_XtoStrBytesByInt(321, 3, 2, 1); + tst_XtoStrBytesByInt(-321, Bry_.Byte_NegSign, 3, 2, 1); + tst_XtoStrBytesByInt(Int_.Max_value, 2,1,4,7,4,8,3,6,4,7); + } + void tst_XtoStrBytesByInt(int val, int... expdAryAsInt) { + byte[] expd = new byte[expdAryAsInt.length]; + for (int i = 0; i < expd.length; i++) { + int expdInt = expdAryAsInt[i]; + expd[i] = expdInt == Bry_.Byte_NegSign ? Bry_.Byte_NegSign : Byte_ascii.To_a7_str(expdAryAsInt[i]); + } + Tfds.Eq_ary(expd, Bry_.To_a7_bry(val, Int_.DigitCount(val))); + } + @Test public void Has_at_end() { + tst_HasAtEnd("a|bcd|e", "d" , 2, 5, true); // y_basic + tst_HasAtEnd("a|bcd|e", "bcd" , 2, 5, true); // y_many + tst_HasAtEnd("a|bcd|e", "|bcd" , 2, 5, false); // n_long + tst_HasAtEnd("a|bcd|e", "|bc" , 2, 5, false); // n_pos + tst_HasAtEnd("abc", "bc", true); // y + tst_HasAtEnd("abc", "bd", false); // n + tst_HasAtEnd("a", "ab", false); // exceeds_len + } + void tst_HasAtEnd(String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_end(Bry_.new_u8(src), Bry_.new_u8(find), bgn, end));} + void tst_HasAtEnd(String src, String find, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_end(Bry_.new_u8(src), Bry_.new_u8(find)));} + @Test public void Has_at_bgn() { + tst_HasAtBgn("y_basic" , "a|bcd|e", "b" , 2, 5, true); + tst_HasAtBgn("y_many" , "a|bcd|e", "bcd" , 2, 5, true); + tst_HasAtBgn("n_long" , "a|bcd|e", "bcde" , 2, 5, false); + tst_HasAtBgn("n_pos" , "a|bcd|e", "|bc" , 2, 5, false); + } void tst_HasAtBgn(String tst, String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.Has_at_bgn(Bry_.new_u8(src), Bry_.new_u8(find), bgn, end), tst);} + @Test public void Match() { + tst_Match("abc", 0, "abc", true); + tst_Match("abc", 2, "c", true); + tst_Match("abc", 0, "cde", false); + tst_Match("abc", 2, "abc", false); // bounds check + tst_Match("abc", 0, "abcd", false); + tst_Match("a" , 0, "", false); + tst_Match("" , 0, "a", false); + tst_Match("" , 0, "", true); + tst_Match("ab", 0, "a", false); // FIX: "ab" should not match "a" b/c .length is different + } void tst_Match(String src, int srcPos, String find, boolean expd) {Tfds.Eq(expd, Bry_.Match(Bry_.new_u8(src), srcPos, Bry_.new_u8(find)));} + @Test public void ReadCsvStr() { + tst_ReadCsvStr("a|" , "a"); + tst_ReadCsvStr("|a|", 1 , "a"); + Int_obj_ref bgn = Int_obj_ref.New_zero(); tst_ReadCsvStr("a|b|c|", bgn, "a"); tst_ReadCsvStr("a|b|c|", bgn, "b"); tst_ReadCsvStr("a|b|c|", bgn, "c"); + tst_ReadCsvStr("|", ""); + tst_ReadCsvStr_err("a"); + + tst_ReadCsvStr("'a'|" , "a"); + tst_ReadCsvStr("'a''b'|" , "a'b"); + tst_ReadCsvStr("'a|b'|" , "a|b"); + tst_ReadCsvStr("''|", ""); + tst_ReadCsvStr_err("''"); + tst_ReadCsvStr_err("'a'b'"); + tst_ReadCsvStr_err("'a"); + tst_ReadCsvStr_err("'a|"); + tst_ReadCsvStr_err("'a'"); + } + @Test public void XtoIntBy4Bytes() { // test len=1, 2, 3, 4 + tst_XtoIntBy4Bytes(32, (byte)32); // space + tst_XtoIntBy4Bytes(8707, (byte)34, (byte)3); // ∃ + tst_XtoIntBy4Bytes(6382179, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c); + tst_XtoIntBy4Bytes(1633837924, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c, Byte_ascii.Ltr_d); + } + @Test public void XtoInt() { + tst_XtoInt("1", 1); + tst_XtoInt("123", 123); + tst_XtoInt("a", Int_.Min_value, Int_.Min_value); + tst_XtoInt("-1", Int_.Min_value, -1); + tst_XtoInt("-123", Int_.Min_value, -123); + tst_XtoInt("123-1", Int_.Min_value, Int_.Min_value); + tst_XtoInt("+123", Int_.Min_value, 123); + tst_XtoInt("", -1); + } + void tst_XtoInt(String val, int expd) {tst_XtoInt(val, -1, expd);} + void tst_XtoInt(String val, int or, int expd) {Tfds.Eq(expd, Bry_.To_int_or(Bry_.new_u8(val), or));} + void tst_XtoIntBy4Bytes(int expd, byte... ary) {Tfds.Eq(expd, Bry_.To_int_by_a7(ary), "XtoInt"); Tfds.Eq_ary(ary, Bry_.new_by_int(expd), "XbyInt");} + void tst_ReadCsvStr(String raw, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.New_zero() , expd);} + void tst_ReadCsvStr(String raw, int bgn, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.New(bgn), expd);} + void tst_ReadCsvStr(String raw, Int_obj_ref bgnRef, String expd) { + int bgn = bgnRef.Val(); + boolean rawHasQuotes = String_.CharAt(raw, bgn) == '\''; + String actl = String_.Replace(Bry_.ReadCsvStr(Bry_.new_u8(String_.Replace(raw, "'", "\"")), bgnRef, (byte)'|'), "\"", "'"); + Tfds.Eq(expd, actl, "rv"); + if (rawHasQuotes) { + int quoteAdj = String_.Count(actl, "'"); + Tfds.Eq(bgn + 1 + String_.Len(actl) + 2 + quoteAdj, bgnRef.Val(), "pos_quote"); // +1=lkp.Len; +2=bgn/end quotes + } + else + Tfds.Eq(bgn + 1 + String_.Len(actl), bgnRef.Val(), "pos"); // +1=lkp.Len + } + void tst_ReadCsvStr_err(String raw) { + try {Bry_.ReadCsvStr(Bry_.new_u8(String_.Replace(raw, "'", "\"")), Int_obj_ref.New_zero(), (byte)'|');} + catch (Exception e) {Err_.Noop(e); return;} + Tfds.Fail_expdError(); + } + @Test public void ReadCsvDte() { + tst_ReadCsvDte("20110801 221435.987"); + } void tst_ReadCsvDte(String raw) {Tfds.Eq_date(DateAdp_.parse_fmt(raw, Bry_.Fmt_csvDte), Bry_.ReadCsvDte(Bry_.new_u8(raw + "|"), Int_obj_ref.New_zero(), (byte)'|'));} + @Test public void ReadCsvInt() { + tst_ReadCsvInt("1234567890"); + } void tst_ReadCsvInt(String raw) {Tfds.Eq(Int_.Parse(raw), Bry_.ReadCsvInt(Bry_.new_u8(raw + "|"), Int_obj_ref.New_zero(), (byte)'|'));} + @Test public void Trim() { + Trim_tst("a b c", 1, 4, "b"); + Trim_tst("a c", 1, 3, ""); + Trim_tst(" ", 0, 2, ""); + } void Trim_tst(String raw, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Trim(Bry_.new_u8(raw), bgn, end)));} + @Test public void Xto_int_lax() { + tst_Xto_int_lax("12a", 12); + tst_Xto_int_lax("1", 1); + tst_Xto_int_lax("123", 123); + tst_Xto_int_lax("a", 0); + tst_Xto_int_lax("-1", -1); + } + private void tst_Xto_int_lax(String val, int expd) {Tfds.Eq(expd, Bry_.To_int_or__lax(Bry_.new_u8(val), 0, String_.Len(val), 0));} + @Test public void To_int_or__trim_ws() { + tst_Xto_int_trim("123 " , 123); + tst_Xto_int_trim(" 123" , 123); + tst_Xto_int_trim(" 123 " , 123); + tst_Xto_int_trim(" 1 3 " , -1); + } + private void tst_Xto_int_trim(String val, int expd) {Tfds.Eq(expd, Bry_.To_int_or__trim_ws(Bry_.new_u8(val), 0, String_.Len(val), -1));} + @Test public void Compare() { + tst_Compare("abcde", 0, 1, "abcde", 0, 1, CompareAble_.Same); + tst_Compare("abcde", 0, 1, "abcde", 1, 2, CompareAble_.Less); + tst_Compare("abcde", 1, 2, "abcde", 0, 1, CompareAble_.More); + tst_Compare("abcde", 0, 1, "abcde", 0, 2, CompareAble_.Less); + tst_Compare("abcde", 0, 2, "abcde", 0, 1, CompareAble_.More); + tst_Compare("abcde", 2, 3, "abçde", 2, 3, CompareAble_.Less); + } void tst_Compare(String lhs, int lhs_bgn, int lhs_end, String rhs, int rhs_bgn, int rhs_end, int expd) {Tfds.Eq(expd, Bry_.Compare(Bry_.new_u8(lhs), lhs_bgn, lhs_end, Bry_.new_u8(rhs), rhs_bgn, rhs_end));} + @Test public void Increment_last() { + tst_IncrementLast(ary_(0), ary_(1)); + tst_IncrementLast(ary_(0, 255), ary_(1, 0)); + tst_IncrementLast(ary_(104, 111, 112, 101), ary_(104, 111, 112, 102)); + } + byte[] ary_(int... ary) { + byte[] rv = new byte[ary.length]; + for (int i = 0; i < ary.length; i++) + rv[i] = Byte_.By_int(ary[i]); + return rv; + } + void tst_IncrementLast(byte[] ary, byte[] expd) {Tfds.Eq_ary(expd, Bry_.Increment_last(Bry_.Copy(ary)));} + @Test public void Replace_between() { + tst_Replace_between("a[0]b" , "[", "]", "0", "a0b"); + tst_Replace_between("a[0]b[1]c" , "[", "]", "0", "a0b0c"); + tst_Replace_between("a[0b" , "[", "]", "0", "a[0b"); + } public void tst_Replace_between(String src, String bgn, String end, String repl, String expd) {Tfds.Eq(expd, String_.new_a7(Bry_.Replace_between(Bry_.new_a7(src), Bry_.new_a7(bgn), Bry_.new_a7(end), Bry_.new_a7(repl))));} + @Test public void Replace() { + Bry_bfr tmp_bfr = Bry_bfr_.New(); + tst_Replace(tmp_bfr, "a0b" , "0", "00", "a00b"); // 1 -> 1 + tst_Replace(tmp_bfr, "a0b0c" , "0", "00", "a00b00c"); // 1 -> 2 + tst_Replace(tmp_bfr, "a00b00c" , "00", "0", "a0b0c"); // 2 -> 1 + tst_Replace(tmp_bfr, "a0b0" , "0", "00", "a00b00"); // 1 -> 2; EOS + tst_Replace(tmp_bfr, "a00b00" , "00", "0", "a0b0"); // 2 -> 1; EOS + tst_Replace(tmp_bfr, "a0b0" , "1", "2", "a0b0"); // no match + tst_Replace(tmp_bfr, "a0b0" , "b1", "b2", "a0b0"); // false match; EOS + } + public void tst_Replace(Bry_bfr tmp_bfr, String src, String bgn, String repl, String expd) { + Tfds.Eq(expd, String_.new_a7(Bry_.Replace(tmp_bfr, Bry_.new_a7(src), Bry_.new_a7(bgn), Bry_.new_a7(repl)))); + } + @Test public void Split_bry() { + Split_bry_tst("a|b|c|" , "|" , String_.Ary("a", "b", "c")); + Split_bry_tst("a|" , "|" , String_.Ary("a")); + } + void Split_bry_tst(String src, String dlm, String[] expd) { + String[] actl = String_.Ary(Bry_split_.Split(Bry_.new_a7(src), Bry_.new_a7(dlm))); + Tfds.Eq_ary_str(expd, actl); + } + @Test public void Split_lines() { + Tst_split_lines("a\nb" , "a", "b"); // basic + Tst_split_lines("a\nb\n" , "a", "b"); // do not create empty trailing lines + Tst_split_lines("a\r\nb" , "a", "b"); // crlf + Tst_split_lines("a\rb" , "a", "b"); // cr only + } + void Tst_split_lines(String src, String... expd) { + Tfds.Eq_ary(expd, New_ary(Bry_split_.Split_lines(Bry_.new_a7(src)))); + } + String[] New_ary(byte[][] lines) { + int len = lines.length; + String[] rv = new String[len]; + for (int i = 0; i < len; i++) + rv[i] = String_.new_u8(lines[i]); + return rv; + } + @Test public void Match_bwd_any() { + Tst_match_bwd_any("abc", 2, 0, "c", true); + Tst_match_bwd_any("abc", 2, 0, "b", false); + Tst_match_bwd_any("abc", 2, 0, "bc", true); + Tst_match_bwd_any("abc", 2, 0, "abc", true); + Tst_match_bwd_any("abc", 2, 0, "zabc", false); + Tst_match_bwd_any("abc", 1, 0, "ab", true); + } + void Tst_match_bwd_any(String src, int src_end, int src_bgn, String find, boolean expd) { + Tfds.Eq(expd, Bry_.Match_bwd_any(Bry_.new_a7(src), src_end, src_bgn, Bry_.new_a7(find))); + } + @Test public void Trim_bgn() { + fxt.Test_trim_bgn(" a" , Byte_ascii.Space, "a"); // trim.one + fxt.Test_trim_bgn(" a" , Byte_ascii.Space, "a"); // trim.many + fxt.Test_trim_bgn("a" , Byte_ascii.Space, "a"); // trim.none + fxt.Test_trim_bgn("" , Byte_ascii.Space, ""); // empty + } + @Test public void Trim_end() { + fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.one + fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.many + fxt.Test_trim_end("a" , Byte_ascii.Space, "a"); // trim.none + fxt.Test_trim_end("" , Byte_ascii.Space, ""); // empty + } + @Test public void Mid_w_trim() { + fxt.Test_Mid_w_trim("abc", "abc"); // no ws + fxt.Test_Mid_w_trim(" a b c ", "a b c"); // ws at bgn and end + fxt.Test_Mid_w_trim("\r\n\t a\r\n\t b \r\n\t ", "a\r\n\t b"); // space at bgn and end + fxt.Test_Mid_w_trim("", ""); // handle 0 bytes + fxt.Test_Mid_w_trim(" ", ""); // handle all ws + } + @Test public void New_u8_nl_apos() { + fxt.Test__new_u8_nl_apos(String_.Ary("a"), "a"); + fxt.Test__new_u8_nl_apos(String_.Ary("a", "b"), "a\nb"); + fxt.Test__new_u8_nl_apos(String_.Ary("a", "b'c", "d"), "a\nb\"c\nd"); + } + @Test public void Repeat_bry() { + fxt.Test__repeat_bry("abc" , 3, "abcabcabc"); + } + @Test public void Xcase__build__all() { + fxt.Test__xcase__build__all(Bool_.N, "abc", "abc"); + fxt.Test__xcase__build__all(Bool_.N, "aBc", "abc"); + } +} +class Bry__fxt { + private final Bry_bfr tmp = Bry_bfr_.New(); + public void Test_trim_end(String raw, byte trim, String expd) { + byte[] raw_bry = Bry_.new_a7(raw); + Tfds.Eq(expd, String_.new_u8(Bry_.Trim_end(raw_bry, trim, raw_bry.length))); + } + public void Test_trim_bgn(String raw, byte trim, String expd) { + byte[] raw_bry = Bry_.new_a7(raw); + Tfds.Eq(expd, String_.new_u8(Bry_.Trim_bgn(raw_bry, trim, 0))); + } + public void Test_new_u8(String raw, byte[] expd) {Tfds.Eq_ary(expd, Bry_.new_u8(raw));} + public void Test_new_a7(String raw, byte[] expd) {Tfds.Eq_ary(expd, Bry_.new_a7(raw));} + public void Test_add(String s, byte b, String expd) {Tfds.Eq_str(expd, String_.new_u8(Bry_.Add(Bry_.new_u8(s), b)));} + public void Test_add(byte b, String s, String expd) {Tfds.Eq_str(expd, String_.new_u8(Bry_.Add(b, Bry_.new_u8(s))));} + public void Test_add_w_dlm(String dlm, String[] itms, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Add_w_dlm(Bry_.new_u8(dlm), Bry_.Ary(itms))));} + public void Test_add_w_dlm(byte dlm, String[] itms, String expd) {Tfds.Eq(expd, String_.new_u8(Bry_.Add_w_dlm(dlm, Bry_.Ary(itms))));} + public void Test_Mid_w_trim(String src, String expd) {byte[] bry = Bry_.new_u8(src); Tfds.Eq(expd, String_.new_u8(Bry_.Mid_w_trim(bry, 0, bry.length)));} + public void Test__new_u8_nl_apos(String[] ary, String expd) { + Tfds.Eq_str_lines(expd, String_.new_u8(Bry_.New_u8_nl_apos(ary))); + } + public void Test__repeat_bry(String s, int count, String expd) { + Gftest.Eq__str(expd, Bry_.Repeat_bry(Bry_.new_u8(s), count)); + } + public void Test__xcase__build__all(boolean upper, String src, String expd) { + Gftest.Eq__str(expd, Bry_.Xcase__build__all(tmp, upper, Bry_.new_u8(src))); + } +} diff --git a/400_xowa/src/gplx/core/gfobjs/Gfobj_rdr__json.java b/400_xowa/src/gplx/core/gfobjs/Gfobj_rdr__json.java index fce5b69dc..53253e6e3 100644 --- a/400_xowa/src/gplx/core/gfobjs/Gfobj_rdr__json.java +++ b/400_xowa/src/gplx/core/gfobjs/Gfobj_rdr__json.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,78 +13,94 @@ 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.core.gfobjs; import gplx.*; import gplx.core.*; -import gplx.langs.jsons.*; -public class Gfobj_rdr__json { - private final Json_parser parser = new Json_parser(); - public Gfobj_grp Load(Io_url url) { - byte[] src = Io_mgr.Instance.LoadFilBryOrNull(url); if (src == null) return null; - return this.Parse(src); - } - public Gfobj_grp Parse(byte[] src) { - Json_doc jdoc = parser.Parse(src); - if (jdoc.Root_grp().Tid() == Json_itm_.Tid__nde) { - Gfobj_nde rv_nde = Gfobj_nde.New(); - Parse_nde((Json_nde)jdoc.Root_grp(), rv_nde); - return rv_nde; - } - else { - Gfobj_ary rv_ary = new Gfobj_ary(null); - Parse_ary((Json_ary)jdoc.Root_grp(), rv_ary); - return rv_ary; - } - } - private void Parse_nde(Json_nde jnde, Gfobj_nde gnde) { - int len = jnde.Len(); - for (int i = 0; i < len; ++i) { - Json_kv kv = jnde.Get_at_as_kv(i); - String key_str = kv.Key_as_str(); - Json_itm val = kv.Val(); - byte val_tid = val.Tid(); - switch (val_tid) { - case Json_itm_.Tid__str: gnde.Add_str (key_str, ((Json_itm_str)val).Data_as_str()); break; - case Json_itm_.Tid__bool: gnde.Add_bool (key_str, ((Json_itm_bool)val).Data_as_bool()); break; - case Json_itm_.Tid__int: gnde.Add_int (key_str, ((Json_itm_int)val).Data_as_int()); break; - case Json_itm_.Tid__long: gnde.Add_long (key_str, ((Json_itm_long)val).Data_as_long()); break; - case Json_itm_.Tid__decimal: gnde.Add_double (key_str, ((Json_itm_decimal)val).Data_as_decimal().To_double()); break; - case Json_itm_.Tid__null: gnde.Add_str (key_str, null); break; - case Json_itm_.Tid__ary: - Gfobj_ary sub_ary = new Gfobj_ary(null); - gnde.Add_ary(key_str, sub_ary); - Parse_ary(Json_ary.cast(val), sub_ary); - break; - case Json_itm_.Tid__nde: - Gfobj_nde sub_gnde = Gfobj_nde.New(); - gnde.Add_nde(key_str, sub_gnde); - Parse_nde(Json_nde.cast(val), sub_gnde); - break; - default: throw Err_.new_unhandled_default(val_tid); - } - } - } - private void Parse_ary(Json_ary jry, Gfobj_ary gry) { - int len = jry.Len(); - Object[] ary = new Object[len]; - gry.Ary_(ary); - for (int i = 0; i < len; ++i) { - Json_itm jsub = jry.Get_at(i); - switch (jsub.Tid()) { - case Json_itm_.Tid__ary: { - Gfobj_ary sub_ary = new Gfobj_ary(null); - Parse_ary(Json_ary.cast(jsub), sub_ary); - ary[i] = sub_ary; - break; - } - case Json_itm_.Tid__nde: { - Gfobj_nde sub_ary = Gfobj_nde.New(); - Parse_nde(Json_nde.cast(jsub), sub_ary); - ary[i] = sub_ary; - break; - } - default: - ary[i] = jsub.Data(); - break; - } - } - } -} +package gplx.core.gfobjs; + +import gplx.Err_; +import gplx.Io_mgr; +import gplx.Io_url; +import gplx.langs.jsons.Json_ary; +import gplx.langs.jsons.Json_doc; +import gplx.langs.jsons.Json_itm; +import gplx.langs.jsons.Json_itm_; +import gplx.langs.jsons.Json_itm_bool; +import gplx.langs.jsons.Json_itm_decimal; +import gplx.langs.jsons.Json_itm_int; +import gplx.langs.jsons.Json_itm_long; +import gplx.langs.jsons.Json_itm_str; +import gplx.langs.jsons.Json_kv; +import gplx.langs.jsons.Json_nde; +import gplx.langs.jsons.Json_parser; + +public class Gfobj_rdr__json { + private final Json_parser parser = new Json_parser(); + public Gfobj_grp Load(Io_url url) { + byte[] src = Io_mgr.Instance.LoadFilBryOrNull(url); if (src == null) return null; + return this.Parse(src); + } + public Gfobj_grp Parse(byte[] src) { + Json_doc jdoc = parser.Parse(src); + if (jdoc.Root_grp().Tid() == Json_itm_.Tid__nde) { + Gfobj_nde rv_nde = Gfobj_nde.New(); + Parse_nde((Json_nde)jdoc.Root_grp(), rv_nde); + return rv_nde; + } + else { + Gfobj_ary rv_ary = new Gfobj_ary(null); + Parse_ary((Json_ary)jdoc.Root_grp(), rv_ary); + return rv_ary; + } + } + private void Parse_nde(Json_nde jnde, Gfobj_nde gnde) { + int len = jnde.Len(); + for (int i = 0; i < len; ++i) { + Json_kv kv = jnde.Get_at_as_kv(i); + String key_str = kv.Key_as_str(); + Json_itm val = kv.Val(); + byte val_tid = val.Tid(); + switch (val_tid) { + case Json_itm_.Tid__str: gnde.Add_str (key_str, ((Json_itm_str)val).Data_as_str()); break; + case Json_itm_.Tid__bool: gnde.Add_bool (key_str, ((Json_itm_bool)val).Data_as_bool()); break; + case Json_itm_.Tid__int: gnde.Add_int (key_str, ((Json_itm_int)val).Data_as_int()); break; + case Json_itm_.Tid__long: gnde.Add_long (key_str, ((Json_itm_long)val).Data_as_long()); break; + case Json_itm_.Tid__decimal: gnde.Add_double (key_str, ((Json_itm_decimal)val).Data_as_decimal().To_double()); break; + case Json_itm_.Tid__null: gnde.Add_str (key_str, null); break; + case Json_itm_.Tid__ary: + Gfobj_ary sub_ary = new Gfobj_ary(null); + gnde.Add_ary(key_str, sub_ary); + Parse_ary(Json_ary.cast(val), sub_ary); + break; + case Json_itm_.Tid__nde: + Gfobj_nde sub_gnde = Gfobj_nde.New(); + gnde.Add_nde(key_str, sub_gnde); + Parse_nde(Json_nde.Cast(val), sub_gnde); + break; + default: throw Err_.new_unhandled_default(val_tid); + } + } + } + private void Parse_ary(Json_ary jry, Gfobj_ary gry) { + int len = jry.Len(); + Object[] ary = new Object[len]; + gry.Ary_(ary); + for (int i = 0; i < len; ++i) { + Json_itm jsub = jry.Get_at(i); + switch (jsub.Tid()) { + case Json_itm_.Tid__ary: { + Gfobj_ary sub_ary = new Gfobj_ary(null); + Parse_ary(Json_ary.cast(jsub), sub_ary); + ary[i] = sub_ary; + break; + } + case Json_itm_.Tid__nde: { + Gfobj_nde sub_ary = Gfobj_nde.New(); + Parse_nde(Json_nde.Cast(jsub), sub_ary); + ary[i] = sub_ary; + break; + } + default: + ary[i] = jsub.Data(); + break; + } + } + } +} diff --git a/400_xowa/src/gplx/langs/jsons/JsonDocBldr.java b/400_xowa/src/gplx/langs/jsons/JsonDocBldr.java new file mode 100644 index 000000000..224836fb2 --- /dev/null +++ b/400_xowa/src/gplx/langs/jsons/JsonDocBldr.java @@ -0,0 +1,69 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +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.langs.jsons; + +import gplx.Bry_; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.String_; + +public class JsonDocBldr { + private final List_adp stack = List_adp_.New(); + private final Json_doc doc = new Json_doc(); + private Json_grp root; + private Json_grp cur; + JsonDocBldr() {} + public JsonDocBldr Clear(boolean isRootNode) { + this.root = isRootNode ? Json_nde.NewByDoc(doc, 0) : Json_ary.NewByDoc(doc, 0, 0); + doc.Ctor(Bry_.Empty, root); + this.cur = root; + stack.Clear(); + return this; + } + public JsonDocBldr NdeBgn(String key) { + Json_nde nde = Json_nde.NewByDoc(doc, 1); + if (cur.Tid() == Json_itm_.Tid__nde) { + Json_kv kv = new Json_kv(Json_itm_str.NewByVal(key), nde); + cur.Add(kv); + } + else { + cur.Add(nde); + } + stack.Add(cur); + cur = nde; + return this; + } + public JsonDocBldr NdeEnd() { + this.cur = (Json_grp)List_adp_.Pop_last(stack); + return this; + } + public JsonDocBldr KvBool(String key, boolean val) {return Kv(key, Json_itm_bool.Get(val));} + public JsonDocBldr KvInt(String key, int val) {return Kv(key, Json_itm_int.NewByVal(val));} + public JsonDocBldr KvStr(String key, byte[] val) {return Kv(key, Json_itm_str.NewByVal(String_.new_u8(val)));} + public JsonDocBldr KvStr(String key, String val) {return Kv(key, Json_itm_str.NewByVal(val));} + private JsonDocBldr Kv(String key, Json_itm val) { + Json_kv rv = new Json_kv(Json_itm_str.NewByVal(key), val); + cur.Add(rv); + return this; + } + public Json_doc ToDoc() { + return doc; + } + public Json_nde ToRootNde() { + return doc.Root_nde(); + } + public static JsonDocBldr NewRootNde() {return new JsonDocBldr().Clear(true);} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_ary.java b/400_xowa/src/gplx/langs/jsons/Json_ary.java index f97d596ca..9a140fcae 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_ary.java +++ b/400_xowa/src/gplx/langs/jsons/Json_ary.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,66 +13,75 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_ary extends Json_itm_base implements Json_grp { - public Json_ary(int src_bgn, int src_end) {this.Ctor(src_bgn, src_end);} - @Override public byte Tid() {return Json_itm_.Tid__ary;} - public void Src_end_(int v) {this.src_end = v;} - @Override public Object Data() {return null;} - @Override public byte[] Data_bry() {return null;} - public int Len() {return subs_len;} private int subs_len = 0, subs_max = 0; - public Json_nde Get_at_as_nde(int i) { - Json_itm rv = subs[i]; if (rv.Tid() != Json_itm_.Tid__nde) throw Err_.new_("json", "itm is not nde", "type", rv.Tid(), "i", i); - return (Json_nde)rv; - } - public Json_itm Get_at(int i) {return subs[i];} - public Json_nde Get_as_nde(int i) {return Json_nde.cast(subs[i]);} - public Json_ary Add_many(Json_itm... ary) { - int len = ary.length; - for (int i = 0; i < len; i++) - Add(ary[i]); - return this; - } - public void Add(Json_itm itm) { - int new_len = subs_len + 1; - if (new_len > subs_max) { // ary too small >>> expand - subs_max = new_len * 2; - Json_itm[] new_subs = new Json_itm[subs_max]; - Array_.Copy_to(subs, 0, new_subs, 0, subs_len); - subs = new_subs; - } - subs[subs_len] = itm; - subs_len = new_len; - } - @Override public void Print_as_json(Bry_bfr bfr, int depth) { - if (subs_len == 0) { // empty grp; print on one line (rather than printing across 3) - bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Brack_end); - return; - } - bfr.Add_byte_nl(); - Json_grp_.Print_indent(bfr, depth); - bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Space); - for (int i = 0; i < subs_len; i++) { - if (i != 0) { - Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); - bfr.Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Space); - } - subs[i].Print_as_json(bfr, depth + 1); - } - Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); - bfr.Add_byte(Byte_ascii.Brack_end).Add_byte_nl(); - } - public byte[][] Xto_bry_ary() { - if (subs_len == 0) return Bry_.Ary_empty; - byte[][] rv = new byte[subs_len][]; - for (int i = 0; i < subs_len; ++i) - rv[i] = subs[i].Data_bry(); - return rv; - } - private Json_itm[] subs = Json_itm_.Ary_empty; - public static Json_ary cast_or_null(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__ary ? null : (Json_ary)v;} - public static Json_ary cast(Json_itm v) { - if (v == null || v.Tid() != Json_itm_.Tid__ary) throw Err_.new_("json", "itm is not array"); - return (Json_ary)v; - } -} +package gplx.langs.jsons; + +import gplx.Array_; +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Byte_ascii; +import gplx.Err_; + +public class Json_ary extends Json_itm_base implements Json_grp { + private Json_ary() {} + @Override public byte Tid() {return Json_itm_.Tid__ary;} + public void Src_end_(int v) {} + @Override public Object Data() {return null;} + @Override public byte[] Data_bry() {return null;} + public int Len() {return subs_len;} private int subs_len = 0, subs_max = 0; + public Json_nde Get_at_as_nde(int i) { + Json_itm rv = subs[i]; if (rv.Tid() != Json_itm_.Tid__nde) throw Err_.new_("json", "itm is not nde", "type", rv.Tid(), "i", i); + return (Json_nde)rv; + } + public Json_itm Get_at(int i) {return subs[i];} + public Json_nde Get_as_nde(int i) {return Json_nde.Cast(subs[i]);} + public Json_ary Add_many(Json_itm... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Add(ary[i]); + return this; + } + public void Add(Json_itm itm) { + int new_len = subs_len + 1; + if (new_len > subs_max) { // ary too small >>> expand + subs_max = new_len * 2; + Json_itm[] new_subs = new Json_itm[subs_max]; + Array_.Copy_to(subs, 0, new_subs, 0, subs_len); + subs = new_subs; + } + subs[subs_len] = itm; + subs_len = new_len; + } + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + if (subs_len == 0) { // empty grp; print on one line (rather than printing across 3) + bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Brack_end); + return; + } + bfr.Add_byte_nl(); + Json_grp_.Print_indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Space); + for (int i = 0; i < subs_len; i++) { + if (i != 0) { + Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Space); + } + subs[i].Print_as_json(bfr, depth + 1); + } + Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Brack_end).Add_byte_nl(); + } + public byte[][] Xto_bry_ary() { + if (subs_len == 0) return Bry_.Ary_empty; + byte[][] rv = new byte[subs_len][]; + for (int i = 0; i < subs_len; ++i) + rv[i] = subs[i].Data_bry(); + return rv; + } + private Json_itm[] subs = Json_itm_.Ary_empty; + public static Json_ary cast_or_null(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__ary ? null : (Json_ary)v;} + public static Json_ary cast(Json_itm v) { + if (v == null || v.Tid() != Json_itm_.Tid__ary) throw Err_.new_("json", "itm is not array"); + return (Json_ary)v; + } + public static Json_ary NewByDoc(Json_doc doc, int src_bgn, int src_end) {return new Json_ary();} + public static Json_ary NewByVal() {return new Json_ary();} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_doc.java b/400_xowa/src/gplx/langs/jsons/Json_doc.java index 4a19bea74..eab159031 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_doc.java +++ b/400_xowa/src/gplx/langs/jsons/Json_doc.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,75 +13,82 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -import gplx.core.primitives.*; -public class Json_doc { - private final byte[][] tmp_qry_bry = new byte[1][]; - public void Ctor(byte[] src, Json_grp new_root) { - this.src = src; - this.root_grp = new_root; - switch (root_grp.Tid()) { - case Json_itm_.Tid__nde: this.root_ary = null; this.root_nde = (Json_nde)root_grp; break; - case Json_itm_.Tid__ary: this.root_nde = null; this.root_ary = (Json_ary)root_grp; break; - default: throw Err_.new_unhandled(root_grp.Tid()); - } - } - public byte[] Src() {return src;} private byte[] src; - public Json_grp Root_grp() {return root_grp;} private Json_grp root_grp; - public Json_nde Root_nde() {return root_nde;} private Json_nde root_nde; - public Json_ary Root_ary() {return root_ary;} private Json_ary root_ary; - public Bry_bfr Bfr() {return bfr;} private final Bry_bfr bfr = Bry_bfr_.New(); - public Gfo_number_parser Utl_num_parser() {return utl_num_parser;} private final Gfo_number_parser utl_num_parser = new Gfo_number_parser(); - public byte[] Tmp_u8_bry() {return tmp_u8_bry;} private final byte[] tmp_u8_bry = new byte[6]; // tmp bry[] for decoding sequences like \u0008 - public byte[] Get_val_as_bry_or(byte[] qry_bry, byte[] or) {tmp_qry_bry[0] = qry_bry; return Get_val_as_bry_or(tmp_qry_bry, or);} - public byte[] Get_val_as_bry_or(byte[][] qry_bry, byte[] or) { - Json_itm nde = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); - return nde == null || nde.Tid() != Json_itm_.Tid__str ? or : nde.Data_bry(); - } - public String Get_val_as_str_or(byte[] qry_bry, String or) {tmp_qry_bry[0] = qry_bry; return Get_val_as_str_or(tmp_qry_bry, or);} - public String Get_val_as_str_or(byte[][] qry_bry, String or) { - Json_itm nde = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); - return nde == null || nde.Tid() != Json_itm_.Tid__str ? or : (String)nde.Data(); - } - public int Get_val_as_int_or(byte[] qry_bry, int or) {tmp_qry_bry[0] = qry_bry; return Get_val_as_int_or(tmp_qry_bry, or);} - public int Get_val_as_int_or(byte[][] qry_bry, int or) { - Json_itm nde = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); - return nde == null || nde.Tid() != Json_itm_.Tid__int ? or : Bry_.To_int(nde.Data_bry()); - } - public Json_grp Get_grp(byte[] qry_bry) { - tmp_qry_bry[0] = qry_bry; - Json_itm rv = Find_nde(root_nde, tmp_qry_bry, 0, 0); if (rv == null) return null; - return (Json_grp)rv; - } - public Json_grp Get_grp_many(String... qry_ary) {return Get_grp_many(Bry_.Ary(qry_ary));} - public Json_grp Get_grp_many(byte[]... qry_bry) { - Json_itm rv = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); if (rv == null) return null; - return (Json_grp)rv; - } - public Json_itm Find_nde(byte[] key) { - tmp_qry_bry[0] = key; - return Find_nde(root_nde, tmp_qry_bry, 0, 0); - } - private Json_itm Find_nde(Json_nde owner, byte[][] paths, int paths_last, int paths_idx) { - byte[] path = paths[paths_idx]; - int subs_len = owner.Len(); - for (int i = 0; i < subs_len; i++) { - Json_kv itm = Json_kv.cast(owner.Get_at(i)); if (itm == null) continue; // ignore simple props, arrays, ndes - if (!itm.Key_eq(path)) continue; - if (paths_idx == paths_last) return itm.Val(); - Json_nde sub_nde = Json_nde.cast(itm.Val()); if (sub_nde == null) return null; // match, but has not a nde; exit - return Find_nde(sub_nde, paths, paths_last, paths_idx + 1); - } - return null; - } - public static String Make_str_by_apos(String... ary) {return String_.Replace(String_.Concat_lines_nl_skip_last(ary), "'", "\"");} - public static String[] Make_str_ary_by_apos(String... ary) { - int len = ary.length; - for (int i = 0; i < len; ++i) { - String itm = ary[i]; - if (String_.Has(itm, "'")) - ary[i] = String_.Replace(itm, "'", "\""); - } - return ary; - } -} +package gplx.langs.jsons; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Err_; +import gplx.String_; +import gplx.core.primitives.Gfo_number_parser; + +public class Json_doc { + private final byte[][] tmp_qry_bry = new byte[1][]; + public void Ctor(byte[] src, Json_grp new_root) { + this.src = src; + this.root_grp = new_root; + switch (root_grp.Tid()) { + case Json_itm_.Tid__nde: this.root_ary = null; this.root_nde = (Json_nde)root_grp; break; + case Json_itm_.Tid__ary: this.root_nde = null; this.root_ary = (Json_ary)root_grp; break; + default: throw Err_.new_unhandled(root_grp.Tid()); + } + } + public byte[] Src() {return src;} private byte[] src; + public Json_grp Root_grp() {return root_grp;} private Json_grp root_grp; + public Json_nde Root_nde() {return root_nde;} private Json_nde root_nde; + public Json_ary Root_ary() {return root_ary;} private Json_ary root_ary; + public Bry_bfr Bfr() {return bfr;} private final Bry_bfr bfr = Bry_bfr_.New(); + public Gfo_number_parser Utl_num_parser() {return utl_num_parser;} private final Gfo_number_parser utl_num_parser = new Gfo_number_parser(); + public byte[] Tmp_u8_bry() {return tmp_u8_bry;} private final byte[] tmp_u8_bry = new byte[6]; // tmp bry[] for decoding sequences like \u0008 + public byte[] Get_val_as_bry_or(byte[] qry_bry, byte[] or) {tmp_qry_bry[0] = qry_bry; return Get_val_as_bry_or(tmp_qry_bry, or);} + public byte[] Get_val_as_bry_or(byte[][] qry_bry, byte[] or) { + Json_itm nde = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); + return nde == null || nde.Tid() != Json_itm_.Tid__str ? or : nde.Data_bry(); + } + public String Get_val_as_str_or(byte[] qry_bry, String or) {tmp_qry_bry[0] = qry_bry; return Get_val_as_str_or(tmp_qry_bry, or);} + public String Get_val_as_str_or(byte[][] qry_bry, String or) { + Json_itm nde = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); + return nde == null || nde.Tid() != Json_itm_.Tid__str ? or : (String)nde.Data(); + } + public int Get_val_as_int_or(byte[] qry_bry, int or) {tmp_qry_bry[0] = qry_bry; return Get_val_as_int_or(tmp_qry_bry, or);} + public int Get_val_as_int_or(byte[][] qry_bry, int or) { + Json_itm nde = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); + return nde == null || nde.Tid() != Json_itm_.Tid__int ? or : Bry_.To_int(nde.Data_bry()); + } + public Json_grp Get_grp(byte[] qry_bry) { + tmp_qry_bry[0] = qry_bry; + Json_itm rv = Find_nde(root_nde, tmp_qry_bry, 0, 0); if (rv == null) return null; + return (Json_grp)rv; + } + public Json_grp Get_grp_many(String... qry_ary) {return Get_grp_many(Bry_.Ary(qry_ary));} + public Json_grp Get_grp_many(byte[]... qry_bry) { + Json_itm rv = Find_nde(root_nde, qry_bry, qry_bry.length - 1, 0); if (rv == null) return null; + return (Json_grp)rv; + } + public Json_itm Find_nde(byte[] key) { + tmp_qry_bry[0] = key; + return Find_nde(root_nde, tmp_qry_bry, 0, 0); + } + private Json_itm Find_nde(Json_nde owner, byte[][] paths, int paths_last, int paths_idx) { + byte[] path = paths[paths_idx]; + int subs_len = owner.Len(); + for (int i = 0; i < subs_len; i++) { + Json_kv itm = Json_kv.Cast(owner.Get_at(i)); if (itm == null) continue; // ignore simple props, arrays, ndes + if (!itm.Key_eq(path)) continue; + if (paths_idx == paths_last) return itm.Val(); + Json_nde sub_nde = Json_nde.Cast(itm.Val()); if (sub_nde == null) return null; // match, but has not a nde; exit + return Find_nde(sub_nde, paths, paths_last, paths_idx + 1); + } + return null; + } + public static String Make_str_by_apos(String... ary) {return String_.Replace(String_.Concat_lines_nl_skip_last(ary), "'", "\"");} + public static String[] Make_str_ary_by_apos(String... ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) { + String itm = ary[i]; + if (String_.Has(itm, "'")) + ary[i] = String_.Replace(itm, "'", "\""); + } + return ary; + } +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_doc_bldr.java b/400_xowa/src/gplx/langs/jsons/Json_doc_bldr.java deleted file mode 100644 index e2c34d0ba..000000000 --- a/400_xowa/src/gplx/langs/jsons/Json_doc_bldr.java +++ /dev/null @@ -1,40 +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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_doc_bldr { - public Json_nde Nde(Json_doc jdoc) {return factory.Nde(jdoc, -1);} - public Json_nde Nde(Json_doc jdoc, Json_grp owner) { - Json_nde rv = factory.Nde(jdoc, -1); - owner.Add(rv); - return rv; - } - public Json_itm Str(byte[] v) {return Str(String_.new_u8(v));} - public Json_itm Str(String v) {return Json_itm_tmp.new_str_(v);} - public Json_itm Int(int v) {return Json_itm_tmp.new_int_(v);} - public Json_kv Kv_int(Json_grp owner, String key, int val) {Json_kv rv = factory.Kv(Json_itm_tmp.new_str_(key), Json_itm_tmp.new_int_(val)); owner.Add(rv); return rv;} - public Json_kv Kv_str(Json_grp owner, String key, String val) {Json_kv rv = factory.Kv(Json_itm_tmp.new_str_(key), Json_itm_tmp.new_str_(val)); owner.Add(rv); return rv;} - public Json_ary Kv_ary(Json_grp owner, String key, Json_itm... subs) { - Json_itm key_itm = Json_itm_tmp.new_str_(key); - Json_ary val_ary = factory.Ary(-1, -1); - Json_kv kv = factory.Kv(key_itm, val_ary); - owner.Add(kv); - int len = subs.length; - for (int i = 0; i < len; i++) - val_ary.Add(subs[i]); - return val_ary; - } - Json_doc doc = new Json_doc(); Json_factory factory = new Json_factory(); -} diff --git a/400_xowa/src/gplx/langs/jsons/Json_doc_wtr.java b/400_xowa/src/gplx/langs/jsons/Json_doc_wtr.java index 63658aeb9..21b4e18ee 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_doc_wtr.java +++ b/400_xowa/src/gplx/langs/jsons/Json_doc_wtr.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,140 +13,155 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -import gplx.objects.strings.unicodes.*; -import gplx.core.encoders.*; -public class Json_doc_wtr { - private int indent = -2; - private Bry_bfr bfr = Bry_bfr_.Reset(255); - public void Opt_unicode_y_() {opt_unicode = true;} private boolean opt_unicode; - public Json_doc_wtr Indent() {return Indent(indent);} - private Json_doc_wtr Indent(int v) {if (v > 0) bfr.Add_byte_repeat(Byte_ascii.Space, v); return this;} - public Json_doc_wtr Indent_add() {indent += 2; return this;} - public Json_doc_wtr Indent_del() {indent -= 2; return this;} - public Json_doc_wtr Nde_bgn() {Indent_add(); Indent(); bfr.Add_byte(Byte_ascii.Curly_bgn).Add_byte_nl(); return this;} - public Json_doc_wtr Nde_end() { Indent(); bfr.Add_byte(Byte_ascii.Curly_end).Add_byte_nl(); Indent_del(); return this;} - public Json_doc_wtr Ary_bgn() {Indent_add(); Indent(); bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte_nl(); return this;} - public Json_doc_wtr Ary_end() { Indent(); bfr.Add_byte(Byte_ascii.Brack_end).Add_byte_nl(); Indent_del(); return this;} - public Json_doc_wtr New_line() {bfr.Add_byte_nl(); return this;} - public Json_doc_wtr Str(byte[] v) { - if (v == null) - bfr.Add(Object_.Bry__null); - else { - bfr.Add_byte(Byte_ascii.Quote); - if (opt_unicode) { - Ustring ustr = Ustring_.New_codepoints(String_.new_u8(v)); - int ustr_len = ustr.Len_in_data(); - for (int i = 0; i < ustr_len; i++) { - int cp = ustr.Get_data(i); - Write_str_codepoint(bfr, cp); - } - } - else { - bfr.Add_bry_escape(Byte_ascii.Quote, Escaped__quote, v, 0, v.length); - } - bfr.Add_byte(Byte_ascii.Quote); - } - return this; - } - private void Write_str_codepoint(Bry_bfr bfr, int val) { - switch (val) { // REF: https://www.json.org/ - case Byte_ascii.Quote: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Quote); - break; - case Byte_ascii.Backslash: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Backslash); - break; - case Byte_ascii.Backfeed: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_b); - break; - case Byte_ascii.Formfeed: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_f); - break; - case Byte_ascii.Nl: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_n); - break; - case Byte_ascii.Cr: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_r); - break; - case Byte_ascii.Tab: - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_t); - break; - default: - if ( val < Byte_ascii.Space // control characters - || val == 160 // nbsp - || val == 8206 // left to right - || val == 8207 // right to left - ) { - // convert to \u1234 - bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_u).Add_str_a7(Hex_utl_.To_str(val, 4)); - } - else { - bfr.Add_u8_int(val); - } - break; - } - } - public Json_doc_wtr Int(int v) {bfr.Add_int_variable(v); return this;} - public Json_doc_wtr Double(double v) {bfr.Add_double(v); return this;} - public Json_doc_wtr Comma() {Indent(); bfr.Add_byte(Byte_ascii.Comma).Add_byte_nl(); return this;} - public Json_doc_wtr Kv_ary_empty(boolean comma, byte[] key) { - Key_internal(comma, key); - bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Brack_end); - bfr.Add_byte_nl(); - return this; - } - public Json_doc_wtr Kv(boolean comma, byte[] key, byte[] val) { - Key_internal(comma, key); - Str(val); - bfr.Add_byte_nl(); - return this; - } - public Json_doc_wtr Kv_double(boolean comma, byte[] key, double v) { - Key_internal(comma, key); - Double(v); - bfr.Add_byte_nl(); - return this; - } - public Json_doc_wtr Kv(boolean comma, byte[] key, int v) { - Key_internal(comma, key); - Int(v); - bfr.Add_byte_nl(); - return this; - } - public Json_doc_wtr Key(boolean comma, String key) {return Key(comma, Bry_.new_u8(key));} - public Json_doc_wtr Key(boolean comma, byte[] key) { - Key_internal(comma, key); - bfr.Add_byte_nl(); - return this; - } - public Json_doc_wtr Val(boolean comma, int v) { - Val_internal(comma); - Int(v); - New_line(); - return this; - } - public Json_doc_wtr Val(boolean comma, byte[] v) { - Val_internal(comma); - Str(v); - New_line(); - return this; - } - Json_doc_wtr Val_internal(boolean comma) { - Indent(); - bfr.Add_byte(comma ? Byte_ascii.Comma : Byte_ascii.Space); - bfr.Add_byte(Byte_ascii.Space); - return this; - } - Json_doc_wtr Key_internal(boolean comma, byte[] key) { - Indent(); - bfr.Add_byte(comma ? Byte_ascii.Comma : Byte_ascii.Space); - bfr.Add_byte(Byte_ascii.Space); - Str(key); - bfr.Add_byte(Byte_ascii.Colon); - return this; - } - public byte[] Bld() {return bfr.To_bry_and_clear();} - public String Bld_as_str() {return bfr.To_str_and_clear();} - private static final byte[] Escaped__quote = Bry_.new_a7("\\\""); -} +package gplx.langs.jsons; + +import gplx.Bool_; +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Byte_ascii; +import gplx.Double_; +import gplx.Object_; +import gplx.String_; +import gplx.core.encoders.Hex_utl_; +import gplx.objects.strings.unicodes.Ustring; +import gplx.objects.strings.unicodes.Ustring_; + +public class Json_doc_wtr { + private int indent = -2; + private Bry_bfr bfr = Bry_bfr_.Reset(255); + public void Opt_unicode_y_() {opt_unicode = true;} private boolean opt_unicode; + public Json_doc_wtr Indent() {return Indent(indent);} + private Json_doc_wtr Indent(int v) {if (v > 0) bfr.Add_byte_repeat(Byte_ascii.Space, v); return this;} + public Json_doc_wtr Indent_add() {indent += 2; return this;} + public Json_doc_wtr Indent_del() {indent -= 2; return this;} + public Json_doc_wtr Nde_bgn() {Indent_add(); Indent(); bfr.Add_byte(Byte_ascii.Curly_bgn).Add_byte_nl(); return this;} + public Json_doc_wtr Nde_end() { Indent(); bfr.Add_byte(Byte_ascii.Curly_end).Add_byte_nl(); Indent_del(); return this;} + public Json_doc_wtr Ary_bgn() {Indent_add(); Indent(); bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte_nl(); return this;} + public Json_doc_wtr Ary_end() { Indent(); bfr.Add_byte(Byte_ascii.Brack_end).Add_byte_nl(); Indent_del(); return this;} + public Json_doc_wtr New_line() {bfr.Add_byte_nl(); return this;} + public Json_doc_wtr Str(byte[] v) { + if (v == null) + bfr.Add(Object_.Bry__null); + else { + bfr.Add_byte(Byte_ascii.Quote); + if (opt_unicode) { + Ustring ustr = Ustring_.New_codepoints(String_.new_u8(v)); + int ustr_len = ustr.Len_in_data(); + for (int i = 0; i < ustr_len; i++) { + int cp = ustr.Get_data(i); + Write_str_codepoint(bfr, cp); + } + } + else { + bfr.Add_bry_escape(Byte_ascii.Quote, Escaped__quote, v, 0, v.length); + } + bfr.Add_byte(Byte_ascii.Quote); + } + return this; + } + private void Write_str_codepoint(Bry_bfr bfr, int val) { + switch (val) { // REF: https://www.json.org/ + case Byte_ascii.Quote: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Quote); + break; + case Byte_ascii.Backslash: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Backslash); + break; + case Byte_ascii.Backfeed: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_b); + break; + case Byte_ascii.Formfeed: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_f); + break; + case Byte_ascii.Nl: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_n); + break; + case Byte_ascii.Cr: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_r); + break; + case Byte_ascii.Tab: + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_t); + break; + default: + if ( val < Byte_ascii.Space // control characters + || val == 160 // nbsp + || val == 8206 // left to right + || val == 8207 // right to left + ) { + // convert to \u1234 + bfr.Add_byte_backslash().Add_byte(Byte_ascii.Ltr_u).Add_str_a7(Hex_utl_.To_str(val, 4)); + } + else { + bfr.Add_u8_int(val); + } + break; + } + } + public Json_doc_wtr Int(int v) {bfr.Add_int_variable(v); return this;} + public Json_doc_wtr Double(double v) {bfr.Add_double(v); return this;} + public Json_doc_wtr Comma() {Indent(); bfr.Add_byte(Byte_ascii.Comma).Add_byte_nl(); return this;} + public Json_doc_wtr Kv_ary_empty(boolean comma, byte[] key) { + Key_internal(comma, key); + bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Brack_end); + bfr.Add_byte_nl(); + return this; + } + public Json_doc_wtr Kv(boolean comma, String key, byte[] val) {return Kv(comma, Bry_.new_u8(key), val);} + public Json_doc_wtr Kv(boolean comma, byte[] key, byte[] val) { + Key_internal(comma, key); + Str(val); + bfr.Add_byte_nl(); + return this; + } + public Json_doc_wtr Kv_double(boolean comma, byte[] key, double v) { + Key_internal(comma, key); + Double(v); + bfr.Add_byte_nl(); + return this; + } + public Json_doc_wtr Kv(boolean comma, String key, int v) {return Kv(comma, Bry_.new_u8(key), v);} + public Json_doc_wtr Kv(boolean comma, byte[] key, int v) { + Key_internal(comma, key); + Int(v); + bfr.Add_byte_nl(); + return this; + } + public Json_doc_wtr Kv(boolean comma, String key, double v) {return Kv(comma, Bry_.new_u8(key), Bry_.new_u8(Double_.To_str(v)));} + public Json_doc_wtr Kv(boolean comma, String key, boolean v) {return Kv(comma, Bry_.new_u8(key), v ? Bool_.Y_bry : Bool_.N_bry);} + public Json_doc_wtr Key(boolean comma, String key) {return Key(comma, Bry_.new_u8(key));} + public Json_doc_wtr Key(boolean comma, byte[] key) { + Key_internal(comma, key); + bfr.Add_byte_nl(); + return this; + } + public Json_doc_wtr Val(boolean comma, int v) { + Val_internal(comma); + Int(v); + New_line(); + return this; + } + public Json_doc_wtr Val(boolean comma, byte[] v) { + Val_internal(comma); + Str(v); + New_line(); + return this; + } + Json_doc_wtr Val_internal(boolean comma) { + Indent(); + bfr.Add_byte(comma ? Byte_ascii.Comma : Byte_ascii.Space); + bfr.Add_byte(Byte_ascii.Space); + return this; + } + Json_doc_wtr Key_internal(boolean comma, byte[] key) { + Indent(); + bfr.Add_byte(comma ? Byte_ascii.Comma : Byte_ascii.Space); + bfr.Add_byte(Byte_ascii.Space); + Str(key); + bfr.Add_byte(Byte_ascii.Colon); + return this; + } + public byte[] Bld() {return bfr.To_bry_and_clear();} + public String Bld_as_str() {return bfr.To_str_and_clear();} + private static final byte[] Escaped__quote = Bry_.new_a7("\\\""); +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_factory.java b/400_xowa/src/gplx/langs/jsons/Json_factory.java deleted file mode 100644 index cfcb5c0ee..000000000 --- a/400_xowa/src/gplx/langs/jsons/Json_factory.java +++ /dev/null @@ -1,28 +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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_factory { - public Json_itm Null() {return Json_itm_null.Null;} - public Json_itm Bool_n() {return Json_itm_bool.Bool_n;} - public Json_itm Bool_y() {return Json_itm_bool.Bool_y;} - public Json_itm_int Int(Json_doc doc, int bgn, int end) {return new Json_itm_int(doc, bgn, end);} - public Json_itm_long Long(Json_doc doc, int bgn, int end) {return new Json_itm_long(doc, bgn, end);} - public Json_itm Decimal(Json_doc doc, int bgn, int end) {return new Json_itm_decimal(doc, bgn, end);} - public Json_itm Str(Json_doc doc, int bgn, int end, boolean exact) {return new Json_itm_str(doc, bgn, end, exact);} - public Json_kv Kv(Json_itm key, Json_itm val) {return new Json_kv(key, val);} - public Json_ary Ary(int bgn, int end) {return new Json_ary(bgn, end);} - public Json_nde Nde(Json_doc doc, int bgn) {return new Json_nde(doc, bgn);} -} diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm.java b/400_xowa/src/gplx/langs/jsons/Json_itm.java index 396908467..dadbcf599 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,21 +13,23 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public interface Json_itm { - byte Tid(); - int Src_bgn(); - int Src_end(); - Object Data(); - byte[] Data_bry(); - void Print_as_json(Bry_bfr bfr, int depth); - boolean Data_eq(byte[] comp); -} -class Json_itm_null extends Json_itm_base { - Json_itm_null() {this.Ctor(-1, -1);} - @Override public byte Tid() {return Json_itm_.Tid__null;} - @Override public Object Data() {return null;} - @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add(Object_.Bry__null);} - @Override public byte[] Data_bry() {return Object_.Bry__null;} - public static final Json_itm_null Null = new Json_itm_null(); -} +package gplx.langs.jsons; + +import gplx.Bry_bfr; +import gplx.Object_; + +public interface Json_itm { + byte Tid(); + Object Data(); + byte[] Data_bry(); + boolean Data_eq(byte[] comp); + void Print_as_json(Bry_bfr bfr, int depth); +} +class Json_itm_null extends Json_itm_base { + Json_itm_null() {} + @Override public byte Tid() {return Json_itm_.Tid__null;} + @Override public Object Data() {return null;} + @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add(Object_.Bry__null);} + @Override public byte[] Data_bry() {return Object_.Bry__null;} + public static final Json_itm_null Null = new Json_itm_null(); +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_base.java b/400_xowa/src/gplx/langs/jsons/Json_itm_base.java index 27cc101f3..17032ffc5 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_base.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm_base.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,15 +13,20 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public abstract class Json_itm_base implements Json_itm { - public abstract byte Tid(); - public void Ctor(int src_bgn, int src_end) {this.src_bgn = src_bgn; this.src_end = src_end;} - public int Src_bgn() {return src_bgn;} private int src_bgn; - public int Src_end() {return src_end;} protected int src_end; - public abstract Object Data(); - public abstract byte[] Data_bry(); - public String Print_as_json() {Bry_bfr bfr = Bry_bfr_.New(); Print_as_json(bfr, 0); return bfr.To_str_and_clear();} - public abstract void Print_as_json(Bry_bfr bfr, int depth); - @gplx.Virtual public boolean Data_eq(byte[] comp) {return false;} -} +package gplx.langs.jsons; + +import gplx.Bry_bfr; +import gplx.Bry_bfr_; + +public abstract class Json_itm_base implements Json_itm { + public abstract byte Tid(); + public abstract Object Data(); + public abstract byte[] Data_bry(); + public String Print_as_json() { + Bry_bfr bfr = Bry_bfr_.New(); + Print_as_json(bfr, 0); + return bfr.To_str_and_clear(); + } + public abstract void Print_as_json(Bry_bfr bfr, int depth); + @gplx.Virtual public boolean Data_eq(byte[] comp) {return false;} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_bool.java b/400_xowa/src/gplx/langs/jsons/Json_itm_bool.java index 3ec497baf..b14125495 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_bool.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm_bool.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,14 +13,22 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_itm_bool extends Json_itm_base { - private boolean data; - public Json_itm_bool(boolean data) {this.data = data; this.Ctor(-1, -1);} - @Override public byte Tid() {return Json_itm_.Tid__bool;} - public boolean Data_as_bool() {return data;} - @Override public Object Data() {return data;} - @Override public byte[] Data_bry() {return data ? Json_itm_.Bry__true : Json_itm_.Bry__false;} - @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add(data ? Json_itm_.Bry__true: Json_itm_.Bry__false);} - public static final Json_itm_bool Bool_n = new Json_itm_bool(false), Bool_y = new Json_itm_bool(true); -} +package gplx.langs.jsons; + +import gplx.Bry_bfr; + +public class Json_itm_bool extends Json_itm_base { + private boolean data; + private Json_itm_bool(boolean data) { + this.data = data; + } + + @Override public byte Tid() {return Json_itm_.Tid__bool;} + public boolean Data_as_bool() {return data;} + @Override public Object Data() {return data;} + @Override public byte[] Data_bry() {return data ? Json_itm_.Bry__true : Json_itm_.Bry__false;} + @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add(data ? Json_itm_.Bry__true: Json_itm_.Bry__false);} + + public static final Json_itm_bool Bool_n = new Json_itm_bool(false), Bool_y = new Json_itm_bool(true); + public static final Json_itm_bool Get(boolean v) {return v ? Bool_y : Bool_n;} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_decimal.java b/400_xowa/src/gplx/langs/jsons/Json_itm_decimal.java index 8b6921c4a..568de75b6 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_decimal.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm_decimal.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,23 +13,54 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_itm_decimal extends Json_itm_base { - private final Json_doc doc; private Decimal_adp data; private byte[] data_bry; - public Json_itm_decimal(Json_doc doc, int src_bgn, int src_end) {this.Ctor(src_bgn, src_end); this.doc = doc;} - @Override public byte Tid() {return Json_itm_.Tid__decimal;} - @Override public Object Data() {return this.Data_as_decimal();} - @Override public byte[] Data_bry() { - if (data_bry == null) data_bry = Bry_.Mid(doc.Src(), this.Src_bgn(), this.Src_end()); - return data_bry; - } - public Decimal_adp Data_as_decimal() { - if (data == null) { - String s = String_.new_a7(this.Data_bry()); - s = String_.Replace(s, "e", "E"); // exponent can be either "e" or "E" in JSON, but Java decimal parse only takes "E"; ISSUE#:565; DATE:2020-03-25 - data = Decimal_adp_.parse(s); - } - return data; - } - @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add_mid(doc.Src(), this.Src_bgn(), this.Src_end());} -} +package gplx.langs.jsons; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Decimal_adp; +import gplx.Decimal_adp_; +import gplx.Double_; +import gplx.String_; + +public class Json_itm_decimal extends Json_itm_base { + private final Json_doc doc; + private final int src_bgn, src_end; + private Decimal_adp data; + private byte[] data_bry; + + private Json_itm_decimal(Json_doc doc, int src_bgn, int src_end, Decimal_adp data) { + this.doc = doc; + this.src_bgn = src_bgn; + this.src_end = src_end; + this.data = data; + } + @Override public byte Tid() {return Json_itm_.Tid__decimal;} + @Override public Object Data() {return this.Data_as_decimal();} + @Override public byte[] Data_bry() { + if (data_bry == null) { + data_bry = data == null + ? Bry_.Mid(doc.Src(), src_bgn, src_end) + : Bry_.new_u8(Double_.To_str_loose(data.To_double())); + } + return data_bry; + } + public Decimal_adp Data_as_decimal() { + if (data == null) { + String s = String_.new_a7(this.Data_bry()); + s = String_.Replace(s, "e", "E"); // exponent can be either "e" or "E" in JSON, but Java decimal parse only takes "E"; ISSUE#:565; DATE:2020-03-25 + data = Decimal_adp_.parse(s); + } + return data; + } + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + if (doc == null) { + bfr.Add_str_a7(Double_.To_str_loose(data.To_double())); + } + else { + bfr.Add_mid(doc.Src(), src_bgn, src_end); + } + } + + public static Json_itm_decimal NewByDoc(Json_doc doc, int src_bgn, int src_end) {return new Json_itm_decimal(doc, src_bgn, src_end, null);} + public static Json_itm_decimal NewByVal(Decimal_adp val) {return new Json_itm_decimal(null, -1, -1, val);} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_int.java b/400_xowa/src/gplx/langs/jsons/Json_itm_int.java index e87531a25..99893c8ff 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_int.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm_int.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,21 +13,52 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_itm_int extends Json_itm_base { - private final Json_doc doc; - private byte[] data_bry; private int data; private boolean data_is_null = true; - public Json_itm_int(Json_doc doc, int src_bgn, int src_end) {this.Ctor(src_bgn, src_end); this.doc = doc;} - @Override public byte Tid() {return Json_itm_.Tid__int;} - public int Data_as_int() { - if (data_is_null) { - data = doc.Utl_num_parser().Parse(doc.Src(), Src_bgn(), Src_end()).Rv_as_int(); - data_is_null = false; - } - return data; - } - @Override public Object Data() {return Data_as_int();} - @Override public byte[] Data_bry() {if (data_bry == null) data_bry = Bry_.Mid(doc.Src(), this.Src_bgn(), this.Src_end()); return data_bry;} - @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add_mid(doc.Src(), this.Src_bgn(), this.Src_end());} - public static Json_itm_int cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__int ? null : (Json_itm_int)v;} -} +package gplx.langs.jsons; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Int_; + +public class Json_itm_int extends Json_itm_base { + private final Json_doc doc; + private final int src_bgn, src_end; + private byte[] data_bry; + private int data; + private boolean data_needs_making; + + private Json_itm_int(Json_doc doc, int src_bgn, int src_end, boolean data_needs_making, int data) { + this.doc = doc; + this.src_bgn = src_bgn; + this.src_end = src_end; + this.data_needs_making = data_needs_making; + if (!data_needs_making) + this.data = data; + } + @Override public byte Tid() {return Json_itm_.Tid__int;} + @Override public Object Data() {return Data_as_int();} + @Override public byte[] Data_bry() { + if (data_bry == null) { + data_bry = doc == null + ? Int_.To_bry(data) + : Bry_.Mid(doc.Src(), src_bgn, src_end); + } + return data_bry; + } + public int Data_as_int() { + if (data_needs_making) { + data = doc.Utl_num_parser().Parse(doc.Src(), src_bgn, src_end).Rv_as_int(); + data_needs_making = false; + } + return data; + } + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + if (doc == null) + bfr.Add_int_variable(data); + else + bfr.Add_mid(doc.Src(), src_bgn, src_end); + } + + public static Json_itm_int NewByDoc(Json_doc doc, int src_bgn, int src_end) {return new Json_itm_int(doc, src_bgn, src_end, true, -1);} + public static Json_itm_int NewByVal(int val) {return new Json_itm_int(null, -1, -1, false, val);} + public static Json_itm_int Cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__int ? null : (Json_itm_int)v;} +} \ No newline at end of file diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_long.java b/400_xowa/src/gplx/langs/jsons/Json_itm_long.java index 0e39a267d..dd7727671 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_long.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm_long.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,21 +13,51 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_itm_long extends Json_itm_base { - private final Json_doc doc; - private byte[] data_bry; private long data; private boolean data_is_null = true; - public Json_itm_long(Json_doc doc, int src_bgn, int src_end) {this.Ctor(src_bgn, src_end); this.doc = doc;} - @Override public byte Tid() {return Json_itm_.Tid__long;} - public long Data_as_long() { - if (data_is_null) { - data = doc.Utl_num_parser().Parse(doc.Src(), Src_bgn(), Src_end()).Rv_as_long(); - data_is_null = false; - } - return data; - } - @Override public Object Data() {return Data_as_long();} - @Override public byte[] Data_bry() {if (data_bry == null) data_bry = Bry_.Mid(doc.Src(), this.Src_bgn(), this.Src_end()); return data_bry;} - @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add_mid(doc.Src(), this.Src_bgn(), this.Src_end());} - public static Json_itm_long cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__long ? null : (Json_itm_long)v;} -} +package gplx.langs.jsons; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Long_; + +public class Json_itm_long extends Json_itm_base { + private final Json_doc doc; + private final int src_bgn, src_end; + private byte[] data_bry; + private long data; + private boolean data_needs_making = true; + private Json_itm_long(Json_doc doc, int src_bgn, int src_end, boolean data_needs_making, long data) { + this.doc = doc; + this.src_bgn = src_bgn; + this.src_end = src_end; + this.data_needs_making = data_needs_making; + if (!data_needs_making) + this.data = data; + } + @Override public byte Tid() {return Json_itm_.Tid__long;} + @Override public Object Data() {return Data_as_long();} + @Override public byte[] Data_bry() { + if (data_bry == null) { + data_bry = doc == null + ? Bry_.new_u8(Long_.To_str(data)) + : Bry_.Mid(doc.Src(), src_bgn, src_end); + } + return data_bry; + } + public long Data_as_long() { + if (data_needs_making) { + data = doc.Utl_num_parser().Parse(doc.Src(), src_bgn, src_end).Rv_as_long(); + data_needs_making = false; + } + return data; + } + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + if (doc == null) + bfr.Add_long_variable(data); + else + bfr.Add_mid(doc.Src(), src_bgn, src_end); + } + + public static Json_itm_long NewByDoc(Json_doc doc, int src_bgn, int src_end) {return new Json_itm_long(doc, src_bgn, src_end, true, -1);} + public static Json_itm_long NewByVal(long val) {return new Json_itm_long(null, -1, -1, false, val);} + public static Json_itm_long Cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__long ? null : (Json_itm_long)v;} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_str.java b/400_xowa/src/gplx/langs/jsons/Json_itm_str.java index a9a4de5a4..a6874f2c4 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_str.java +++ b/400_xowa/src/gplx/langs/jsons/Json_itm_str.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,83 +13,153 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -import gplx.core.intls.*; -public class Json_itm_str extends Json_itm_base { - private final boolean exact; private final Json_doc doc; - private String data_str; private byte[] data_bry = null; - public Json_itm_str(Json_doc doc, int src_bgn, int src_end, boolean exact) {this.Ctor(src_bgn + 1, src_end - 1); this.doc = doc; this.exact = exact;} - @Override public byte Tid() {return Json_itm_.Tid__str;} - @Override public void Print_as_json(Bry_bfr bfr, int depth) { - bfr.Add_byte(Byte_ascii.Quote); - gplx.langs.htmls.Gfh_utl.Escape_html_to_bfr(bfr, doc.Src(), this.Src_bgn(), this.Src_end(), true, true, true, true, false); // false to apos for backwards compatibility - bfr.Add_byte(Byte_ascii.Quote); - } - @Override public Object Data() {return this.Data_as_str();} - public String Data_as_str() { - if (data_str == null) { - if (data_bry == null) - data_bry = Data_make_bry(); - data_str = String_.new_u8(data_bry); - } - return data_str; - } - @Override public byte[] Data_bry() {if (data_bry == null) data_bry = Data_make_bry(); return data_bry;} - @Override public boolean Data_eq(byte[] comp) { - if (exact) return Bry_.Eq(doc.Src(), this.Src_bgn(), this.Src_end(), comp); - if (data_bry == null) data_bry = Data_make_bry(); - return Bry_.Match(data_bry, comp); - } - private byte[] Data_make_bry() { - byte[] src = doc.Src(); int bgn = this.Src_bgn(), end = this.Src_end(); - if (exact) return Bry_.Mid(src, bgn, end); - Bry_bfr bfr = doc.Bfr(); - byte[] utf8_bry = doc.Tmp_u8_bry(); - for (int i = bgn; i < end; i++) { - byte b = src[i]; - switch (b) { - case Byte_ascii.Backslash: - b = src[++i]; - switch (b) { // NOTE: must properly unescape chars; EX:wd.q:2; DATE:2014-04-23 - case Byte_ascii.Ltr_t: bfr.Add_byte(Byte_ascii.Tab); break; - case Byte_ascii.Ltr_n: bfr.Add_byte(Byte_ascii.Nl); break; - case Byte_ascii.Ltr_r: bfr.Add_byte(Byte_ascii.Cr); break; - case Byte_ascii.Ltr_b: bfr.Add_byte(Byte_ascii.Backfeed); break; - case Byte_ascii.Ltr_f: bfr.Add_byte(Byte_ascii.Formfeed); break; - case Byte_ascii.Ltr_u: - i += 1; // +1 to skip "u" - int utf8_val = gplx.core.encoders.Hex_utl_.Parse_or(src, i, i + 4, -1); - // check for UTF surrogate-pairs; ISSUE#:487; DATE:2019-06-02 - // hi: 0xD800-0xDBFF; 55,296-56,319 - if (utf8_val >= Utf16_.Surrogate_hi_bgn && utf8_val <= Utf16_.Surrogate_hi_end) { - int lo_bgn = i + 4; // +4 to skip 4 hex-dec chars - if (lo_bgn + 6 <= end // +6 to handle encoded String; EX: '\u0022' - && src[lo_bgn] == Byte_ascii.Backslash - && src[lo_bgn + 1] == Byte_ascii.Ltr_u) { - lo_bgn = lo_bgn + 2; // +2 to skip '\' and 'u' - int lo = gplx.core.encoders.Hex_utl_.Parse_or(src, lo_bgn, lo_bgn + 4, -1); - // lo: 0xDC00-0xDFFF; 56,320-57,343 - if (lo >= Utf16_.Surrogate_lo_bgn && lo <= Utf16_.Surrogate_lo_end) { - utf8_val = Utf16_.Surrogate_merge(utf8_val, lo); - i += 6; // +6 to skip entire lo-String; EX: '\u0022' - } - } - } - int len = gplx.core.intls.Utf16_.Encode_int(utf8_val, utf8_bry, 0); - bfr.Add_mid(utf8_bry, 0, len); - i += 3; // +3 b/c for-loop will do another +1 to bring total to 4; EX: '0022' - break; - case Byte_ascii.Backslash: - case Byte_ascii.Slash: - default: - bfr.Add_byte(b); break; // \? " \ / b f n r t - } - break; - default: - bfr.Add_byte(b); - break; - } - } - return bfr.To_bry_and_clear(); - } -} +package gplx.langs.jsons; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Byte_ascii; +import gplx.String_; +import gplx.core.intls.Utf16_; +import gplx.langs.htmls.Gfh_utl; + +public class Json_itm_str extends Json_itm_base { + private final Json_doc doc; + private final int src_bgn, src_end; + private String data_str; + private byte[] data_bry; + private boolean data_needs_making; + private boolean escaped; + + private Json_itm_str(Json_doc doc, int src_bgn, int src_end, String data_str, boolean escaped) { + this.doc = doc; + this.src_bgn = src_bgn; + this.src_end = src_end; + this.data_str = data_str; + this.data_bry = null; + this.data_needs_making = true; + this.escaped = escaped; + } + @Override public byte Tid() {return Json_itm_.Tid__str;} + @Override public Object Data() {return this.Data_as_str();} + public void Overwrite_bry(byte[] v) { + this.data_bry = v; //needed by MapLink/MapFrame + this.data_needs_making = false; + } + @Override public byte[] Data_bry() { + if (data_bry == null) { + data_bry = Data_make_bry(); + } + return data_bry; + } + public String Data_as_str() { + if (data_str == null) { + data_bry = Data_make_bry(); + data_str = String_.new_u8(data_bry); + } + return data_str; + } + @Override public boolean Data_eq(byte[] comp) { + return Bry_.Match(this.Data_bry(), comp); + } + private byte[] Data_make_bry() { + // data already made; return it; + if (!data_needs_making) + return data_bry; + + // mark data as made + this.data_needs_making = false; + + // get src, bgn, end, depending on whether or not itm is from jdoc or standalone + byte[] src; + int bgn; + int end; + if (doc == null) { + src = Bry_.new_u8_safe(this.data_str); + bgn = 0; + end = src == null ? 0 : src.length; + } + else { + src = doc.Src(); + bgn = src_bgn; + end = src_end; + } + + // not escaped -> return the src + if (!escaped) { + this.data_bry = Bry_.Mid(src, bgn, end); + return data_bry; + } + + // escaped; get some temp vars + Bry_bfr bfr; + byte[] utf8_bry; + if (doc == null) { + bfr = Bry_bfr_.New(); + utf8_bry = new byte[6]; + } + else { // PERF:reuse bfr / bry on jdoc itself + bfr = doc.Bfr(); + utf8_bry = doc.Tmp_u8_bry(); + } + + // loop and unescape + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Backslash: + b = src[++i]; + switch (b) { // NOTE: must properly unescape chars; EX:wd.q:2; DATE:2014-04-23 + case Byte_ascii.Ltr_t: bfr.Add_byte(Byte_ascii.Tab); break; + case Byte_ascii.Ltr_n: bfr.Add_byte(Byte_ascii.Nl); break; + case Byte_ascii.Ltr_r: bfr.Add_byte(Byte_ascii.Cr); break; + case Byte_ascii.Ltr_b: bfr.Add_byte(Byte_ascii.Backfeed); break; + case Byte_ascii.Ltr_f: bfr.Add_byte(Byte_ascii.Formfeed); break; + case Byte_ascii.Ltr_u: + i += 1; // +1 to skip "u" + int utf8_val = gplx.core.encoders.Hex_utl_.Parse_or(src, i, i + 4, -1); + // check for UTF surrogate-pairs; ISSUE#:487; DATE:2019-06-02 + // hi: 0xD800-0xDBFF; 55,296-56,319 + if (utf8_val >= Utf16_.Surrogate_hi_bgn && utf8_val <= Utf16_.Surrogate_hi_end) { + int lo_bgn = i + 4; // +4 to skip 4 hex-dec chars + if (lo_bgn + 6 <= end // +6 to handle encoded String; EX: '\u0022' + && src[lo_bgn] == Byte_ascii.Backslash + && src[lo_bgn + 1] == Byte_ascii.Ltr_u) { + lo_bgn = lo_bgn + 2; // +2 to skip '\' and 'u' + int lo = gplx.core.encoders.Hex_utl_.Parse_or(src, lo_bgn, lo_bgn + 4, -1); + // lo: 0xDC00-0xDFFF; 56,320-57,343 + if (lo >= Utf16_.Surrogate_lo_bgn && lo <= Utf16_.Surrogate_lo_end) { + utf8_val = Utf16_.Surrogate_merge(utf8_val, lo); + i += 6; // +6 to skip entire lo-String; EX: '\u0022' + } + } + } + int len = gplx.core.intls.Utf16_.Encode_int(utf8_val, utf8_bry, 0); + bfr.Add_mid(utf8_bry, 0, len); + i += 3; // +3 b/c for-loop will do another +1 to bring total to 4; EX: '0022' + break; + case Byte_ascii.Backslash: + case Byte_ascii.Slash: + default: + bfr.Add_byte(b); break; // \? " \ / b f n r t + } + break; + default: + bfr.Add_byte(b); + break; + } + } + this.data_bry = bfr.To_bry_and_clear(); + return data_bry; + } + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + bfr.Add_byte(Byte_ascii.Quote); + byte[] data_bry = this.Data_bry(); + int data_len = data_bry.length; + Gfh_utl.Escape_html_to_bfr(bfr, data_bry, 0, data_len, true, true, true, true, false); // false to apos for backwards compatibility + bfr.Add_byte(Byte_ascii.Quote); + } + + public static Json_itm_str NewByDoc(Json_doc doc, int src_bgn, int src_end, boolean escaped) {return new Json_itm_str(doc, src_bgn + 1, src_end - 1, null, escaped);} + public static Json_itm_str NewByVal(String val) {return new Json_itm_str(null, -1, -1, val, false);} +} \ No newline at end of file diff --git a/400_xowa/src/gplx/langs/jsons/Json_itm_tmp.java b/400_xowa/src/gplx/langs/jsons/Json_itm_tmp.java deleted file mode 100644 index d0ef93e36..000000000 --- a/400_xowa/src/gplx/langs/jsons/Json_itm_tmp.java +++ /dev/null @@ -1,29 +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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_itm_tmp implements Json_itm { // TEST: - public Json_itm_tmp(byte tid, String data) {this.tid = tid; this.data = data;} - public byte Tid() {return tid;} private byte tid; - public byte[] Data_bry() {return Bry_.new_u8(Object_.Xto_str_strict_or_empty(data));} - public int Src_bgn() {return -1;} - public int Src_end() {return -1;} - public Object Data() {return data;} private String data; - public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add_str_u8(data);} - public boolean Data_eq(byte[] comp) {return false;} - public void Clear() {} - public static Json_itm new_str_(String v) {return new Json_itm_tmp(Json_itm_.Tid__str, "\"" + v + "\"");} - public static Json_itm new_int_(int v) {return new Json_itm_tmp(Json_itm_.Tid__int, Int_.To_str(v));} -} diff --git a/400_xowa/src/gplx/langs/jsons/Json_kv.java b/400_xowa/src/gplx/langs/jsons/Json_kv.java index 729907d35..aa03c0d25 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_kv.java +++ b/400_xowa/src/gplx/langs/jsons/Json_kv.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,25 +13,29 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -public class Json_kv extends Json_itm_base { - public Json_kv(Json_itm key, Json_itm val) {this.key = key; this.val = val;} - @Override public byte Tid() {return Json_itm_.Tid__kv;} - public Json_itm Key() {return key;} private final Json_itm key; - public Json_itm Val() {return val;} private final Json_itm val; - public byte[] Key_as_bry() {return key.Data_bry();} - public String Key_as_str() {return (String)key.Data();} - public byte[] Val_as_bry() {return val.Data_bry();} - public Json_nde Val_as_nde() {return Json_nde.cast(val);} - public Json_ary Val_as_ary() {return Json_ary.cast(val);} - public boolean Key_eq(byte[] comp) {return ((Json_itm_str)key).Data_eq(comp);} - @Override public Object Data() {return null;} - @Override public byte[] Data_bry() {return null;} - @Override public void Print_as_json(Bry_bfr bfr, int depth) { - key.Print_as_json(bfr, depth); - bfr.Add_byte(Byte_ascii.Colon); - val.Print_as_json(bfr, depth); - } - public static final Json_kv[] Ary_empty = new Json_kv[0]; - public static Json_kv cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__kv ? null : (Json_kv)v;} -} +package gplx.langs.jsons; + +import gplx.Bry_bfr; +import gplx.Byte_ascii; + +public class Json_kv extends Json_itm_base { + public Json_kv(Json_itm key, Json_itm val) {this.key = key; this.val = val;} + @Override public byte Tid() {return Json_itm_.Tid__kv;} + public Json_itm Key() {return key;} private final Json_itm key; + public Json_itm Val() {return val;} private final Json_itm val; + public byte[] Key_as_bry() {return key.Data_bry();} + public String Key_as_str() {return (String)key.Data();} + public byte[] Val_as_bry() {return val.Data_bry();} + public Json_nde Val_as_nde() {return Json_nde.Cast(val);} + public Json_ary Val_as_ary() {return Json_ary.cast(val);} + public boolean Key_eq(byte[] comp) {return ((Json_itm_str)key).Data_eq(comp);} + @Override public Object Data() {return null;} + @Override public byte[] Data_bry() {return null;} + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + key.Print_as_json(bfr, depth); + bfr.Add_byte(Byte_ascii.Colon); + val.Print_as_json(bfr, depth); + } + public static final Json_kv[] Ary_empty = new Json_kv[0]; + public static Json_kv Cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__kv ? null : (Json_kv)v;} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_kv_ary_srl_tst.java b/400_xowa/src/gplx/langs/jsons/Json_kv_ary_srl_tst.java index 92278b632..6edcf271e 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_kv_ary_srl_tst.java +++ b/400_xowa/src/gplx/langs/jsons/Json_kv_ary_srl_tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,37 +13,42 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -import org.junit.*; -public class Json_kv_ary_srl_tst { - @Before public void init() {fxt.Clear();} private Json_kv_ary_srl_fxt fxt = new Json_kv_ary_srl_fxt(); - @Test public void Null() {fxt.Test_parse("{'k0':null}" , fxt.ary_(fxt.kv_str_("k0", null)));} - @Test public void Bool_n() {fxt.Test_parse("{'k0':false}" , fxt.ary_(fxt.kv_bool_("k0", false)));} - @Test public void Num() {fxt.Test_parse("{'k0':123}" , fxt.ary_(fxt.kv_int_("k0", 123)));} - @Test public void Num_exp() {fxt.Test_parse("{'k0':1.23e2}" , fxt.ary_(fxt.kv_int_("k0", 123)));} // exponent can be either "e" or "E" in JSON, but Java decimal parse only takes "E"; ISSUE#:565; DATE:2020-03-25 - @Test public void Str() {fxt.Test_parse("{'k0':'v0'}" , fxt.ary_(fxt.kv_str_("k0", "v0")));} - @Test public void Num_dec() {fxt.Test_parse("{'k0':1.23}" , fxt.ary_(fxt.kv_dec_("k0", Decimal_adp_.parse("1.23"))));} - @Test public void Ary_int() {fxt.Test_parse("{'k0':[1,2,3]}" , fxt.ary_(fxt.kv_obj_("k0", fxt.ary_(fxt.kv_int_("1", 1), fxt.kv_int_("2", 2), fxt.kv_int_("3", 3)))));} - @Test public void Ary_empty() {fxt.Test_parse("{'k0':[]}" , fxt.ary_(fxt.kv_obj_("k0", fxt.ary_())));} - @Test public void Subs_int() {fxt.Test_parse("{'k0':{'k00':1,'k01':2}}" , fxt.ary_(fxt.kv_obj_("k0", fxt.ary_(fxt.kv_int_("k00", 1), fxt.kv_int_("k01", 2)))));} - @Test public void Subs_empty() {fxt.Test_parse("{'k0':{}}" , fxt.ary_(fxt.kv_obj_("k0", fxt.ary_())));} -} -class Json_kv_ary_srl_fxt { - public void Clear() { - if (parser == null) { - parser = new Json_parser(); - } - } private Json_parser parser; - public void Test_parse(String raw_str, Keyval[] expd) { - byte[] raw_bry = Json_parser_tst.Replace_apos(Bry_.new_u8(raw_str)); - Json_doc doc = parser.Parse(raw_bry); - Keyval[] actl = Json_kv_ary_srl.Val_by_itm_nde(doc.Root_nde()); - Tfds.Eq_str_lines(Keyval_.Ary_to_str(expd), Keyval_.Ary_to_str(actl)); - } - public Keyval[] ary_(Keyval... ary) {return ary;} - public Keyval kv_obj_(String key, Object val) {return Keyval_.new_(key, val);} - public Keyval kv_str_(String key, String val) {return Keyval_.new_(key, val);} - public Keyval kv_int_(String key, int val) {return Keyval_.new_(key, val);} - public Keyval kv_bool_(String key, boolean val) {return Keyval_.new_(key, Bool_.To_str_lower(val));} - public Keyval kv_dec_(String key, Decimal_adp val) {return Keyval_.new_(key, val.To_str());} -} +package gplx.langs.jsons; + +import gplx.Bool_; +import gplx.Bry_; +import gplx.Decimal_adp; +import gplx.Decimal_adp_; +import gplx.Keyval; +import gplx.Keyval_; +import gplx.Tfds; +import org.junit.Test; + +public class Json_kv_ary_srl_tst { + private final Json_kv_ary_srl_fxt fxt = new Json_kv_ary_srl_fxt(); + @Test public void Null() {fxt.Test_parse("{'k0':null}" , fxt.ary_(fxt.New_kv_str("k0", null)));} + @Test public void Bool_n() {fxt.Test_parse("{'k0':false}" , fxt.ary_(fxt.New_kv_bool("k0", false)));} + @Test public void Num() {fxt.Test_parse("{'k0':123}" , fxt.ary_(fxt.New_kv_int("k0", 123)));} + @Test public void Num_exp() {fxt.Test_parse("{'k0':1.23e2}" , fxt.ary_(fxt.New_kv_int("k0", 123)));} // exponent can be either "e" or "E" in JSON, but Java decimal parse only takes "E"; ISSUE#:565; DATE:2020-03-25 + @Test public void Str() {fxt.Test_parse("{'k0':'v0'}" , fxt.ary_(fxt.New_kv_str("k0", "v0")));} + @Test public void Num_dec() {fxt.Test_parse("{'k0':1.23}" , fxt.ary_(fxt.New_kv_dec("k0", Decimal_adp_.parse("1.23"))));} + @Test public void Ary_int() {fxt.Test_parse("{'k0':[1,2,3]}" , fxt.ary_(fxt.New_kv_obj("k0", fxt.ary_(fxt.New_kv_int("1", 1), fxt.New_kv_int("2", 2), fxt.New_kv_int("3", 3)))));} + @Test public void Ary_empty() {fxt.Test_parse("{'k0':[]}" , fxt.ary_(fxt.New_kv_obj("k0", fxt.ary_())));} + @Test public void Subs_int() {fxt.Test_parse("{'k0':{'k00':1,'k01':2}}" , fxt.ary_(fxt.New_kv_obj("k0", fxt.ary_(fxt.New_kv_int("k00", 1), fxt.New_kv_int("k01", 2)))));} + @Test public void Subs_empty() {fxt.Test_parse("{'k0':{}}" , fxt.ary_(fxt.New_kv_obj("k0", fxt.ary_())));} +} +class Json_kv_ary_srl_fxt { + private final Json_parser parser = new Json_parser(); + public void Test_parse(String raw_str, Keyval[] expd) { + byte[] raw_bry = Bry_.new_u8(Json_doc.Make_str_by_apos(raw_str)); + Json_doc doc = parser.Parse(raw_bry); + Keyval[] actl = Json_kv_ary_srl.Val_by_itm_nde(doc.Root_nde()); + Tfds.Eq_str_lines(Keyval_.Ary_to_str(expd), Keyval_.Ary_to_str(actl)); + } + public Keyval[] ary_(Keyval... ary) {return ary;} + public Keyval New_kv_obj(String key, Object val) {return Keyval_.new_(key, val);} + public Keyval New_kv_str(String key, String val) {return Keyval_.new_(key, val);} + public Keyval New_kv_int(String key, int val) {return Keyval_.new_(key, val);} + public Keyval New_kv_bool(String key, boolean val) {return Keyval_.new_(key, Bool_.To_str_lower(val));} + public Keyval New_kv_dec(String key, Decimal_adp val) {return Keyval_.new_(key, val.To_str());} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_nde.java b/400_xowa/src/gplx/langs/jsons/Json_nde.java index 23a4822f1..d8f7ada6e 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_nde.java +++ b/400_xowa/src/gplx/langs/jsons/Json_nde.java @@ -1,191 +1,217 @@ -/* -XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2020 gnosygnu@gmail.com - -XOWA is licensed under the terms of the General Public License (GPL) Version 3, -or alternatively under the terms of the Apache License Version 2.0. - -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.langs.jsons; - -import gplx.Array_; -import gplx.Bool_; -import gplx.Bry_; -import gplx.Bry_bfr; -import gplx.Byte_ascii; -import gplx.DateAdp; -import gplx.DateAdp_; -import gplx.Err_; -import gplx.Hash_adp_bry; -import gplx.Int_; -import gplx.Long_; -import gplx.String_; - -public class Json_nde extends Json_itm_base implements Json_grp { - private Json_itm[] subs = Json_itm_.Ary_empty; private int subs_len = 0, subs_max = 0; - private Hash_adp_bry subs_hash; - public Json_nde(Json_doc jdoc, int src_bgn) {this.jdoc = jdoc; this.Ctor(src_bgn, -1);} - @Override public byte Tid() {return Json_itm_.Tid__nde;} - public Json_doc Doc() {return jdoc;} private final Json_doc jdoc; - public void Src_end_(int v) {this.src_end = v;} - @Override public Object Data() {return null;} - @Override public byte[] Data_bry() {return null;} - public int Len() {return subs_len;} - public Json_itm Get_at(int i) {return subs[i];} - public Json_itm Get_as_itm_or_null(String key) {return Get_as_itm_or_null(Bry_.new_u8(key));} - public Json_itm Get_as_itm_or_null(byte[] key) {if (subs_hash == null) subs_hash = subs_hash_init(); return (Json_itm)subs_hash.Get_by_bry(key);} - public Json_ary Get_as_ary(int idx) {return Json_ary.cast(Get_at(idx));} - public Json_nde Get_as_nde(String key) {return Json_nde.cast(Get_as_itm_or_null(Bry_.new_u8(key)));} - public Json_nde Get_as_nde(int idx) {return Json_nde.cast(Get_at(idx));} - public Json_ary Get_as_ary(String key) {return Get_as_ary(Bry_.new_u8(key));} - public Json_ary Get_as_ary(byte[] key) { - Json_itm rv = Get_as_itm_or_null(key); if (rv == null) throw Err_.new_("json", "key missing", "key", key); - return Json_ary.cast(rv); - } - public Json_ary Get_as_ary_or_null(String key) {return Get_as_ary_or_null(Bry_.new_u8(key));} - public Json_ary Get_as_ary_or_null(byte[] key) { - Json_itm rv = Get_as_itm_or_null(key); - return rv == null - ? null - : Json_ary.cast(rv); - } - public byte[] Get_as_bry(String key) { - byte[] rv = Get_as_bry_or(Bry_.new_u8(key), null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); - return rv; - } - public byte[] Get_as_bry_or(byte[] key, byte[] or) { - Json_itm rv = Get_as_itm_or_null(key); - return rv == null ? or : rv.Data_bry(); - } - public String Get_as_str(String key) { - String rv = Get_as_str_or(key, null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); - return rv; - } - public String Get_as_str_or(String key, String or) {return Get_as_str_or(Bry_.new_u8(key), or);} - public String Get_as_str_or(byte[] key, String or) { - byte[] rv = Get_as_bry_or(key, null); - return rv == null ? or : String_.new_u8(rv); - } - public int Get_as_int(String key) { - int rv = Get_as_int_or(key, Int_.Min_value); if (rv == Int_.Min_value) throw Err_.new_("json", "key missing", "key", key); - return rv; - } - public int Get_as_int_or(String key, int or) {return Get_as_int_or(Bry_.new_u8(key), or);} - public int Get_as_int_or(byte[] key, int or) { - byte[] rv = Get_as_bry_or(key, null); - return rv == null ? or : Bry_.To_int(rv); - } - public long Get_as_long(String key) { - long rv = Get_as_long_or(key, Long_.Min_value); if (rv == Long_.Min_value) throw Err_.new_("json", "key missing", "key", key); - return rv; - } - public long Get_as_long_or(String key, long or) {return Get_as_long_or(Bry_.new_u8(key), or);} - public long Get_as_long_or(byte[] key, long or) { - byte[] rv = Get_as_bry_or(key, null); - return rv == null ? or : Bry_.To_long_or(rv, or); - } - public boolean Get_as_bool_or(String key, boolean or) {return Get_as_bool_or(Bry_.new_u8(key), or);} - public boolean Get_as_bool_or(byte[] key, boolean or) { - byte[] rv = Get_as_bry_or(key, null); - return rv == null ? or : Bry_.Eq(rv, Bool_.True_bry); - } - public DateAdp Get_as_date_by_utc(String key) { - byte[] rv = Get_as_bry_or(Bry_.new_u8(key), null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); - return DateAdp_.parse_gplx(String_.new_u8(rv)); - } - - // to convert - public boolean Has(byte[] key) {return Get_bry(key, null) != null;} - public Json_kv Get_at_as_kv(int i) { - Json_itm rv_itm = Get_at(i); - Json_kv rv = Json_kv.cast(rv_itm); if (rv == null) throw Err_.new_("json", "sub is not kv", "i", i, "src", Bry_.Mid(jdoc.Src(), this.Src_bgn(), src_end)); - return rv; - } - - public Json_kv Get_kv(byte[] key) {return Json_kv.cast(Get_itm(key));} - public Json_nde Get(String key) {return Get(Bry_.new_u8(key));} - public Json_nde Get(byte[] key) { - Json_kv kv = Json_kv.cast(this.Get_itm(key)); if (kv == null) throw Err_.new_("json", "kv not found", "key", key); - Json_nde rv = Json_nde.cast(kv.Val()); if (rv == null) throw Err_.new_("json", "nde not found", "key", key); - return rv; - } - public Json_itm Get_itm(byte[] key) { - for (int i = 0; i < subs_len; i++) { - Json_itm itm = subs[i]; - if (itm.Tid() == Json_itm_.Tid__kv) { - Json_kv itm_as_kv = (Json_kv)itm; - if (Bry_.Eq(key, itm_as_kv.Key().Data_bry())) - return itm; - } - } - return null; - } - public Json_ary Get_ary(String key) {return Get_ary(Bry_.new_u8(key));} - public Json_ary Get_ary(byte[] key) {return Json_ary.cast(Get_kv(key).Val_as_ary());} - public String Get_str(String key) {return String_.new_u8(Get_bry(Bry_.new_u8(key)));} - public byte[] Get_bry(byte[] key) { - byte[] rv = Get_bry(key, null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); - return rv; - } - public byte[] Get_bry_or_null(String key) {return Get_bry(Bry_.new_u8(key), null);} - public byte[] Get_bry_or_null(byte[] key) {return Get_bry(key, null);} - public byte[] Get_bry(byte[] key, byte[] or) { - Json_itm kv_obj = Get_itm(key); - if (kv_obj == null) return or; // key not found; - if (kv_obj.Tid() != Json_itm_.Tid__kv) return or; // key is not a key_val - Json_kv kv = (Json_kv)kv_obj; - Json_itm val = kv.Val(); - return (val == null) ? or : val.Data_bry(); - } - public Json_nde Add_many(Json_itm... ary) { - int len = ary.length; - for (int i = 0; i < len; i++) - Add(ary[i]); - return this; - } - public void Add(Json_itm itm) { - int new_len = subs_len + 1; - if (new_len > subs_max) { // ary too small >>> expand - subs_max = new_len * 2; - Json_itm[] new_subs = new Json_itm[subs_max]; - Array_.Copy_to(subs, 0, new_subs, 0, subs_len); - subs = new_subs; - } - subs[subs_len] = (Json_itm)itm; - subs_len = new_len; - subs_hash = null; - } - @Override public void Print_as_json(Bry_bfr bfr, int depth) { - if (bfr.Len() != 0) bfr.Add_byte_nl(); - Json_grp_.Print_indent(bfr, depth); - bfr.Add_byte(Byte_ascii.Curly_bgn).Add_byte(Byte_ascii.Space); - for (int i = 0; i < subs_len; i++) { - if (i != 0) { - Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); - bfr.Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Space); - } - subs[i].Print_as_json(bfr, depth + 1); - } - Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); - bfr.Add_byte(Byte_ascii.Curly_end).Add_byte_nl(); - } - private Hash_adp_bry subs_hash_init() { - Hash_adp_bry rv = Hash_adp_bry.cs(); - for (int i = 0; i < subs_len; ++i) { - Json_itm itm = subs[i]; - if (itm.Tid() == Json_itm_.Tid__kv) { - Json_kv itm_as_kv = (Json_kv)itm; - rv.Add(itm_as_kv.Key().Data_bry(), itm_as_kv.Val()); - } - } - return rv; - } - public static Json_nde cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__nde ? null : (Json_nde)v;} -} +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +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.langs.jsons; + +import gplx.Array_; +import gplx.Bool_; +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Byte_ascii; +import gplx.DateAdp; +import gplx.DateAdp_; +import gplx.Decimal_adp_; +import gplx.Err_; +import gplx.Hash_adp_bry; +import gplx.Int_; +import gplx.Long_; +import gplx.String_; + +public class Json_nde extends Json_itm_base implements Json_grp { + private final int src_bgn; + private int src_end; + private Json_itm[] subs = Json_itm_.Ary_empty; + private int subs_len = 0, subs_max = 0; + private Hash_adp_bry subs_hash; + + private Json_nde(Json_doc jdoc, int src_bgn) { + this.jdoc = jdoc; + this.src_bgn = src_bgn; + } + @Override public byte Tid() {return Json_itm_.Tid__nde;} + public Json_doc Doc() {return jdoc;} private final Json_doc jdoc; + public void Src_end_(int v) {this.src_end = v;} + @Override public Object Data() {return null;} + @Override public byte[] Data_bry() {return null;} + public int Len() {return subs_len;} + public Json_itm Get_at(int i) {return subs[i];} + public Json_itm Get_as_itm_or_null(String key) {return Get_as_itm_or_null(Bry_.new_u8(key));} + public Json_itm Get_as_itm_or_null(byte[] key) {if (subs_hash == null) subs_hash = subs_hash_init(); return (Json_itm)subs_hash.Get_by_bry(key);} + public Json_ary Get_as_ary(int idx) {return Json_ary.cast(Get_at(idx));} + public Json_nde Get_as_nde(String key) {return Json_nde.Cast(Get_as_itm_or_null(Bry_.new_u8(key)));} + public Json_nde Get_as_nde(int idx) {return Json_nde.Cast(Get_at(idx));} + public Json_ary Get_as_ary(String key) {return Get_as_ary(Bry_.new_u8(key));} + public Json_ary Get_as_ary(byte[] key) { + Json_itm rv = Get_as_itm_or_null(key); if (rv == null) throw Err_.new_("json", "key missing", "key", key); + return Json_ary.cast(rv); + } + public Json_ary Get_as_ary_or_null(String key) {return Get_as_ary_or_null(Bry_.new_u8(key));} + public Json_ary Get_as_ary_or_null(byte[] key) { + Json_itm rv = Get_as_itm_or_null(key); + return rv == null + ? null + : Json_ary.cast(rv); + } + public byte[] Get_as_bry(String key) { + byte[] rv = Get_as_bry_or(Bry_.new_u8(key), null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); + return rv; + } + public byte[] Get_as_bry_or(byte[] key, byte[] or) { + Json_itm rv = Get_as_itm_or_null(key); + return rv == null ? or : rv.Data_bry(); + } + public String Get_as_str(String key) { + String rv = Get_as_str_or(key, null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); + return rv; + } + public String Get_as_str_or(String key, String or) {return Get_as_str_or(Bry_.new_u8(key), or);} + public String Get_as_str_or(byte[] key, String or) { + byte[] rv = Get_as_bry_or(key, null); + return rv == null ? or : String_.new_u8(rv); + } + public int Get_as_int(String key) { + int rv = Get_as_int_or(key, Int_.Min_value); if (rv == Int_.Min_value) throw Err_.new_("json", "key missing", "key", key); + return rv; + } + public int Get_as_int_or(String key, int or) {return Get_as_int_or(Bry_.new_u8(key), or);} + public int Get_as_int_or(byte[] key, int or) { + byte[] rv = Get_as_bry_or(key, null); + return rv == null ? or : Bry_.To_int(rv); + } + public long Get_as_long(String key) { + long rv = Get_as_long_or(key, Long_.Min_value); if (rv == Long_.Min_value) throw Err_.new_("json", "key missing", "key", key); + return rv; + } + public long Get_as_long_or(String key, long or) {return Get_as_long_or(Bry_.new_u8(key), or);} + public long Get_as_long_or(byte[] key, long or) { + byte[] rv = Get_as_bry_or(key, null); + return rv == null ? or : Bry_.To_long_or(rv, or); + } + public boolean Get_as_bool_or(String key, boolean or) {return Get_as_bool_or(Bry_.new_u8(key), or);} + public boolean Get_as_bool_or(byte[] key, boolean or) { + byte[] rv = Get_as_bry_or(key, null); + return rv == null ? or : Bry_.Eq(rv, Bool_.True_bry); + } + public DateAdp Get_as_date_by_utc(String key) { + byte[] rv = Get_as_bry_or(Bry_.new_u8(key), null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); + return DateAdp_.parse_gplx(String_.new_u8(rv)); + } + + // to convert + public boolean Has(byte[] key) {return Get_bry(key, null) != null;} + public Json_kv Get_at_as_kv(int i) { + Json_itm rv_itm = Get_at(i); + Json_kv rv = Json_kv.Cast(rv_itm); + if (rv == null) { + byte[] snip = jdoc == null ? Bry_.new_a7("no source") : Bry_.Mid(jdoc.Src(), src_bgn, src_end); + throw Err_.new_("json", "sub is not kv", "i", i, "src", snip); + } + return rv; + } + + public Json_kv Get_kv(byte[] key) {return Json_kv.Cast(Get_itm(key));} + public Json_nde Get(String key) {return Get(Bry_.new_u8(key));} + public Json_nde Get(byte[] key) { + Json_kv kv = Json_kv.Cast(this.Get_itm(key)); if (kv == null) throw Err_.new_("json", "kv not found", "key", key); + Json_nde rv = Json_nde.Cast(kv.Val()); if (rv == null) throw Err_.new_("json", "nde not found", "key", key); + return rv; + } + public Json_itm Get_itm(byte[] key) { + for (int i = 0; i < subs_len; i++) { + Json_itm itm = subs[i]; + if (itm != null && itm.Tid() == Json_itm_.Tid__kv) { + Json_kv itm_as_kv = (Json_kv)itm; + if (Bry_.Eq(key, itm_as_kv.Key().Data_bry())) + return itm; + } + } + return null; + } + public Json_ary Get_ary(String key) {return Get_ary(Bry_.new_u8(key));} + public Json_ary Get_ary(byte[] key) {return Json_ary.cast(Get_kv(key).Val_as_ary());} + public String Get_str(String key) {return String_.new_u8(Get_bry(Bry_.new_u8(key)));} + public byte[] Get_bry(byte[] key) { + byte[] rv = Get_bry(key, null); if (rv == null) throw Err_.new_("json", "key missing", "key", key); + return rv; + } + public byte[] Get_bry_or_null(String key) {return Get_bry(Bry_.new_u8(key), null);} + public byte[] Get_bry_or_null(byte[] key) {return Get_bry(key, null);} + public byte[] Get_bry(byte[] key, byte[] or) { + Json_itm kv_obj = Get_itm(key); + if (kv_obj == null) return or; // key not found; + if (kv_obj.Tid() != Json_itm_.Tid__kv) return or; // key is not a key_val + Json_kv kv = (Json_kv)kv_obj; + Json_itm val = kv.Val(); + return (val == null) ? or : val.Data_bry(); + } + public void AddKvBool(String key, boolean val) {AddKv(key, Json_itm_bool.Get(val));} + public void AddKvInt(String key, int val) {AddKv(key, Json_itm_int.NewByVal(val));} + public void AddKvDouble(String key, double val) {AddKv(key, Json_itm_decimal.NewByVal(Decimal_adp_.double_(val)));} + public void AddKvStr(String key, byte[] val) {AddKv(key, Json_itm_str.NewByVal(String_.new_u8(val)));} + public void AddKvStr(String key, String val) {AddKv(key, Json_itm_str.NewByVal(val));} + public void AddKvNde(String key, Json_nde val) {AddKv(key, val);} + public void AddKvAry(String key, Json_ary val) {AddKv(key, val);} + private void AddKv(String key, Json_itm val) { + Json_kv rv = new Json_kv(Json_itm_str.NewByVal(key), val); + Add(rv); + } + public Json_nde Add_many(Json_itm... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Add(ary[i]); + return this; + } + public void Add(Json_itm itm) { + int new_len = subs_len + 1; + if (new_len > subs_max) { // ary too small >>> expand + subs_max = new_len * 2; + Json_itm[] new_subs = new Json_itm[subs_max]; + Array_.Copy_to(subs, 0, new_subs, 0, subs_len); + subs = new_subs; + } + subs[subs_len] = (Json_itm)itm; + subs_len = new_len; + subs_hash = null; + } + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + if (bfr.Len() != 0) bfr.Add_byte_nl(); + Json_grp_.Print_indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Curly_bgn).Add_byte(Byte_ascii.Space); + for (int i = 0; i < subs_len; i++) { + if (i != 0) { + Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Space); + } + subs[i].Print_as_json(bfr, depth + 1); + } + Json_grp_.Print_nl(bfr); Json_grp_.Print_indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Curly_end).Add_byte_nl(); + } + private Hash_adp_bry subs_hash_init() { + Hash_adp_bry rv = Hash_adp_bry.cs(); + for (int i = 0; i < subs_len; ++i) { + Json_itm itm = subs[i]; + if (itm.Tid() == Json_itm_.Tid__kv) { + Json_kv itm_as_kv = (Json_kv)itm; + rv.Add(itm_as_kv.Key().Data_bry(), itm_as_kv.Val()); + } + } + return rv; + } + + public static Json_nde NewByDoc(Json_doc doc, int src_bgn) {return new Json_nde(doc, src_bgn);} + public static Json_nde NewByVal() {return new Json_nde(null, -1);} + public static Json_nde Cast(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid__nde ? null : (Json_nde)v;} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_parser.java b/400_xowa/src/gplx/langs/jsons/Json_parser.java index fbfd89731..98bf78d30 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_parser.java +++ b/400_xowa/src/gplx/langs/jsons/Json_parser.java @@ -1,198 +1,197 @@ -/* -XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2020 gnosygnu@gmail.com - -XOWA is licensed under the terms of the General Public License (GPL) Version 3, -or alternatively under the terms of the Apache License Version 2.0. - -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.langs.jsons; - -import gplx.Bool_; -import gplx.Bry_; -import gplx.Byte_ascii; -import gplx.Char_; -import gplx.Err; -import gplx.Err_; -import gplx.Int_; -import gplx.String_; -import gplx.core.primitives.Gfo_number_parser; - -public class Json_parser { - private byte[] src; private int src_len, pos; private final Gfo_number_parser num_parser = new Gfo_number_parser(); - public Json_factory Factory() {return factory;} private final Json_factory factory = new Json_factory(); - public Json_doc Parse_by_apos_ary(String... ary) {return Parse_by_apos(String_.Concat_lines_nl(ary));} - public Json_doc Parse_by_apos(String s) {return Parse(Bry_.Replace(Bry_.new_u8(s), Byte_ascii.Apos, Byte_ascii.Quote));} - public Json_doc Parse(String src) {return Parse(Bry_.new_u8(src));} - public Json_doc Parse(byte[] src) { - synchronized (factory) { - this.src = src; if (src == null) return null; - this.src_len = src.length; if (src_len == 0) return null; - this.pos = 0; - Skip_ws(); - boolean root_is_nde = true; - switch (src[pos]) { - case Byte_ascii.Curly_bgn: root_is_nde = Bool_.Y; break; - case Byte_ascii.Brack_bgn: root_is_nde = Bool_.N; break; - default: return null; - } - Skip_ws(); - Json_doc doc = new Json_doc(); - Json_grp root = null; - if (root_is_nde) - root = Make_nde(doc); - else - root = Make_ary(doc); - doc.Ctor(src, root); - return doc; - } - } - private Json_nde Make_nde(Json_doc doc) { - ++pos; // brack_bgn - Json_nde nde = new Json_nde(doc, pos); - while (pos < src_len) { - Skip_ws(); - if (src[pos] == Byte_ascii.Curly_end) {++pos; return nde;} - else nde.Add(Make_kv(doc)); - Skip_ws(); - switch (src[pos++]) { - case Byte_ascii.Comma: break; - case Byte_ascii.Curly_end: return nde; - default: throw Err_.new_unhandled(src[pos - 1]); - } - } - throw Err_.new_wo_type("eos inside nde"); - } - private Json_itm Make_kv(Json_doc doc) { - Json_itm key = Make_string(doc); - Skip_ws(); - Chk(Byte_ascii.Colon); - Skip_ws(); - Json_itm val = Make_val(doc); - return new Json_kv(key, val); - } - private Json_itm Make_val(Json_doc doc) { - while (pos < src_len) { - byte b = src[pos]; - switch (b) { - case Byte_ascii.Ltr_n: return Make_literal(Bry_null_ull , 3, factory.Null()); - case Byte_ascii.Ltr_f: return Make_literal(Bry_bool_alse , 4, factory.Bool_n()); - case Byte_ascii.Ltr_t: return Make_literal(Bry_bool_rue , 3, factory.Bool_y()); - case Byte_ascii.Quote: return Make_string(doc); - case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: - case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: - case Byte_ascii.Dash: return Make_num(doc); - case Byte_ascii.Brack_bgn: return Make_ary(doc); - case Byte_ascii.Curly_bgn: return Make_nde(doc); - } - throw Err_.new_unhandled(Char_.To_str(b)); - } - throw Err_.new_wo_type("eos reached in val"); - } - private Json_itm Make_literal(byte[] remainder, int remainder_len, Json_itm singleton) { - ++pos; // 1st char - int literal_end = pos + remainder_len; - if (Bry_.Eq(src, pos, literal_end, remainder)) { - pos = literal_end; - return singleton; - } - throw Err_.new_("json.parser", "invalid literal", "excerpt", Bry_.Mid_by_len_safe(src, pos - 1, 16)); - } - private Json_itm Make_string(Json_doc doc) { - int bgn = pos++; // ++: quote_bgn - boolean exact = true; - while (pos < src_len) { - switch (src[pos]) { - case Byte_ascii.Backslash: - ++pos; // backslash - switch (src[pos]) { - case Byte_ascii.Ltr_u: pos += 5; break; // \uFFFF 1 u + 4 hex-dec; ISSUE#:486; DATE:2019-06-02 - default: ++pos; break; // \? " \ / b f n r t - } - exact = false; - break; - case Byte_ascii.Quote: - return factory.Str(doc, bgn, ++pos, exact); // ++: quote_end - default: - ++pos; - break; - } - } - throw Err_.new_wo_type("eos reached inside quote"); - } - private Json_itm Make_num(Json_doc doc) { - int num_bgn = pos; - boolean loop = true; - while (loop) { - if (pos == src_len) throw Err_.new_wo_type("eos reached inside num"); - switch (src[pos]) { - case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: - case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: - ++pos; - break; - case Byte_ascii.Dot: - case Byte_ascii.Dash: case Byte_ascii.Plus: - case Byte_ascii.Ltr_E: case Byte_ascii.Ltr_e: // e e+ e- E E+ E- - ++pos; - break; - default: - loop = false; - break; - } - } - num_parser.Parse(src, num_bgn, pos); - if (num_parser.Has_frac()) - return factory.Decimal(doc, num_bgn, pos); - else { - if (num_parser.Is_int()) - return factory.Int(doc, num_bgn, pos); - else - return factory.Long(doc, num_bgn, pos); - } - } - private Json_ary Make_ary(Json_doc doc) { - Json_ary rv = factory.Ary(pos++, pos); // brack_bgn - while (pos < src_len) { - Skip_ws(); - if (src[pos] == Byte_ascii.Brack_end) {++pos; return rv;} - else rv.Add(Make_val(doc)); - Skip_ws(); - switch (src[pos]) { - case Byte_ascii.Comma: ++pos; break; - case Byte_ascii.Brack_end: ++pos; return rv; - } - } - throw Err_.new_wo_type("eos inside ary"); - } - private void Skip_ws() { - while (pos < src_len) { - switch (src[pos]) { - case Byte_ascii.Space: case Byte_ascii.Nl: case Byte_ascii.Tab: case Byte_ascii.Cr: ++pos; break; - default: return; - } - } - } - private void Chk(byte expd) { - if (src[pos] == expd) - ++pos; - else - throw err_(src, pos, "expected '{0}' but got '{1}'", Char_.To_str(expd), Char_.To_str(src[pos])); - } - private Err err_(byte[] src, int bgn, String fmt, Object... args) {return err_(src, bgn, src.length, fmt, args);} - private Err err_(byte[] src, int bgn, int src_len, String fmt, Object... args) { - String msg = String_.Format(fmt, args) + " " + Int_.To_str(bgn) + " " + String_.new_u8__by_len(src, bgn, 20); - return Err_.new_wo_type(msg); - } - private static final byte[] Bry_bool_rue = Bry_.new_a7("rue"), Bry_bool_alse = Bry_.new_a7("alse"), Bry_null_ull = Bry_.new_a7("ull"); - public static Json_doc ParseToJdoc(String src) { - Json_parser parser = new Json_parser(); - return parser.Parse(src); - } -} +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +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.langs.jsons; + +import gplx.Bool_; +import gplx.Bry_; +import gplx.Byte_ascii; +import gplx.Char_; +import gplx.Err; +import gplx.Err_; +import gplx.Int_; +import gplx.String_; +import gplx.core.primitives.Gfo_number_parser; + +public class Json_parser { + private byte[] src; + private int src_len, pos; + private final Gfo_number_parser num_parser = new Gfo_number_parser(); + public Json_doc Parse_by_apos_ary(String... ary) {return Parse_by_apos(String_.Concat_lines_nl(ary));} + public Json_doc Parse_by_apos(String s) {return Parse(Bry_.Replace(Bry_.new_u8(s), Byte_ascii.Apos, Byte_ascii.Quote));} + public Json_doc Parse(String src) {return Parse(Bry_.new_u8(src));} + public Json_doc Parse(byte[] src) { + this.src = src; if (src == null) return null; + this.src_len = src.length; if (src_len == 0) return null; + this.pos = 0; + Skip_ws(); + boolean root_is_nde = true; + switch (src[pos]) { + case Byte_ascii.Curly_bgn: root_is_nde = Bool_.Y; break; + case Byte_ascii.Brack_bgn: root_is_nde = Bool_.N; break; + default: return null; + } + Skip_ws(); + Json_doc doc = new Json_doc(); + Json_grp root = null; + if (root_is_nde) + root = Make_nde(doc); + else + root = Make_ary(doc); + doc.Ctor(src, root); + return doc; + } + private Json_nde Make_nde(Json_doc doc) { + ++pos; // brack_bgn + Json_nde nde = Json_nde.NewByDoc(doc, pos); + while (pos < src_len) { + Skip_ws(); + if (src[pos] == Byte_ascii.Curly_end) {++pos; return nde;} + else nde.Add(Make_kv(doc)); + Skip_ws(); + switch (src[pos++]) { + case Byte_ascii.Comma: break; + case Byte_ascii.Curly_end: return nde; + default: throw Err_.new_unhandled(src[pos - 1]); + } + } + throw Err_.new_wo_type("eos inside nde"); + } + private Json_itm Make_kv(Json_doc doc) { + Json_itm key = Make_string(doc); + Skip_ws(); + Chk(Byte_ascii.Colon); + Skip_ws(); + Json_itm val = Make_val(doc); + return new Json_kv(key, val); + } + private Json_itm Make_val(Json_doc doc) { + while (pos < src_len) { + byte b = src[pos]; + switch (b) { + case Byte_ascii.Ltr_n: return Make_literal(Bry_null_ull , 3, Json_itm_null.Null); + case Byte_ascii.Ltr_f: return Make_literal(Bry_bool_alse , 4, Json_itm_bool.Bool_n); + case Byte_ascii.Ltr_t: return Make_literal(Bry_bool_rue , 3, Json_itm_bool.Bool_y); + case Byte_ascii.Quote: return Make_string(doc); + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + case Byte_ascii.Dash: return Make_num(doc); + case Byte_ascii.Brack_bgn: return Make_ary(doc); + case Byte_ascii.Curly_bgn: return Make_nde(doc); + } + throw Err_.new_unhandled(Char_.To_str(b)); + } + throw Err_.new_wo_type("eos reached in val"); + } + private Json_itm Make_literal(byte[] remainder, int remainder_len, Json_itm singleton) { + ++pos; // 1st char + int literal_end = pos + remainder_len; + if (Bry_.Eq(src, pos, literal_end, remainder)) { + pos = literal_end; + return singleton; + } + throw Err_.new_("json.parser", "invalid literal", "excerpt", Bry_.Mid_by_len_safe(src, pos - 1, 16)); + } + private Json_itm Make_string(Json_doc doc) { + int bgn = pos++; // ++: quote_bgn + boolean escaped = false; + while (pos < src_len) { + switch (src[pos]) { + case Byte_ascii.Backslash: + ++pos; // backslash + switch (src[pos]) { + case Byte_ascii.Ltr_u: pos += 5; break; // \uFFFF 1 u + 4 hex-dec; ISSUE#:486; DATE:2019-06-02 + default: ++pos; break; // \? " \ / b f n r t + } + escaped = true; + break; + case Byte_ascii.Quote: + return Json_itm_str.NewByDoc(doc, bgn, ++pos, escaped); // ++: quote_end + default: + ++pos; + break; + } + } + throw Err_.new_wo_type("eos reached inside quote"); + } + private Json_itm Make_num(Json_doc doc) { + int num_bgn = pos; + boolean loop = true; + while (loop) { + if (pos == src_len) throw Err_.new_wo_type("eos reached inside num"); + switch (src[pos]) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + ++pos; + break; + case Byte_ascii.Dot: + case Byte_ascii.Dash: case Byte_ascii.Plus: + case Byte_ascii.Ltr_E: case Byte_ascii.Ltr_e: // e e+ e- E E+ E- + ++pos; + break; + default: + loop = false; + break; + } + } + num_parser.Parse(src, num_bgn, pos); + if (num_parser.Has_frac()) + return Json_itm_decimal.NewByDoc(doc, num_bgn, pos); + else { + if (num_parser.Is_int()) + return Json_itm_int.NewByDoc(doc, num_bgn, pos); + else + return Json_itm_long.NewByDoc(doc, num_bgn, pos); + } + } + private Json_ary Make_ary(Json_doc doc) { + Json_ary rv = Json_ary.NewByDoc(doc, pos++, pos); // brack_bgn + while (pos < src_len) { + Skip_ws(); + if (src[pos] == Byte_ascii.Brack_end) {++pos; return rv;} + else rv.Add(Make_val(doc)); + Skip_ws(); + switch (src[pos]) { + case Byte_ascii.Comma: ++pos; break; + case Byte_ascii.Brack_end: ++pos; return rv; + } + } + throw Err_.new_wo_type("eos inside ary"); + } + private void Skip_ws() { + while (pos < src_len) { + switch (src[pos]) { + case Byte_ascii.Space: case Byte_ascii.Nl: case Byte_ascii.Tab: case Byte_ascii.Cr: ++pos; break; + default: return; + } + } + } + private void Chk(byte expd) { + if (src[pos] == expd) + ++pos; + else + throw err_(src, pos, "expected '{0}' but got '{1}'", Char_.To_str(expd), Char_.To_str(src[pos])); + } + private Err err_(byte[] src, int bgn, String fmt, Object... args) {return err_(src, bgn, src.length, fmt, args);} + private Err err_(byte[] src, int bgn, int src_len, String fmt, Object... args) { + String msg = String_.Format(fmt, args) + " " + Int_.To_str(bgn) + " " + String_.new_u8__by_len(src, bgn, 20); + return Err_.new_wo_type(msg); + } + private static final byte[] Bry_bool_rue = Bry_.new_a7("rue"), Bry_bool_alse = Bry_.new_a7("alse"), Bry_null_ull = Bry_.new_a7("ull"); + public static Json_doc ParseToJdoc(String src) { + Json_parser parser = new Json_parser(); + return parser.Parse(src); + } +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_parser__list_nde__base.java b/400_xowa/src/gplx/langs/jsons/Json_parser__list_nde__base.java index 00a182a35..1ad877ef0 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_parser__list_nde__base.java +++ b/400_xowa/src/gplx/langs/jsons/Json_parser__list_nde__base.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,56 +13,61 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -import gplx.core.primitives.*; -public class Json_parser__list_nde__base extends Json_parser__itm__base { - public void Parse_grp(String context, Json_grp grp) { - this.context = context; - int len = grp.Len(); - for (int i = 0; i < len; ++i) { - Json_nde sub = null; - if (grp.Tid() == Json_itm_.Tid__nde) { - Json_kv kv = Json_nde.cast(grp).Get_at_as_kv(i); - sub = kv.Val_as_nde(); - } - else { - sub = Json_nde.cast(grp.Get_at(i)); - } - Parse_nde(context, sub); - } - } - public void Parse_nde(String context, Json_nde nde) { - this.cur_itm = nde; - for (int j = 0; j < keys_len; ++j) - atrs[j] = null; - int atr_len = nde.Len(); - for (int j = 0; j < atr_len; ++j) { - Json_kv atr = nde.Get_at_as_kv(j); - Object idx_obj = hash.Get_by_bry(atr.Key_as_bry()); - if (idx_obj == null) {Warn("unknown json parser key", atr); continue;} - int idx_int = ((Int_obj_val)idx_obj).Val(); - atrs[idx_int] = atr; - } - Parse_hook_nde(nde, atrs); - } - public void Parse_to_list_as_bry(String context, Json_ary ary, Ordered_hash list) { - this.cur_itm = ary; - int len = ary.Len(); - for (int i = 0; i < len; ++i) { - byte[] val = ary.Get_at(i).Data_bry(); - list.Add(val, val); - } - } - public void Parse_to_list_as_kv(String context, Json_nde nde, Ordered_hash list) { - this.cur_itm = nde; - int len = nde.Len(); - for (int i = 0; i < len; ++i) { - Json_kv sub = nde.Get_at_as_kv(i); - byte[] key = sub.Key_as_bry(); - byte[] val = Parse_to_list_as_kv__get_val(sub, key); - list.Add(key, Keyval_.new_(String_.new_u8(key), String_.new_u8(val))); - } - } - @gplx.Virtual protected byte[] Parse_to_list_as_kv__get_val(Json_kv sub, byte[] key) {return sub.Val_as_bry();} - @Override protected void Parse_hook_nde(Json_nde sub, Json_kv[] atrs) {} -} +package gplx.langs.jsons; + +import gplx.Keyval_; +import gplx.Ordered_hash; +import gplx.String_; +import gplx.core.primitives.Int_obj_val; + +public class Json_parser__list_nde__base extends Json_parser__itm__base { + public void Parse_grp(String context, Json_grp grp) { + this.context = context; + int len = grp.Len(); + for (int i = 0; i < len; ++i) { + Json_nde sub = null; + if (grp.Tid() == Json_itm_.Tid__nde) { + Json_kv kv = Json_nde.Cast(grp).Get_at_as_kv(i); + sub = kv.Val_as_nde(); + } + else { + sub = Json_nde.Cast(grp.Get_at(i)); + } + Parse_nde(context, sub); + } + } + public void Parse_nde(String context, Json_nde nde) { + this.cur_itm = nde; + for (int j = 0; j < keys_len; ++j) + atrs[j] = null; + int atr_len = nde.Len(); + for (int j = 0; j < atr_len; ++j) { + Json_kv atr = nde.Get_at_as_kv(j); + Object idx_obj = hash.Get_by_bry(atr.Key_as_bry()); + if (idx_obj == null) {Warn("unknown json parser key", atr); continue;} + int idx_int = ((Int_obj_val)idx_obj).Val(); + atrs[idx_int] = atr; + } + Parse_hook_nde(nde, atrs); + } + public void Parse_to_list_as_bry(String context, Json_ary ary, Ordered_hash list) { + this.cur_itm = ary; + int len = ary.Len(); + for (int i = 0; i < len; ++i) { + byte[] val = ary.Get_at(i).Data_bry(); + list.Add(val, val); + } + } + public void Parse_to_list_as_kv(String context, Json_nde nde, Ordered_hash list) { + this.cur_itm = nde; + int len = nde.Len(); + for (int i = 0; i < len; ++i) { + Json_kv sub = nde.Get_at_as_kv(i); + byte[] key = sub.Key_as_bry(); + byte[] val = Parse_to_list_as_kv__get_val(sub, key); + list.Add(key, Keyval_.new_(String_.new_u8(key), String_.new_u8(val))); + } + } + @gplx.Virtual protected byte[] Parse_to_list_as_kv__get_val(Json_kv sub, byte[] key) {return sub.Val_as_bry();} + @Override protected void Parse_hook_nde(Json_nde sub, Json_kv[] atrs) {} +} diff --git a/400_xowa/src/gplx/langs/jsons/Json_parser_tst.java b/400_xowa/src/gplx/langs/jsons/Json_parser_tst.java index 03f217de7..57df912cd 100644 --- a/400_xowa/src/gplx/langs/jsons/Json_parser_tst.java +++ b/400_xowa/src/gplx/langs/jsons/Json_parser_tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,87 +13,96 @@ 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.langs.jsons; import gplx.*; import gplx.langs.*; -import org.junit.*; -public class Json_parser_tst { - private final Json_parser_fxt fxt = new Json_parser_fxt(); - @Before public void init() {fxt.Clear();} - @Test public void Null() {fxt.Test_parse_val0("{'k0':null}" , null);} - @Test public void Bool_n() {fxt.Test_parse_val0("{'k0':false}" , false);} - @Test public void Bool_y() {fxt.Test_parse_val0("{'k0':true}" , true);} - @Test public void Num() {fxt.Test_parse_val0("{'k0':123}" , 123);} - @Test public void Num_neg() {fxt.Test_parse_val0("{'k0':-123}" , -123);} - @Test public void Str() {fxt.Test_parse_val0("{'k0':'v0'}" , "v0");} - @Test public void Str_esc_quote() {fxt.Test_parse_val0("{'k0':'a\\\"b'}" , "a\"b");} - @Test public void Str_encoded_basic() {fxt.Test_parse_val0("{'k0':'a\\u0021b'}" , "a!b");} - @Test public void Str_encoded_surrogate() {fxt.Test_parse_val0("{'k0':'a\\ud83c\\udf0eb'}", "a🌎b");} // check for UTF surrogate-pairs; symbol is earth globe americas (U+1F30E); ISSUE#:487; DATE:2019-06-02 - @Test public void Num_dec() {fxt.Test_parse("{'k0':1.23}" , fxt.itm_nde_().Add_many(fxt.itm_kv_dec_("k0", "1.23")));} - @Test public void Num_exp() {fxt.Test_parse("{'k0':1e+2}" , fxt.itm_nde_().Add_many(fxt.itm_kv_dec_("k0", "1e+2")));} - @Test public void Num_mix() {fxt.Test_parse("{'k0':-1.23e-1}" , fxt.itm_nde_().Add_many(fxt.itm_kv_dec_("k0", "-1.23e-1")));} - @Test public void Str_many() {fxt.Test_parse("{'k0':'v0','k1':'v1','k2':'v2'}", fxt.itm_nde_().Add_many(fxt.itm_kv_("k0", "v0"), fxt.itm_kv_("k1", "v1"), fxt.itm_kv_("k2", "v2")));} - @Test public void Ary_empty() {fxt.Test_parse("{'k0':[]}", fxt.itm_nde_().Add_many(fxt.itm_kv_ary_int_("k0")));} - @Test public void Ary_int() {fxt.Test_parse("{'k0':[1,2,3]}", fxt.itm_nde_().Add_many(fxt.itm_kv_ary_int_("k0", 1, 2, 3)));} - @Test public void Ary_str() {fxt.Test_parse("{'k0':['a','b','c']}", fxt.itm_nde_().Add_many(fxt.itm_kv_ary_str_("k0", "a", "b", "c")));} - @Test public void Ary_ws() {fxt.Test_parse("{'k0': [ 1 , 2 , 3 ] }", fxt.itm_nde_().Add_many(fxt.itm_kv_ary_int_("k0", 1, 2, 3)));} - @Test public void Subs_int() {fxt.Test_parse("{'k0':{'k00':1}}", fxt.itm_nde_().Add_many(fxt.itm_kv_("k0", fxt.itm_nde_().Add_many(fxt.itm_kv_("k00", 1)))));} - @Test public void Subs_empty() {fxt.Test_parse("{'k0':{}}", fxt.itm_nde_().Add_many(fxt.itm_kv_("k0", fxt.itm_nde_())));} - @Test public void Subs_ws() {fxt.Test_parse("{'k0': { 'k00' : 1 } }", fxt.itm_nde_().Add_many(fxt.itm_kv_("k0", fxt.itm_nde_().Add_many(fxt.itm_kv_("k00", 1)))));} - @Test public void Ws() {fxt.Test_parse(" { 'k0' : 'v0' } ", fxt.itm_nde_().Add_many(fxt.itm_kv_("k0", "v0")));} - @Test public void Root_is_ary() {fxt.Test_parse("[ 1 , 2 , 3 ]", fxt.itm_ary_().Add_many(fxt.itm_int_(1), fxt.itm_int_(2), fxt.itm_int_(3)));} - public static String Replace_apos_as_str(String v) {return String_.new_u8(Replace_apos(Bry_.new_u8(v)));} - public static byte[] Replace_apos(byte[] v) {return Bry_.Replace(v, Byte_ascii.Apos, Byte_ascii.Quote);} -} -class Json_parser_fxt { - public void Clear() { - if (parser == null) { - parser = new Json_parser(); - factory = parser.Factory(); - } - } Json_parser parser; Json_factory factory; Bry_bfr tmp_bfr = Bry_bfr_.Reset(255); - public Json_itm itm_int_(int v) {return Json_itm_tmp.new_int_(v);} - Json_itm itm_str_(String v) {return Json_itm_tmp.new_str_(v);} - public Json_ary itm_ary_() {return factory.Ary(-1, -1);} - public Json_nde itm_nde_() {return factory.Nde(null, -1);} - public Json_kv itm_kv_null_(String k) {return factory.Kv(itm_str_(k), factory.Null());} - public Json_kv itm_kv_(String k, String v) {return factory.Kv(itm_str_(k), itm_str_(v));} - public Json_kv itm_kv_(String k, int v) {return factory.Kv(itm_str_(k), itm_int_(v));} - public Json_kv itm_kv_(String k, boolean v) {return factory.Kv(itm_str_(k), v ? factory.Bool_y() : factory.Bool_n());} - public Json_kv itm_kv_dec_(String k, String v) {return factory.Kv(itm_str_(k), new Json_itm_tmp(Json_itm_.Tid__decimal, v));} - public Json_kv itm_kv_(String k, Json_nde v) {return factory.Kv(itm_str_(k), v);} - public Json_kv itm_kv_ary_int_(String k, int... v) { - Json_ary ary = factory.Ary(-1, -1); - int len = v.length; - for (int i = 0; i < len; i++) - ary.Add(itm_int_(v[i])); - return factory.Kv(itm_str_(k), ary); - } - public Json_kv itm_kv_ary_str_(String k, String... v) { - Json_ary ary = factory.Ary(-1, -1); - int len = v.length; - for (int i = 0; i < len; i++) - ary.Add(itm_str_(v[i])); - return factory.Kv(itm_str_(k), ary); - } - public void Test_parse(String raw_str, Json_itm... expd_ary) { - byte[] raw = Json_parser_tst.Replace_apos(Bry_.new_u8(raw_str)); - Json_doc doc = parser.Parse(raw); - doc.Root_grp().Print_as_json(tmp_bfr, 0); - String actl = tmp_bfr.To_str_and_clear(); - String expd = Xto_str(raw, doc, expd_ary, 0, expd_ary.length); - Tfds.Eq_str_lines(expd, actl, actl); - } - public void Test_parse_val0(String raw_str, Object expd) { - byte[] raw = Json_parser_tst.Replace_apos(Bry_.new_u8(raw_str)); - Json_doc doc = parser.Parse(raw); - Json_kv kv = Json_kv.cast(doc.Root_nde().Get_at(0)); // assume root has kv as first sub; EX: {"a":"b"} - Object actl = kv.Val().Data(); // NOTE: Data_bry is escaped val; EX: a\"b has DataBry of a"b - Tfds.Eq(expd, actl); - } - String Xto_str(byte[] raw, Json_doc doc, Json_itm[] ary, int bgn, int end) { - for (int i = bgn; i < end; i++) { - Json_itm itm = ary[i]; - itm.Print_as_json(tmp_bfr, 0); - } - return tmp_bfr.To_str_and_clear(); - } -} +package gplx.langs.jsons; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Decimal_adp; +import gplx.Decimal_adp_; +import gplx.Tfds; +import gplx.core.tests.Gftest; +import org.junit.Test; + +public class Json_parser_tst { + private final Json_parser_fxt fxt = new Json_parser_fxt(); + @Test public void Null() {fxt.Test_parse_obj("{'k0':null}" , null);} + @Test public void Bool_n() {fxt.Test_parse_obj("{'k0':false}" , false);} + @Test public void Bool_y() {fxt.Test_parse_obj("{'k0':true}" , true);} + @Test public void Num() {fxt.Test_parse_obj("{'k0':123}" , 123);} + @Test public void Num_neg() {fxt.Test_parse_obj("{'k0':-123}" , -123);} + @Test public void Str() {fxt.Test_parse_obj("{'k0':'v0'}" , "v0");} + @Test public void Str_esc_quote() {fxt.Test_parse_obj("{'k0':'a\\\"b'}" , "a\"b");} + @Test public void Str_encoded_basic() {fxt.Test_parse_obj("{'k0':'a\\u0021b'}" , "a!b");} + @Test public void Str_encoded_surrogate() {fxt.Test_parse_obj("{'k0':'a\\ud83c\\udf0eb'}", "a🌎b");} // check for UTF surrogate-pairs; symbol is earth globe americas (U+1F30E); ISSUE#:487; DATE:2019-06-02 + @Test public void Num_dec() {fxt.Test_parse_dec("{'k0':1.23}" , Decimal_adp_.parse("1.23"));} + @Test public void Num_exp() {fxt.Test_parse_obj("{'k0':1e+2}" , 100);} + @Test public void Num_mix() {fxt.Test_parse_dec("{'k0':-1.23e-1}" , Decimal_adp_.parse("-1.23e-1"));} + @Test public void Str_many() {fxt.Test_parse("{'k0':'v0','k1':'v1','k2':'v2'}" , fxt.Init_nde().Add_many(fxt.Init_kv("k0", "v0"), fxt.Init_kv("k1", "v1"), fxt.Init_kv("k2", "v2")));} + @Test public void Ary_empty() {fxt.Test_parse("{'k0':[]}" , fxt.Init_nde().Add_many(fxt.Init_kv_ary_int("k0")));} + @Test public void Ary_int() {fxt.Test_parse("{'k0':[1,2,3]}" , fxt.Init_nde().Add_many(fxt.Init_kv_ary_int("k0", 1, 2, 3)));} + @Test public void Ary_str() {fxt.Test_parse("{'k0':['a','b','c']}" , fxt.Init_nde().Add_many(fxt.Init_kvary_str_("k0", "a", "b", "c")));} + @Test public void Ary_ws() {fxt.Test_parse("{'k0': [ 1 , 2 , 3 ] }" , fxt.Init_nde().Add_many(fxt.Init_kv_ary_int("k0", 1, 2, 3)));} + @Test public void Subs_int() {fxt.Test_parse("{'k0':{'k00':1}}" , fxt.Init_nde().Add_many(fxt.Init_kv("k0", fxt.Init_nde().Add_many(fxt.Init_kv("k00", 1)))));} + @Test public void Subs_empty() {fxt.Test_parse("{'k0':{}}" , fxt.Init_nde().Add_many(fxt.Init_kv("k0", fxt.Init_nde())));} + @Test public void Subs_ws() {fxt.Test_parse("{'k0': { 'k00' : 1 } }" , fxt.Init_nde().Add_many(fxt.Init_kv("k0", fxt.Init_nde().Add_many(fxt.Init_kv("k00", 1)))));} + @Test public void Ws() {fxt.Test_parse(" { 'k0' : 'v0' } " , fxt.Init_nde().Add_many(fxt.Init_kv("k0", "v0")));} + @Test public void Root_is_ary() {fxt.Test_parse("[1,2,3]" , fxt.Init_ary().Add_many(fxt.Init_int(1), fxt.Init_int(2), fxt.Init_int(3)));} +} +class Json_parser_fxt { + private final Json_parser parser = new Json_parser(); + private final Bry_bfr tmp_bfr = Bry_bfr_.Reset(255); + public Json_itm Init_int(int v) {return Json_itm_int.NewByVal(v);} + public Json_itm Init_str(String v) {return Json_itm_str.NewByVal(v);} + public Json_ary Init_ary() {return Json_ary.NewByVal();} + public Json_nde Init_nde() {return Json_nde.NewByVal();} + public Json_kv Init_kv_null(String k) {return new Json_kv(Init_str(k), Json_itm_null.Null);} + public Json_kv Init_kv(String k, String v) {return new Json_kv(Init_str(k), Init_str(v));} + public Json_kv Init_kv(String k, int v) {return new Json_kv(Init_str(k), Init_int(v));} + public Json_kv Init_kv(String k, boolean v) {return new Json_kv(Init_str(k), v ? Json_itm_bool.Bool_y : Json_itm_bool.Bool_n);} + public Json_kv Init_kv(String k, Json_nde v) {return new Json_kv(Init_str(k), v);} + public Json_kv Init_kv_ary_int(String k, int... v) { + Json_ary ary = Json_ary.NewByVal(); + int len = v.length; + for (int i = 0; i < len; i++) + ary.Add(Init_int(v[i])); + return new Json_kv(Init_str(k), ary); + } + public Json_kv Init_kvary_str_(String k, String... v) { + Json_ary ary = Json_ary.NewByVal(); + int len = v.length; + for (int i = 0; i < len; i++) + ary.Add(Init_str(v[i])); + return new Json_kv(Init_str(k), ary); + } + public void Test_parse(String raw_str, Json_itm... expd_ary) { + byte[] raw = Bry_.new_u8(Json_doc.Make_str_by_apos(raw_str)); + Json_doc doc = parser.Parse(raw); + doc.Root_grp().Print_as_json(tmp_bfr, 0); + String actl = tmp_bfr.To_str_and_clear(); + String expd = Xto_str(raw, doc, expd_ary, 0, expd_ary.length); + Tfds.Eq_str_lines(expd, actl, actl); + } + public void Test_parse_obj(String raw_str, Object expd) { + Json_kv kv = Parse_and_get_kv0(raw_str); + Object actl = kv.Val().Data(); // NOTE: Data_bry is escaped val; EX: a\"b has DataBry of a"b + Gftest.Eq__obj_or_null(expd, actl); + } + public void Test_parse_dec(String raw_str, Decimal_adp expd) { + Json_kv kv = Parse_and_get_kv0(raw_str); + Json_itm_decimal decimal_itm = (Json_itm_decimal)kv.Val(); + Gftest.Eq__bool(true, decimal_itm.Data_as_decimal().Eq(expd)); + } + private Json_kv Parse_and_get_kv0(String raw_str) { + byte[] raw = Bry_.new_u8(Json_doc.Make_str_by_apos(raw_str)); + Json_doc doc = parser.Parse(raw); + return Json_kv.Cast(doc.Root_nde().Get_at(0)); // assume root has kv as first sub; EX: {"a":"b"} + } + private String Xto_str(byte[] raw, Json_doc doc, Json_itm[] ary, int bgn, int end) { + for (int i = bgn; i < end; i++) { + Json_itm itm = ary[i]; + itm.Print_as_json(tmp_bfr, 0); + } + return tmp_bfr.To_str_and_clear(); + } +} diff --git a/400_xowa/src/gplx/langs/mustaches/JsonMustacheNde.java b/400_xowa/src/gplx/langs/mustaches/JsonMustacheNde.java new file mode 100644 index 000000000..3b520d319 --- /dev/null +++ b/400_xowa/src/gplx/langs/mustaches/JsonMustacheNde.java @@ -0,0 +1,112 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +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.langs.mustaches; + +import gplx.Bool_; +import gplx.Bry_; +import gplx.String_; +import gplx.langs.jsons.Json_ary; +import gplx.langs.jsons.Json_itm; +import gplx.langs.jsons.Json_itm_; +import gplx.langs.jsons.Json_kv; +import gplx.langs.jsons.Json_nde; +import gplx.objects.Object_; + +public class JsonMustacheNde implements Mustache_doc_itm { + private final Json_nde nde; + public JsonMustacheNde(Json_nde nde) {this.nde = nde;} + public boolean Mustache__write(String key, Mustache_bfr bfr) { + Json_itm itm = nde.Get_itm(Bry_.new_u8(key)); + if (itm == null) { // mustacheKey does not exist in current jsonNde + return false; + } + else { // mustacheKey exists + switch (itm.Tid()) { + // array / bool node -> ignore; EX: `{{#person}}Never shown{{/person}}` + case Json_itm_.Tid__bool: + case Json_itm_.Tid__ary: + case Json_itm_.Tid__nde: + return false; + // item node -> render it; EX: `Hello {{name}}` + default: + bfr.Add_bry(Json_kv.Cast(itm).Val_as_bry()); + return true; + } + } + } + public Mustache_doc_itm[] Mustache__subs(String key) { + Json_itm itm = nde.Get_itm(Bry_.new_u8(key)); + if (itm == null) { // mustacheKey does not exist in current jsonNde + return Mustache_doc_itm_.Ary__bool__n; + } + else { // mustacheKey exists + if (itm.Tid() == Json_itm_.Tid__kv) { + Json_kv kv = Json_kv.Cast(itm); + switch (kv.Val().Tid()) { + // bool node -> render; EX: `{{#person}}Never shown{{/person}}` + case Json_itm_.Tid__bool: + boolean dataVal = Bool_.Cast(kv.Val().Data()); + return dataVal ? Mustache_doc_itm_.Ary__bool__y : Mustache_doc_itm_.Ary__bool__n; + // array node -> render; EX: `{{#repo}} {{name}}{{/repo}}` + case Json_itm_.Tid__ary: + return ToJsonMustachNdeAry(itm); + // item node -> render only if key matchers + default: + return new Mustache_doc_itm[] {new JsonMustacheVal(true, key, kv.Val().Data())}; + } + } + else { + return Mustache_doc_itm_.Ary__bool__n; + } + } + } + private static Mustache_doc_itm[] ToJsonMustachNdeAry(Json_itm itm) { + Json_ary dataAry = Json_ary.cast_or_null(Json_kv.Cast(itm).Val()); + int subs_len = dataAry.Len(); + Mustache_doc_itm[] rv = new Mustache_doc_itm[subs_len]; + for (int i = 0; i < subs_len; i++) { + Json_itm sub = dataAry.Get_at(i); + if (sub.Tid() == Json_itm_.Tid__nde) { + rv[i] = new JsonMustacheNde((Json_nde)sub); + } + else { + rv[i] = new JsonMustacheVal(false, Mustache_tkn_def.ItemString, sub.Data()); + } + } + return rv; + } +} +class JsonMustacheVal implements Mustache_doc_itm { + private final boolean fromArray; + private final String jsonKey; + private final Object jsonVal; + public JsonMustacheVal(boolean fromArray, String jsonKey, Object jsonVal) { + this.fromArray = fromArray; + this.jsonKey = jsonKey; + this.jsonVal = jsonVal; + } + public boolean Mustache__write(String mustacheKey, Mustache_bfr bfr) { + if ( (String_.Eq(mustacheKey, jsonKey)) // print if `{{match}}`; EX: `{{#prop}}{{prop}}{{/prop}}` + || (String_.Eq(mustacheKey, Mustache_tkn_def.ItemString) && fromArray)) { // print if `{{.}}` and from array; EX: `{{#array}}{{.}}{{/array}}` + bfr.Add_bry(Bry_.new_u8(Object_.To_str(jsonVal))); + return true; + } + else { + return false; + } + } + public Mustache_doc_itm[] Mustache__subs(String key) {return Mustache_doc_itm_.Ary__empty;} +} diff --git a/400_xowa/src/gplx/langs/mustaches/Mustache_doc_itm.java b/400_xowa/src/gplx/langs/mustaches/Mustache_doc_itm.java index c2896e14c..1854313fa 100644 --- a/400_xowa/src/gplx/langs/mustaches/Mustache_doc_itm.java +++ b/400_xowa/src/gplx/langs/mustaches/Mustache_doc_itm.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,9 +13,9 @@ 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.langs.mustaches; import gplx.*; import gplx.langs.*; -import gplx.langs.jsons.*; -public interface Mustache_doc_itm { - boolean Mustache__write(String key, Mustache_bfr bfr); - Mustache_doc_itm[] Mustache__subs(String key); -} +package gplx.langs.mustaches; + +public interface Mustache_doc_itm { + boolean Mustache__write(String key, Mustache_bfr bfr); + Mustache_doc_itm[] Mustache__subs(String key); +} diff --git a/400_xowa/src/gplx/langs/mustaches/Mustache_itm_render_tst.java b/400_xowa/src/gplx/langs/mustaches/Mustache_itm_render_tst.java index 3119a132e..166437f75 100644 --- a/400_xowa/src/gplx/langs/mustaches/Mustache_itm_render_tst.java +++ b/400_xowa/src/gplx/langs/mustaches/Mustache_itm_render_tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,150 +13,177 @@ 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.langs.mustaches; import gplx.*; import gplx.langs.*; -import org.junit.*; import gplx.core.primitives.*; -public class Mustache_itm_render_tst { - private final Mustache_itm_render_fxt fxt = new Mustache_itm_render_fxt(); - @Test public void Text() { - fxt.Test__parse("a b c", "a b c"); - } - @Test public void Variable() { - fxt.Init__root(fxt.Make_mock(0).Add_prop("prop1", "1").Add_prop("prop2", "2")); - fxt.Test__parse("{{prop1}}", "1"); - fxt.Test__parse("a{{prop1}}b{{prop2}}c", "a1b2c"); - } - @Test public void Escape() { - fxt.Init__root(fxt.Make_mock(0).Add_prop("prop1", "<")); - fxt.Test__parse("{{{prop1}}}", "<"); - fxt.Test__parse("{{prop1}}", "<"); - } - @Test public void Section_bool() { - fxt.Init__root(fxt.Make_mock(0).Add_bool_y("bool_y").Add_bool_n("bool_n")); - fxt.Test__parse("a{{#bool_y}}b{{/bool_y}}c", "abc"); - fxt.Test__parse("a{{#bool_n}}b{{/bool_n}}c", "ac"); - fxt.Test__parse("a{{#bool_y}}b{{/bool_y}}c{{#bool_n}}d{{/bool_n}}e", "abce"); - } - @Test public void Section_not() { - fxt.Init__root(fxt.Make_mock(0).Add_bool_y("bool_y").Add_bool_n("bool_n")); - fxt.Test__parse("a{{^bool_y}}b{{/bool_y}}c", "ac"); - fxt.Test__parse("a{{^bool_n}}b{{/bool_n}}c", "abc"); - fxt.Test__parse("a{{^bool_y}}b{{/bool_y}}c{{^bool_n}}d{{/bool_n}}e", "acde"); - } - @Test public void Section_ws() { - fxt.Init__root(fxt.Make_mock(0).Add_bool_y("bool_y")); - fxt.Test__parse("a\n {{#bool_y}} \nb\n {{/bool_y}} \nc", "a\nb\nc"); - } - @Test public void Section_subs_flat() { - fxt.Init__root(fxt.Make_mock(0).Add_subs("subs1" - , fxt.Make_mock(1).Add_prop("prop1", "1").Add_subs("subs2") - , fxt.Make_mock(2).Add_prop("prop1", "2").Add_subs("subs2") - )); - fxt.Test__parse("a{{#subs1}}({{prop1}}){{/subs1}}d", "a(1)(2)d"); - } - @Test public void Section_subs_nest_1() { - fxt.Init__root - ( fxt.Make_mock(0).Add_subs("subs1" - , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" - , fxt.Make_mock(11).Add_prop("prop2", "1") - , fxt.Make_mock(12).Add_prop("prop2", "2")) - )); - fxt.Test__parse - ( "{{#subs1}}{{prop1}}{{#subs2}}{{prop2}}{{/subs2}}{{/subs1}}" - , "a12" - ); - } - @Test public void Section_subs_nest_2() { - fxt.Init__root - ( fxt.Make_mock(0).Add_subs("subs1" - , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" - , fxt.Make_mock(11).Add_prop("prop2", "1") - , fxt.Make_mock(12).Add_prop("prop2", "2") - ) - , fxt.Make_mock(2).Add_prop("prop1", "b") - ) - ); - fxt.Test__parse - ( "{{#subs1}}{{prop1}}{{#subs2}}{{prop2}}{{/subs2}}{{/subs1}}" - , "a12b" - ); - } - @Test public void Section_subs_nest_3() { - fxt.Init__root - ( fxt.Make_mock(0).Add_subs("subs1" - , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" - , fxt.Make_mock(11).Add_prop("prop2", "1") - , fxt.Make_mock(12).Add_prop("prop2", "2") - ) - , fxt.Make_mock(2).Add_prop("prop1", "b").Add_subs("subs2" - , fxt.Make_mock(21).Add_prop("prop2", "3") - , fxt.Make_mock(22).Add_prop("prop2", "4") - ) - ) - ); - fxt.Test__parse - ( "{{#subs1}}{{prop1}}{{#subs2}}{{prop2}}{{/subs2}}{{prop1}}{{/subs1}}" - , "a12ab34b" - ); - } - @Test public void Section_bool_subs() { // handle prop written after boolean; should not pick up inner prop - fxt.Init__root - ( fxt.Make_mock(0).Add_bool_y("bool1").Add_prop("prop2", "2").Add_subs("subs1" - , fxt.Make_mock(1).Add_prop("prop1", "11") - , fxt.Make_mock(2).Add_prop("prop1", "12") - )); - fxt.Test__parse - ( "a{{#bool1}}b{{#subs1}}c{{prop1}}d{{/subs1}}e{{/bool1}}f{{prop2}}g" - , "abc11dc12def2g" - ); - } - @Test public void Section_owner() { - fxt.Init__root - ( fxt.Make_mock(0).Add_subs("subs1" - , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" - , fxt.Make_mock(11).Add_prop("prop2", "1") - ) - )); - fxt.Test__parse - ( "{{#subs1}}{{#subs2}}{{prop1}}{{prop2}}{{/subs2}}{{/subs1}}" // prop1 is cited in subs2, but value belongs to subs1 - , "a1" - ); - } -} -class Mustache_itm_render_fxt { - private final Mustache_tkn_parser parser = new Mustache_tkn_parser(); - private final Mustache_render_ctx ctx = new Mustache_render_ctx(); - private final Mustache_bfr bfr = Mustache_bfr.New(); - private Mustache_doc_itm__mock root; - public Mustache_doc_itm__mock Make_mock(int id) {return new Mustache_doc_itm__mock(id);} - public void Init__root(Mustache_doc_itm__mock v) {this.root = v;} - public void Test__parse(String src_str, String expd) { - byte[] src_bry = Bry_.new_a7(src_str); - Mustache_tkn_itm actl_itm = parser.Parse(src_bry, 0, src_bry.length); - ctx.Init(root); - actl_itm.Render(bfr, ctx); - Tfds.Eq_str_lines(expd, bfr.To_str_and_clear()); - } -} -class Mustache_doc_itm__mock implements Mustache_doc_itm { - private final Hash_adp hash_prop = Hash_adp_.New(), hash_bool = Hash_adp_.New(), hash_subs = Hash_adp_.New(); - public Mustache_doc_itm__mock(int id) {this.id = id;} - public int id; - public Mustache_doc_itm__mock Add_prop(String key, String val) {hash_prop.Add(key, Bry_.new_u8(val)); return this;} - public Mustache_doc_itm__mock Add_bool_y(String key) {hash_bool.Add(key, Bool_obj_ref.y_()); return this;} - public Mustache_doc_itm__mock Add_bool_n(String key) {hash_bool.Add(key, Bool_obj_ref.n_()); return this;} - public Mustache_doc_itm__mock Add_subs(String key, Mustache_doc_itm__mock... ary) {hash_subs.Add(key, ary); return this;} - public boolean Mustache__write(String key, Mustache_bfr bfr) { - byte[] rv = (byte[])hash_prop.Get_by(key); - if (rv == null) return false; - bfr.Add_bry(rv); - return true; - } - public Mustache_doc_itm[] Mustache__subs(String key) { - Object rv = hash_bool.Get_by(key); - if (rv != null) { - boolean bool_val = ((Bool_obj_ref)rv).Val(); - return bool_val ? Mustache_doc_itm_.Ary__bool__y : Mustache_doc_itm_.Ary__bool__n; - } - return (Mustache_doc_itm__mock[])hash_subs.Get_by(key); - } -} +package gplx.langs.mustaches; + +import gplx.Bry_; +import gplx.Hash_adp; +import gplx.Hash_adp_; +import gplx.Tfds; +import gplx.core.primitives.Bool_obj_ref; +import gplx.langs.jsons.Json_doc; +import gplx.langs.jsons.Json_nde; +import gplx.langs.jsons.Json_parser; +import org.junit.Test; + +public class Mustache_itm_render_tst { + private final Mustache_itm_render_fxt fxt = new Mustache_itm_render_fxt(); + @Test public void Text() { + fxt.Test__parse("a b c", "a b c"); + } + @Test public void Variable() { + fxt.Init__root(fxt.Make_mock(0).Add_prop("prop1", "1").Add_prop("prop2", "2")); + fxt.Test__parse("{{prop1}}", "1"); + fxt.Test__parse("a{{prop1}}b{{prop2}}c", "a1b2c"); + } + @Test public void Escape() { + fxt.Init__root(fxt.Make_mock(0).Add_prop("prop1", "<")); + fxt.Test__parse("{{{prop1}}}", "<"); + fxt.Test__parse("{{prop1}}", "<"); + } + @Test public void Section_bool() { + fxt.Init__root(fxt.Make_mock(0).Add_bool_y("bool_y").Add_bool_n("bool_n")); + fxt.Test__parse("a{{#bool_y}}b{{/bool_y}}c", "abc"); + fxt.Test__parse("a{{#bool_n}}b{{/bool_n}}c", "ac"); + fxt.Test__parse("a{{#bool_y}}b{{/bool_y}}c{{#bool_n}}d{{/bool_n}}e", "abce"); + } + @Test public void Section_not() { + fxt.Init__root(fxt.Make_mock(0).Add_bool_y("bool_y").Add_bool_n("bool_n")); + fxt.Test__parse("a{{^bool_y}}b{{/bool_y}}c", "ac"); + fxt.Test__parse("a{{^bool_n}}b{{/bool_n}}c", "abc"); + fxt.Test__parse("a{{^bool_y}}b{{/bool_y}}c{{^bool_n}}d{{/bool_n}}e", "acde"); + } + @Test public void Section_ws() { + fxt.Init__root(fxt.Make_mock(0).Add_bool_y("bool_y")); + fxt.Test__parse("a\n {{#bool_y}} \nb\n {{/bool_y}} \nc", "a\nb\nc"); + } + @Test public void Section_subs_flat() { + fxt.Init__root(fxt.Make_mock(0).Add_subs("subs1" + , fxt.Make_mock(1).Add_prop("prop1", "1").Add_subs("subs2") + , fxt.Make_mock(2).Add_prop("prop1", "2").Add_subs("subs2") + )); + fxt.Test__parse("a{{#subs1}}({{prop1}}){{/subs1}}d", "a(1)(2)d"); + } + @Test public void Section_subs_nest_1() { + fxt.Init__root + ( fxt.Make_mock(0).Add_subs("subs1" + , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" + , fxt.Make_mock(11).Add_prop("prop2", "1") + , fxt.Make_mock(12).Add_prop("prop2", "2")) + )); + fxt.Test__parse + ( "{{#subs1}}{{prop1}}{{#subs2}}{{prop2}}{{/subs2}}{{/subs1}}" + , "a12" + ); + } + @Test public void Section_subs_nest_2() { + fxt.Init__root + ( fxt.Make_mock(0).Add_subs("subs1" + , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" + , fxt.Make_mock(11).Add_prop("prop2", "1") + , fxt.Make_mock(12).Add_prop("prop2", "2") + ) + , fxt.Make_mock(2).Add_prop("prop1", "b") + ) + ); + fxt.Test__parse + ( "{{#subs1}}{{prop1}}{{#subs2}}{{prop2}}{{/subs2}}{{/subs1}}" + , "a12b" + ); + } + @Test public void Section_subs_nest_3() { + fxt.Init__root + ( fxt.Make_mock(0).Add_subs("subs1" + , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" + , fxt.Make_mock(11).Add_prop("prop2", "1") + , fxt.Make_mock(12).Add_prop("prop2", "2") + ) + , fxt.Make_mock(2).Add_prop("prop1", "b").Add_subs("subs2" + , fxt.Make_mock(21).Add_prop("prop2", "3") + , fxt.Make_mock(22).Add_prop("prop2", "4") + ) + ) + ); + fxt.Test__parse + ( "{{#subs1}}{{prop1}}{{#subs2}}{{prop2}}{{/subs2}}{{prop1}}{{/subs1}}" + , "a12ab34b" + ); + } + @Test public void Section_bool_subs() { // handle prop written after boolean; should not pick up inner prop + fxt.Init__root + ( fxt.Make_mock(0).Add_bool_y("bool1").Add_prop("prop2", "2").Add_subs("subs1" + , fxt.Make_mock(1).Add_prop("prop1", "11") + , fxt.Make_mock(2).Add_prop("prop1", "12") + )); + fxt.Test__parse + ( "a{{#bool1}}b{{#subs1}}c{{prop1}}d{{/subs1}}e{{/bool1}}f{{prop2}}g" + , "abc11dc12def2g" + ); + } + @Test public void Section_owner() { + fxt.Init__root + ( fxt.Make_mock(0).Add_subs("subs1" + , fxt.Make_mock(1).Add_prop("prop1", "a").Add_subs("subs2" + , fxt.Make_mock(11).Add_prop("prop2", "1") + ) + )); + fxt.Test__parse + ( "{{#subs1}}{{#subs2}}{{prop1}}{{prop2}}{{/subs2}}{{/subs1}}" // prop1 is cited in subs2, but value belongs to subs1 + , "a1" + ); + } + @Test public void Dot() { + fxt.Test__parse + ( "{'subs':['a', 'b', 'c', 'd']}" + , "{{#subs}}{{.}},{{/subs}}" + , "a,b,c,d," + ); + } +} +class Mustache_itm_render_fxt { + private final Mustache_tkn_parser parser = new Mustache_tkn_parser(); + private final Mustache_render_ctx ctx = new Mustache_render_ctx(); + private final Mustache_bfr bfr = Mustache_bfr.New(); + private Mustache_doc_itm__mock root; + public Mustache_doc_itm__mock Make_mock(int id) {return new Mustache_doc_itm__mock(id);} + public void Init__root(Mustache_doc_itm__mock v) {this.root = v;} + public void Test__parse(String src_str, String expd) { + byte[] src_bry = Bry_.new_a7(src_str); + Mustache_tkn_itm actl_itm = parser.Parse(src_bry, 0, src_bry.length); + ctx.Init(root); + actl_itm.Render(bfr, ctx); + Tfds.Eq_str_lines(expd, bfr.To_str_and_clear()); + } + public void Test__parse(String jdoc, String src_str, String expd) { + Json_nde jnde = Json_parser.ParseToJdoc(Json_doc.Make_str_by_apos(jdoc)).Root_nde(); + JsonMustacheNde nde = new JsonMustacheNde(jnde); + + byte[] src_bry = Bry_.new_a7(src_str); + Mustache_tkn_itm actl_itm = parser.Parse(src_bry, 0, src_bry.length); + ctx.Init(nde); + actl_itm.Render(bfr, ctx); + Tfds.Eq_str_lines(expd, bfr.To_str_and_clear()); + } +} +class Mustache_doc_itm__mock implements Mustache_doc_itm { + private final Hash_adp hash_prop = Hash_adp_.New(), hash_bool = Hash_adp_.New(), hash_subs = Hash_adp_.New(); + public Mustache_doc_itm__mock(int id) {this.id = id;} + public int id; + public Mustache_doc_itm__mock Add_prop(String key, String val) {hash_prop.Add(key, Bry_.new_u8(val)); return this;} + public Mustache_doc_itm__mock Add_bool_y(String key) {hash_bool.Add(key, Bool_obj_ref.y_()); return this;} + public Mustache_doc_itm__mock Add_bool_n(String key) {hash_bool.Add(key, Bool_obj_ref.n_()); return this;} + public Mustache_doc_itm__mock Add_subs(String key, Mustache_doc_itm__mock... ary) {hash_subs.Add(key, ary); return this;} + public boolean Mustache__write(String key, Mustache_bfr bfr) { + byte[] rv = (byte[])hash_prop.Get_by(key); + if (rv == null) return false; + bfr.Add_bry(rv); + return true; + } + public Mustache_doc_itm[] Mustache__subs(String key) { + Object rv = hash_bool.Get_by(key); + if (rv != null) { + boolean bool_val = ((Bool_obj_ref)rv).Val(); + return bool_val ? Mustache_doc_itm_.Ary__bool__y : Mustache_doc_itm_.Ary__bool__n; + } + return (Mustache_doc_itm__mock[])hash_subs.Get_by(key); + } +} diff --git a/400_xowa/src/gplx/langs/mustaches/Mustache_render_ctx.java b/400_xowa/src/gplx/langs/mustaches/Mustache_render_ctx.java index 0986b8bd3..c93206489 100644 --- a/400_xowa/src/gplx/langs/mustaches/Mustache_render_ctx.java +++ b/400_xowa/src/gplx/langs/mustaches/Mustache_render_ctx.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,86 +13,100 @@ 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.langs.mustaches; import gplx.*; import gplx.langs.*; -public class Mustache_render_ctx { - private final List_adp stack = List_adp_.New(); - private Mustache_doc_itm cur; - private Mustache_doc_itm[] subs; private int subs_idx, subs_len; private byte cur_is_bool; - public Mustache_render_ctx Init(Mustache_doc_itm cur) { - this.cur = cur; - this.subs = null; - this.subs_idx = subs_len = 0; this.cur_is_bool = Bool_.__byte; - return this; - } - public boolean Render_variable(Mustache_bfr bfr, String key) { - boolean rv = false; - int stack_pos = stack.Len(); - Mustache_doc_itm itm = cur; - while (itm != Mustache_doc_itm_.Null_itm) { - boolean resolved = itm.Mustache__write(key, bfr); - if (resolved) { - rv = true; - break; - } - else { - --stack_pos; - if (stack_pos == -1) // nothing else in stack - break; - else - itm = ((Mustache_stack_itm)stack.Get_at(stack_pos)).cur; - } - } - return rv; - } - public void Section_bgn(String key) { - Mustache_stack_itm stack_itm = new Mustache_stack_itm(cur, subs, subs_idx, subs_len, cur_is_bool); // note that cur is "owner" since subs_idx == 0 - stack.Add(stack_itm); - subs = cur.Mustache__subs(key); if (subs == null) subs = Mustache_doc_itm_.Ary__empty; // subs == null if property does not exist; EX: "folder{{#files}}file{{/files}}" and folder = new Folder(File[0]); - subs_len = subs.length; - subs_idx = -1; - } - public boolean Section_do(boolean inverted) { - if (++subs_idx >= subs_len) return false; - Mustache_doc_itm sub = subs[subs_idx]; - if (subs_idx == 0) { // special logic to handle 1st item; note that there always be at least one item - if (sub == Mustache_doc_itm_.Itm__bool__n) { - boolean rv = Bool_.N; - if (inverted) rv = !rv; - cur_is_bool = Bool_.To_byte(rv); - return rv; - } - else if (sub == Mustache_doc_itm_.Itm__bool__y) { - boolean rv = Bool_.Y; - if (inverted) rv = !rv; - cur_is_bool = Bool_.To_byte(rv); - return rv; - } - else - cur_is_bool = Bool_.__byte; - } - cur = sub; - return true; - } - public void Section_end() { - Mustache_stack_itm itm = (Mustache_stack_itm)List_adp_.Pop(stack); - subs = itm.subs; - subs_len = itm.subs_len; - subs_idx = itm.subs_idx; - cur = itm.cur; - cur_is_bool = itm.cur_is_bool; - } -} -class Mustache_stack_itm { - public Mustache_stack_itm(Mustache_doc_itm cur, Mustache_doc_itm[] subs, int subs_idx, int subs_len, byte cur_is_bool) { - this.cur = cur; - this.cur_is_bool = cur_is_bool; - this.subs = subs; - this.subs_idx = subs_idx; - this.subs_len = subs_len; - } - public final Mustache_doc_itm cur; - public final byte cur_is_bool; - public final Mustache_doc_itm[] subs; - public final int subs_idx; - public final int subs_len; -} +package gplx.langs.mustaches; + +import gplx.Bool_; +import gplx.List_adp; +import gplx.List_adp_; + +public class Mustache_render_ctx { + private final List_adp stack = List_adp_.New(); + private Mustache_doc_itm cur; + private Mustache_doc_itm[] subs; + private int subs_idx, subs_len; + private byte cur_is_bool; + + public Mustache_render_ctx Init(Mustache_doc_itm cur) { + this.cur = cur; + this.subs = null; + this.subs_idx = subs_len = 0; + this.cur_is_bool = Bool_.__byte; + return this; + } + public boolean Render_variable(Mustache_bfr bfr, String key) { + boolean rv = false; + int stack_pos = stack.Len(); + Mustache_doc_itm itm = cur; + while (itm != Mustache_doc_itm_.Null_itm) { + boolean resolved = itm.Mustache__write(key, bfr); + // current itm handles key -> exit + if (resolved) { + rv = true; + break; + } + // current itm does not handle key -> go up stack + else { + --stack_pos; + if (stack_pos == -1) // nothing else in stack + break; + else + itm = ((Mustache_stack_itm)stack.Get_at(stack_pos)).cur; + } + } + return rv; + } + public void Section_bgn(String key) { + Mustache_stack_itm stack_itm = new Mustache_stack_itm(cur, subs, subs_idx, subs_len, cur_is_bool); // note that cur is "owner" since subs_idx == 0 + stack.Add(stack_itm); + subs = cur.Mustache__subs(key); + if (subs == null) // subs == null if property does not exist; EX: "folder{{#files}}file{{/files}}" and folder = new Folder(File[0]); + subs = Mustache_doc_itm_.Ary__empty; + subs_len = subs.length; + subs_idx = -1; + } + public boolean Section_do(boolean inverted) { + if (++subs_idx >= subs_len) + return false; + Mustache_doc_itm sub = subs[subs_idx]; + if (subs_idx == 0) { // special logic to handle 1st item; note that there always be at least one item + if (sub == Mustache_doc_itm_.Itm__bool__n) { + boolean rv = Bool_.N; + if (inverted) rv = !rv; + cur_is_bool = Bool_.To_byte(rv); + return rv; + } + else if (sub == Mustache_doc_itm_.Itm__bool__y) { + boolean rv = Bool_.Y; + if (inverted) rv = !rv; + cur_is_bool = Bool_.To_byte(rv); + return rv; + } + else + cur_is_bool = Bool_.__byte; + } + cur = sub; + return true; + } + public void Section_end() { + Mustache_stack_itm itm = (Mustache_stack_itm)List_adp_.Pop(stack); + subs = itm.subs; + subs_len = itm.subs_len; + subs_idx = itm.subs_idx; + cur = itm.cur; + cur_is_bool = itm.cur_is_bool; + } +} +class Mustache_stack_itm { + public Mustache_stack_itm(Mustache_doc_itm cur, Mustache_doc_itm[] subs, int subs_idx, int subs_len, byte cur_is_bool) { + this.cur = cur; + this.cur_is_bool = cur_is_bool; + this.subs = subs; + this.subs_idx = subs_idx; + this.subs_len = subs_len; + } + public final Mustache_doc_itm cur; + public final byte cur_is_bool; + public final Mustache_doc_itm[] subs; + public final int subs_idx; + public final int subs_len; +} diff --git a/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_def.java b/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_def.java index 9768c936f..8bb8b1b82 100644 --- a/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_def.java +++ b/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_def.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,30 +13,40 @@ 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.langs.mustaches; import gplx.*; import gplx.langs.*; -class Mustache_tkn_def { - public byte[] Variable_lhs = Dflt_variable_lhs; - public byte[] Variable_rhs = Dflt_variable_rhs; - public int Variable_lhs_len; - public int Variable_rhs_len; - public static final byte[] - Dflt_variable_lhs = Bry_.new_a7("{{") - , Dflt_variable_rhs = Bry_.new_a7("}}") - ; - public static final byte - Variable = Byte_ascii.Curly_end // {{=<% %>=}} - , Escape_bgn = Byte_ascii.Curly_bgn // {{{escape}}} - , Escape_end = Byte_ascii.Curly_end // {{{escape}}} - , Section = Byte_ascii.Hash // {{#section}} - , Grp_end = Byte_ascii.Slash // {{/section}} - , Inverted = Byte_ascii.Pow // {{^inverted}} - , Comment = Byte_ascii.Bang // {{!comment}} - , Partial = Byte_ascii.Angle_bgn // {{>partial}} - , Delimiter_bgn = Byte_ascii.Eq // {{=<% %>=}} - , Delimiter_end = Byte_ascii.Curly_end // {{=<% %>=}} - ; - public Mustache_tkn_def() { - Variable_lhs_len = Variable_lhs.length; - Variable_rhs_len = Variable_rhs.length; - } -} +package gplx.langs.mustaches; + +import gplx.Bry_; +import gplx.Byte_; +import gplx.Byte_ascii; +import gplx.String_; + +class Mustache_tkn_def { + public byte[] Variable_lhs = Dflt_variable_lhs; + public byte[] Variable_rhs = Dflt_variable_rhs; + public int Variable_lhs_len; + public int Variable_rhs_len; + public static final byte[] + Dflt_variable_lhs = Bry_.new_a7("{{") + , Dflt_variable_rhs = Bry_.new_a7("}}") + ; + public static final byte + Variable = Byte_ascii.Curly_end // {{=<% %>=}} + , Escape_bgn = Byte_ascii.Curly_bgn // {{{escape}}} + , Escape_end = Byte_ascii.Curly_end // {{{escape}}} + , Section = Byte_ascii.Hash // {{#section}} + , Grp_end = Byte_ascii.Slash // {{/section}} + , Inverted = Byte_ascii.Pow // {{^inverted}} + , Comment = Byte_ascii.Bang // {{!comment}} + , Partial = Byte_ascii.Angle_end // {{>partial}} + , Delimiter_bgn = Byte_ascii.Eq // {{=<% %>=}} + , Delimiter_end = Byte_ascii.Curly_end // {{=<% %>=}} + , Item = Byte_ascii.Dot // {{.}} + ; + public static final String + ItemString = String_.new_u8(Byte_.To_bry(Item)) + ; + public Mustache_tkn_def() { + Variable_lhs_len = Variable_lhs.length; + Variable_rhs_len = Variable_rhs.length; + } +} diff --git a/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_itm.java b/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_itm.java index 3719bbe2a..762f145b2 100644 --- a/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_itm.java +++ b/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_itm.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,98 +13,114 @@ 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.langs.mustaches; import gplx.*; import gplx.langs.*; -public interface Mustache_tkn_itm { - int Tid(); - String Key(); - Mustache_tkn_itm[] Subs_ary(); - void Subs_ary_(Mustache_tkn_itm[] v); - void Render(Mustache_bfr bfr, Mustache_render_ctx ctx); -} -class Mustache_tkn_itm_ {// for types, see http://mustache.github.io/mustache.5.html - public static final int Tid__root = 0, Tid__text = 1, Tid__variable = 2, Tid__escape = 3, Tid__section = 4, Tid__inverted = 5, Tid__comment = 6, Tid__partial = 7, Tid__delimiter = 8; - public static final Mustache_tkn_itm[] Ary_empty = new Mustache_tkn_itm[0]; -} -abstract class Mustache_tkn_base implements Mustache_tkn_itm { - public Mustache_tkn_base(int tid, byte[] key_bry) {this.tid = tid; this.key = String_.new_u8(key_bry);} - public int Tid() {return tid;} private final int tid; - public String Key() {return key;} private final String key; - @gplx.Virtual public Mustache_tkn_itm[] Subs_ary() {return Mustache_tkn_itm_.Ary_empty;} - @gplx.Virtual public void Subs_ary_(Mustache_tkn_itm[] v) {throw Err_.new_unsupported();} // fail if trying to set and not overridden - @gplx.Virtual public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {throw Err_.new_unsupported();} // should be abstract -} -class Mustache_tkn_root extends Mustache_tkn_base { // EX: {{variable}} -> <a> - private Mustache_tkn_itm[] subs_ary; - public Mustache_tkn_root() {super(Mustache_tkn_itm_.Tid__root, Bry_.Empty);} - @Override public Mustache_tkn_itm[] Subs_ary() {return subs_ary;} - @Override public void Subs_ary_(Mustache_tkn_itm[] v) {subs_ary = v;} - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { - int subs_len = subs_ary.length; - for (int i = 0; i < subs_len; ++i) { - Mustache_tkn_itm sub = subs_ary[i]; - sub.Render(bfr, ctx); - } - } -} -class Mustache_tkn_text extends Mustache_tkn_base { // EX: text -> text - private final byte[] src; private final int src_bgn, src_end; - public Mustache_tkn_text(byte[] src, int src_bgn, int src_end) {super(Mustache_tkn_itm_.Tid__text, Bry_.Empty); - this.src = src; - this.src_bgn = src_bgn; - this.src_end = src_end; - } - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { - bfr.Add_mid(src, src_bgn, src_end); - } -} -class Mustache_tkn_comment extends Mustache_tkn_base { // EX: {{!section}}comment{{/section}} -> - public Mustache_tkn_comment() {super(Mustache_tkn_itm_.Tid__comment, Bry_.Empty);} - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {} -} -class Mustache_tkn_variable extends Mustache_tkn_base { // EX: {{variable}} -> <a> - public Mustache_tkn_variable(byte[] key) {super(Mustache_tkn_itm_.Tid__variable, key);} - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { - String key = this.Key(); - ctx.Render_variable(bfr.Escape_(Bool_.Y), key); - } -} -class Mustache_tkn_escape extends Mustache_tkn_base { // EX: {{{variable}}} -> - public Mustache_tkn_escape(byte[] key) {super(Mustache_tkn_itm_.Tid__escape, key);} - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { - String key = this.Key(); - ctx.Render_variable(bfr.Escape_(Bool_.N), key); - } -} -class Mustache_tkn_section extends Mustache_tkn_base { // EX: {{#section}}val{{/section}} -> val (if boolean) or valvalval (if list) - private Mustache_tkn_itm[] subs_ary; - public Mustache_tkn_section(byte[] key) {super(Mustache_tkn_itm_.Tid__section, key);} - @Override public Mustache_tkn_itm[] Subs_ary() {return subs_ary;} - @Override public void Subs_ary_(Mustache_tkn_itm[] v) {subs_ary = v;} - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {Render_static(Bool_.N, this, bfr, ctx);} - public static void Render_static(boolean inverted, Mustache_tkn_base tkn, Mustache_bfr bfr, Mustache_render_ctx ctx) { - String key = tkn.Key(); - Mustache_tkn_itm[] subs_ary = tkn.Subs_ary(); - ctx.Section_bgn(key); - while (ctx.Section_do(inverted)) { - int subs_len = subs_ary.length; - for (int i = 0; i < subs_len; ++i) { - Mustache_tkn_itm sub = subs_ary[i]; - sub.Render(bfr, ctx); - } - } - ctx.Section_end(); - } -} -class Mustache_tkn_inverted extends Mustache_tkn_base { // EX: {{^section}}missing{{/section}} -> missing - private Mustache_tkn_itm[] subs_ary; - public Mustache_tkn_inverted(byte[] key) {super(Mustache_tkn_itm_.Tid__inverted, key);} - @Override public Mustache_tkn_itm[] Subs_ary() {return subs_ary;} - @Override public void Subs_ary_(Mustache_tkn_itm[] v) {subs_ary = v;} - @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {Mustache_tkn_section.Render_static(Bool_.Y, this, bfr, ctx);} -} -class Mustache_tkn_partial extends Mustache_tkn_base { // EX: {{>a}} -> abc (deferred eval) - public Mustache_tkn_partial(byte[] key) {super(Mustache_tkn_itm_.Tid__partial, key);} -} -class Mustache_tkn_delimiter extends Mustache_tkn_base {// EX: {{=<% %>=}} -> <% variable %> - public Mustache_tkn_delimiter(byte[] key) {super(Mustache_tkn_itm_.Tid__delimiter, key);} -} +package gplx.langs.mustaches; + +import gplx.Bool_; +import gplx.Bry_; +import gplx.Byte_ascii; +import gplx.Err_; +import gplx.Io_url; +import gplx.String_; + +public interface Mustache_tkn_itm { + int Tid(); + String Key(); + Mustache_tkn_itm[] Subs_ary(); + void Subs_ary_(Mustache_tkn_itm[] v); + void Render(Mustache_bfr bfr, Mustache_render_ctx ctx); +} +class Mustache_tkn_itm_ {// for types, see http://mustache.github.io/mustache.5.html + public static final int Tid__root = 0, Tid__text = 1, Tid__variable = 2, Tid__escape = 3, Tid__section = 4, Tid__inverted = 5, Tid__comment = 6, Tid__partial = 7, Tid__delimiter = 8; + public static final Mustache_tkn_itm[] Ary_empty = new Mustache_tkn_itm[0]; +} +abstract class Mustache_tkn_base implements Mustache_tkn_itm { + public Mustache_tkn_base(int tid, byte[] key_bry) {this.tid = tid; this.key = String_.new_u8(key_bry);} + public int Tid() {return tid;} private final int tid; + public String Key() {return key;} private final String key; + @gplx.Virtual public Mustache_tkn_itm[] Subs_ary() {return Mustache_tkn_itm_.Ary_empty;} + @gplx.Virtual public void Subs_ary_(Mustache_tkn_itm[] v) {throw Err_.new_unsupported();} // fail if trying to set and not overridden + @gplx.Virtual public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {throw Err_.new_unsupported();} // should be abstract +} +class Mustache_tkn_root extends Mustache_tkn_base { // EX: {{variable}} -> <a> + private Mustache_tkn_itm[] subs_ary; + public Mustache_tkn_root() {super(Mustache_tkn_itm_.Tid__root, Bry_.Empty);} + @Override public Mustache_tkn_itm[] Subs_ary() {return subs_ary;} + @Override public void Subs_ary_(Mustache_tkn_itm[] v) {subs_ary = v;} + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { + int subs_len = subs_ary.length; + for (int i = 0; i < subs_len; ++i) { + Mustache_tkn_itm sub = subs_ary[i]; + sub.Render(bfr, ctx); + } + } +} +class Mustache_tkn_text extends Mustache_tkn_base { // EX: text -> text + private final byte[] src; private final int src_bgn, src_end; + public Mustache_tkn_text(byte[] src, int src_bgn, int src_end) {super(Mustache_tkn_itm_.Tid__text, Bry_.Empty); + this.src = src; + this.src_bgn = src_bgn; + this.src_end = src_end; + } + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { + bfr.Add_mid(src, src_bgn, src_end); + } +} +class Mustache_tkn_comment extends Mustache_tkn_base { // EX: {{!section}}comment{{/section}} -> + public Mustache_tkn_comment() {super(Mustache_tkn_itm_.Tid__comment, Bry_.Empty);} + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {} +} +class Mustache_tkn_variable extends Mustache_tkn_base { // EX: {{variable}} -> <a> + public Mustache_tkn_variable(byte[] key) {super(Mustache_tkn_itm_.Tid__variable, key);} + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { + String key = this.Key(); + ctx.Render_variable(bfr.Escape_(Bool_.Y), key); + } +} +class Mustache_tkn_escape extends Mustache_tkn_base { // EX: {{{variable}}} -> + public Mustache_tkn_escape(byte[] key) {super(Mustache_tkn_itm_.Tid__escape, key);} + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { + String key = this.Key(); + ctx.Render_variable(bfr.Escape_(Bool_.N), key); + } +} +class Mustache_tkn_section extends Mustache_tkn_base { // EX: {{#section}}val{{/section}} -> val (if boolean) or valvalval (if list) + private Mustache_tkn_itm[] subs_ary; + public Mustache_tkn_section(byte[] key) {super(Mustache_tkn_itm_.Tid__section, key);} + @Override public Mustache_tkn_itm[] Subs_ary() {return subs_ary;} + @Override public void Subs_ary_(Mustache_tkn_itm[] v) {subs_ary = v;} + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {Render_static(Bool_.N, this, bfr, ctx);} + public static void Render_static(boolean inverted, Mustache_tkn_base tkn, Mustache_bfr bfr, Mustache_render_ctx ctx) { + String key = tkn.Key(); + Mustache_tkn_itm[] subs_ary = tkn.Subs_ary(); + ctx.Section_bgn(key); + while (ctx.Section_do(inverted)) { + int subs_len = subs_ary.length; + for (int i = 0; i < subs_len; ++i) { + Mustache_tkn_itm sub = subs_ary[i]; + sub.Render(bfr, ctx); + } + } + ctx.Section_end(); + } +} +class Mustache_tkn_inverted extends Mustache_tkn_base { // EX: {{^section}}missing{{/section}} -> missing + private Mustache_tkn_itm[] subs_ary; + public Mustache_tkn_inverted(byte[] key) {super(Mustache_tkn_itm_.Tid__inverted, key);} + @Override public Mustache_tkn_itm[] Subs_ary() {return subs_ary;} + @Override public void Subs_ary_(Mustache_tkn_itm[] v) {subs_ary = v;} + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) {Mustache_tkn_section.Render_static(Bool_.Y, this, bfr, ctx);} +} +class Mustache_tkn_partial extends Mustache_tkn_base { // EX: {{>a}} -> abc (deferred eval) + private Mustache_tkn_itm template_root; + public Mustache_tkn_partial(byte[] key, Io_url dir) { + super(Mustache_tkn_itm_.Tid__partial, key); + Mustache_tkn_parser parser = new Mustache_tkn_parser(dir); + template_root = parser.Parse(String_.new_a7(Bry_.Trim_bgn(key, Byte_ascii.Space, 0))); + } + @Override public void Render(Mustache_bfr bfr, Mustache_render_ctx ctx) { + template_root.Render(bfr, ctx); + } +} +class Mustache_tkn_delimiter extends Mustache_tkn_base { // EX: {{=<% %>=}} -> <% variable %> + public Mustache_tkn_delimiter(byte[] key) {super(Mustache_tkn_itm_.Tid__delimiter, key);} +} diff --git a/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_parser.java b/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_parser.java index 6eb99c3d9..372ecc71a 100644 --- a/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_parser.java +++ b/400_xowa/src/gplx/langs/mustaches/Mustache_tkn_parser.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,140 +13,162 @@ 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.langs.mustaches; import gplx.*; import gplx.langs.*; -public class Mustache_tkn_parser { - private byte[] src; private int src_end; - private final Mustache_tkn_def tkn_def = new Mustache_tkn_def(); - public Mustache_tkn_itm Parse(byte[] src) {return Parse(src, 0, src.length);} - public Mustache_tkn_itm Parse(byte[] src, int src_bgn, int src_end) { - this.src = src; this.src_end = src_end; - Mustache_tkn_root root = new Mustache_tkn_root(); - Parse_grp(root, src_bgn); - return root; - } - private int Parse_grp(Mustache_tkn_itm owner, int src_bgn) { - List_adp subs_list = List_adp_.New(); - int txt_bgn = src_bgn; - boolean end_grp = false; - while (true) {// loop for "{{" - int lhs_bgn = Bry_find_.Find_fwd(src, tkn_def.Variable_lhs, txt_bgn, src_end); // next "{{" - if (lhs_bgn == Bry_find_.Not_found) { // no more "{{" - subs_list.Add(new Mustache_tkn_text(src, txt_bgn, src_end)); // add everything between prv "}}" and cur "{{" - break; - } - int lhs_end = lhs_bgn + tkn_def.Variable_lhs_len; - - Mustache_tkn_data tkn_data = new Mustache_tkn_data(src[lhs_end]); // preview tkn - lhs_end += tkn_data.lhs_end_adj; - - int rhs_bgn = Bry_find_.Find_fwd(src, tkn_def.Variable_rhs, lhs_end, src_end); // next "}}" - if (rhs_bgn == Bry_find_.Not_found) throw Fail(lhs_bgn, "unclosed tag"); // fail if no "}}" - int rhs_end = rhs_bgn + tkn_def.Variable_rhs_len; - if (tkn_data.rhs_bgn_chk != Byte_ascii.Null) { - if (src[rhs_bgn] != tkn_data.rhs_bgn_chk) throw Fail(lhs_end, "invalid check byte"); - ++rhs_end; // skip the chk_byte; note that bottom of function will skip "}}" by adding +2 - } - - - int txt_end = lhs_bgn; // get text tkn - if (tkn_data.ws_ignore) { - int new_txt_bgn = Trim_bwd_to_nl(src, txt_bgn, txt_end); - if (new_txt_bgn != -1) { - int new_txt_end = Trim_fwd_to_nl(src, rhs_end, src_end); - if (new_txt_end != -1) { - txt_end = new_txt_bgn; - rhs_end = new_txt_end == src_end ? src_end : new_txt_end + 1; - } - } - } - if (txt_end > txt_bgn) // ignore 0-byte text tkns; occurs when consecutive tkns; EX: {{v1}}{{v2}} will try to create text tkn between "}}{{" - subs_list.Add(new Mustache_tkn_text(src, txt_bgn, txt_end)); // add everything between prv "}}" and cur "{{" - - txt_bgn = Parse_itm(tkn_data, subs_list, lhs_end, rhs_bgn, rhs_end); // do parse - if (txt_bgn < 0) { // NOTE: txt_bgn < 0 means end grp - txt_bgn *= -1; - end_grp = true; - } - if (end_grp) break; - } - if (subs_list.Count() > 0) // don't create subs if no members - owner.Subs_ary_((Mustache_tkn_itm[])subs_list.To_ary_and_clear(Mustache_tkn_itm.class)); - return txt_bgn; - } - private int Parse_itm(Mustache_tkn_data tkn_data, List_adp subs_list, int lhs_end, int rhs_bgn, int rhs_end) { - byte[] val_bry = Bry_.Mid(src, lhs_end, rhs_bgn); - Mustache_tkn_base tkn = null; - switch (tkn_data.tid) { - default: throw Err_.new_unhandled(tkn_data.tid); - case Mustache_tkn_def.Variable: tkn = new Mustache_tkn_variable(val_bry); break; - case Mustache_tkn_def.Comment: tkn = new Mustache_tkn_comment(); break; - case Mustache_tkn_def.Partial: tkn = new Mustache_tkn_partial(val_bry); break; - case Mustache_tkn_def.Delimiter_bgn: tkn = new Mustache_tkn_delimiter(val_bry); break; // TODO_OLD: implement delimiter; EX: {{=<% %>=}} - case Mustache_tkn_def.Escape_bgn: tkn = new Mustache_tkn_escape(val_bry); break; - case Mustache_tkn_def.Section: tkn = new Mustache_tkn_section(val_bry); break; - case Mustache_tkn_def.Inverted: tkn = new Mustache_tkn_inverted(val_bry); break; - case Mustache_tkn_def.Grp_end: { - return -(rhs_end); // pop the stack - } - } - subs_list.Add(tkn); - if (tkn_data.parse_grp) { - return Parse_grp(tkn, rhs_end); - } - else - return rhs_end; - } - private Err Fail(int pos, String fmt, Object... args) { - return Err_.new_("mustache", fmt, "excerpt", Bry_.Mid_by_len_safe(src, pos, 32)); - } - private static int Trim_bwd_to_nl(byte[] src, int txt_bgn, int txt_end) { - int stop = txt_bgn - 1; - int pos = txt_end - 1; - while (pos > stop) { - byte b = src[pos]; - switch (b) { - case Byte_ascii.Tab: - case Byte_ascii.Space: --pos; break; - case Byte_ascii.Nl: return pos + 1; // 1 char after \n - default: return -1; - } - } - return -1; - } - private static int Trim_fwd_to_nl(byte[] src, int txt_bgn, int txt_end) { - int pos = txt_bgn; - while (pos < txt_end) { - byte b = src[pos]; - switch (b) { - case Byte_ascii.Tab: - case Byte_ascii.Space: ++pos; break; - case Byte_ascii.Nl: return pos; - default: return -1; - } - } - return -1; - } -} -class Mustache_tkn_data { - public int tid; - public int lhs_end_adj; - public byte rhs_bgn_chk; - public boolean parse_grp; - public boolean ws_ignore; - public Mustache_tkn_data(byte b) { - tid = b; - parse_grp = ws_ignore = false; - lhs_end_adj = 1; - rhs_bgn_chk = Byte_ascii.Null; - switch (b) { - default: lhs_end_adj = 0; tid = Mustache_tkn_def.Variable; break; - case Mustache_tkn_def.Comment: - case Mustache_tkn_def.Partial: - case Mustache_tkn_def.Grp_end: ws_ignore = true; break; - case Mustache_tkn_def.Delimiter_bgn: rhs_bgn_chk = Mustache_tkn_def.Delimiter_end; break; // check for "=}}"; TODO_OLD: implement delimiter; EX: {{=<% %>=}} - case Mustache_tkn_def.Escape_bgn: rhs_bgn_chk = Mustache_tkn_def.Escape_end; break; // check for "" - case Mustache_tkn_def.Section: - case Mustache_tkn_def.Inverted: ws_ignore = true; parse_grp = true; break; - } - } -} +package gplx.langs.mustaches; + +import gplx.Bry_; +import gplx.Bry_find_; +import gplx.Byte_ascii; +import gplx.Err; +import gplx.Err_; +import gplx.Io_mgr; +import gplx.Io_url; +import gplx.List_adp; +import gplx.List_adp_; + +public class Mustache_tkn_parser { + private byte[] src; private int src_end; + private Io_url template_root; + private final Mustache_tkn_def tkn_def = new Mustache_tkn_def(); + public Mustache_tkn_parser() { + } + public Mustache_tkn_parser(Io_url template_root) { + this.template_root = template_root; + } + public Mustache_tkn_itm Parse(String template) { return Parse(template, Bry_.Empty); } + public Mustache_tkn_itm Parse(String template, byte[] default_text) { + byte[] template_data = Io_mgr.Instance.LoadFilBryOr(template_root.GenSubFil_nest(template + ".mustache"), default_text); + return Parse(template_data, 0, template_data.length); + } + public Mustache_tkn_itm Parse(byte[] src) {return Parse(src, 0, src.length);} + public Mustache_tkn_itm Parse(byte[] src, int src_bgn, int src_end) { + this.src = src; this.src_end = src_end; + Mustache_tkn_root root = new Mustache_tkn_root(); + Parse_grp(root, src_bgn); + return root; + } + private int Parse_grp(Mustache_tkn_itm owner, int src_bgn) { + List_adp subs_list = List_adp_.New(); + int txt_bgn = src_bgn; + boolean end_grp = false; + while (true) {// loop for "{{" + int lhs_bgn = Bry_find_.Find_fwd(src, tkn_def.Variable_lhs, txt_bgn, src_end); // next "{{" + if (lhs_bgn == Bry_find_.Not_found) { // no more "{{" + subs_list.Add(new Mustache_tkn_text(src, txt_bgn, src_end)); // add everything between prv "}}" and cur "{{" + break; + } + int lhs_end = lhs_bgn + tkn_def.Variable_lhs_len; + + Mustache_tkn_data tkn_data = new Mustache_tkn_data(src[lhs_end]); // preview tkn + lhs_end += tkn_data.lhs_end_adj; + + int rhs_bgn = Bry_find_.Find_fwd(src, tkn_def.Variable_rhs, lhs_end, src_end); // next "}}" + if (rhs_bgn == Bry_find_.Not_found) throw Fail(lhs_bgn, "unclosed tag"); // fail if no "}}" + int rhs_end = rhs_bgn + tkn_def.Variable_rhs_len; + if (tkn_data.rhs_bgn_chk != Byte_ascii.Null) { + if (src[rhs_bgn] != tkn_data.rhs_bgn_chk) throw Fail(lhs_end, "invalid check byte"); + ++rhs_end; // skip the chk_byte; note that bottom of function will skip "}}" by adding +2 + } + + + int txt_end = lhs_bgn; // get text tkn + if (tkn_data.ws_ignore) { + int new_txt_bgn = Trim_bwd_to_nl(src, txt_bgn, txt_end); + if (new_txt_bgn != -1) { + int new_txt_end = Trim_fwd_to_nl(src, rhs_end, src_end); + if (new_txt_end != -1) { + txt_end = new_txt_bgn; + rhs_end = new_txt_end == src_end ? src_end : new_txt_end + 1; + } + } + } + if (txt_end > txt_bgn) // ignore 0-byte text tkns; occurs when consecutive tkns; EX: {{v1}}{{v2}} will try to create text tkn between "}}{{" + subs_list.Add(new Mustache_tkn_text(src, txt_bgn, txt_end)); // add everything between prv "}}" and cur "{{" + + txt_bgn = Parse_itm(tkn_data, subs_list, lhs_end, rhs_bgn, rhs_end); // do parse + if (txt_bgn < 0) { // NOTE: txt_bgn < 0 means end grp + txt_bgn *= -1; + end_grp = true; + } + if (end_grp) break; + } + if (subs_list.Count() > 0) // don't create subs if no members + owner.Subs_ary_((Mustache_tkn_itm[])subs_list.To_ary_and_clear(Mustache_tkn_itm.class)); + return txt_bgn; + } + private int Parse_itm(Mustache_tkn_data tkn_data, List_adp subs_list, int lhs_end, int rhs_bgn, int rhs_end) { + byte[] val_bry = Bry_.Mid(src, lhs_end, rhs_bgn); + Mustache_tkn_base tkn = null; + switch (tkn_data.tid) { + default: throw Err_.new_unhandled(tkn_data.tid); + case Mustache_tkn_def.Variable: tkn = new Mustache_tkn_variable(val_bry); break; + case Mustache_tkn_def.Comment: tkn = new Mustache_tkn_comment(); break; + case Mustache_tkn_def.Partial: tkn = new Mustache_tkn_partial(val_bry, template_root); break; + case Mustache_tkn_def.Delimiter_bgn: tkn = new Mustache_tkn_delimiter(val_bry); break; // TODO_OLD: implement delimiter; EX: {{=<% %>=}} + case Mustache_tkn_def.Escape_bgn: tkn = new Mustache_tkn_escape(val_bry); break; + case Mustache_tkn_def.Section: tkn = new Mustache_tkn_section(val_bry); break; + case Mustache_tkn_def.Inverted: tkn = new Mustache_tkn_inverted(val_bry); break; + case Mustache_tkn_def.Grp_end: { + return -(rhs_end); // pop the stack + } + } + subs_list.Add(tkn); + if (tkn_data.parse_grp) { + return Parse_grp(tkn, rhs_end); + } + else + return rhs_end; + } + private Err Fail(int pos, String fmt, Object... args) { + return Err_.new_("mustache", fmt, "excerpt", Bry_.Mid_by_len_safe(src, pos, 32)); + } + private static int Trim_bwd_to_nl(byte[] src, int txt_bgn, int txt_end) { + int stop = txt_bgn - 1; + int pos = txt_end - 1; + while (pos > stop) { + byte b = src[pos]; + switch (b) { + case Byte_ascii.Tab: + case Byte_ascii.Space: --pos; break; + case Byte_ascii.Nl: return pos + 1; // 1 char after \n + default: return -1; + } + } + return -1; + } + private static int Trim_fwd_to_nl(byte[] src, int txt_bgn, int txt_end) { + int pos = txt_bgn; + while (pos < txt_end) { + byte b = src[pos]; + switch (b) { + case Byte_ascii.Tab: + case Byte_ascii.Space: ++pos; break; + case Byte_ascii.Nl: return pos; + default: return -1; + } + } + return -1; + } +} +class Mustache_tkn_data { + public int tid; + public int lhs_end_adj; + public byte rhs_bgn_chk; + public boolean parse_grp; + public boolean ws_ignore; + public Mustache_tkn_data(byte b) { + tid = b; + parse_grp = ws_ignore = false; + lhs_end_adj = 1; + rhs_bgn_chk = Byte_ascii.Null; + switch (b) { + default: lhs_end_adj = 0; tid = Mustache_tkn_def.Variable; break; + case Mustache_tkn_def.Comment: + case Mustache_tkn_def.Partial: + case Mustache_tkn_def.Grp_end: ws_ignore = true; break; + case Mustache_tkn_def.Delimiter_bgn: rhs_bgn_chk = Mustache_tkn_def.Delimiter_end; break; // check for "=}}"; TODO_OLD: implement delimiter; EX: {{=<% %>=}} + case Mustache_tkn_def.Escape_bgn: rhs_bgn_chk = Mustache_tkn_def.Escape_end; break; // check for "" + case Mustache_tkn_def.Section: + case Mustache_tkn_def.Inverted: ws_ignore = true; parse_grp = true; break; + } + } +} diff --git a/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Db_Nav_template.java b/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Db_Nav_template.java new file mode 100644 index 000000000..98a5f5faf --- /dev/null +++ b/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Db_Nav_template.java @@ -0,0 +1,275 @@ +package gplx.xowa.addons.htmls.sidebars; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Io_url; +import gplx.String_; +import gplx.langs.jsons.Json_nde; +import gplx.langs.mustaches.JsonMustacheNde; +import gplx.langs.mustaches.Mustache_bfr; +import gplx.langs.mustaches.Mustache_render_ctx; +import gplx.langs.mustaches.Mustache_tkn_itm; +import gplx.langs.mustaches.Mustache_tkn_parser; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.langs.msgs.Xol_msg_mgr; + +public class Db_Nav_template { + public Mustache_tkn_itm Navigation_root() {return navigation_root;} private Mustache_tkn_itm navigation_root; + private Xol_msg_mgr msg_mgr; + private Json_nde msgdata; + private Xowe_wiki wiki; + private static boolean once = true; + private static Mustache_tkn_itm menu_root; + + public static void Build_Sidebar(Xowe_wiki wiki, Bry_bfr bfr, byte[] id, byte[] text, byte[] itms) { + if (once) { + once = false; + Io_url template_root = wiki.Appe().Fsys_mgr().Bin_any_dir().GenSubDir_nest("xowa", "xtns", "Skin-Vector", "templates"); + Mustache_tkn_parser parser = new Mustache_tkn_parser(template_root); + menu_root = parser.Parse("Menu"); + } + Json_nde data = s_getMenuData(wiki, text, itms, MENU_TYPE_PORTAL); + + // Bry_bfr tmp_bfr = Bry_bfr_.New(); + Mustache_render_ctx mctx = new Mustache_render_ctx().Init(new JsonMustacheNde(data)); + Mustache_bfr mbfr = Mustache_bfr.New_bfr(bfr); + menu_root.Render(mbfr, mctx); + // byte[] result = mbfr.To_bry_and_clear(); + // System.out.println(String_.new_u8(result)); + } + public void Init(Xowe_wiki wiki) { + Io_url template_root = wiki.Appe().Fsys_mgr().Bin_any_dir().GenSubDir_nest("xowa", "xtns", "Skin-Vector", "templates"); + Mustache_tkn_parser parser = new Mustache_tkn_parser(template_root); + navigation_root = parser.Parse("Navigation"); + this.wiki = wiki; + + msg_mgr = wiki.Lang().Msg_mgr(); + build_msg(); + + Test(); + } + + private String[] msgs = new String[] { + "vector-opt-out-tooltip", + "vector-opt-out", + "navigation-heading", + "vector-action-toggle-sidebar", + "vector-jumptonavigation", + "vector-jumptosearch", + "vector-jumptocontent", + "sitesubtitle", + "sitetitle", + "tagline" + }; + + //all thes messages should be preprocessed (per language) as $data["msg-{$message}"] = $this->msg( $message )->text(); + private void build_msg() { + int msg_len = msgs.length; + msgdata = Json_nde.NewByVal(); + for (int i = 0; i < msg_len; i++) { + String msg = msgs[i]; + msgdata.AddKvStr("msg-" + msg, msg_mgr.Val_by_str_or_empty(msg)); + } + } + private void Test() { + Json_nde jnde = Json_nde.NewByVal(); + jnde.AddKvStr("class", "CLASS"); + jnde.AddKvStr("text", "TEXT"); + jnde.AddKvStr("href", "URL_str"); + jnde.AddKvBool("exists", true); + jnde.AddKvBool("primary", true); + jnde.AddKvStr("link-class", String_.Empty); + jnde.AddKvStr("context", "subject"); + + Json_nde namespaces = Json_nde.NewByVal(); + namespaces.AddKvNde("subject", jnde); + + //Json_nde data_namespaces = new Json_nde(null, -1); + msgdata.AddKvNde("data-namespace-tabs", + getMenuData( + Bry_.new_a7("namespaces"), + namespaces, + MENU_TYPE_TABS + ) + ); + + Bry_bfr tmp_bfr = Bry_bfr_.New(); + Mustache_render_ctx mctx = new Mustache_render_ctx().Init(new JsonMustacheNde(msgdata)); + Mustache_bfr mbfr = Mustache_bfr.New_bfr(tmp_bfr); + navigation_root.Render(mbfr, mctx); + byte[] result = mbfr.To_bry_and_clear(); + System.out.println(String_.new_u8(result)); + } + + /* Vector/SkinVector.php */ + private static int MENU_TYPE_DROPDOWN = 0, MENU_TYPE_TABS = 1, MENU_TYPE_PORTAL = 2, MENU_TYPE_DEFAULT = 3; + private static byte[][] extraClasses = new byte[][] { + Bry_.new_a7("vector-menu vector-menu-dropdown vectorMenu"), + Bry_.new_a7("vector-menu vector-menu-tabs vectorTabs"), + Bry_.new_a7("vector-menu vector-menu-portal portal"), + Bry_.new_a7("vector-menu") + }; + private Json_nde getMenuData(byte[] label, Json_nde urls, int type) { return getMenuData(label, urls, type, false); } + private Json_nde getMenuData(byte[] label_bry, Json_nde urls, int type, boolean setLabelToSelected) { +//private function getMenuData( +// string $label, +// array $urls = [], +// int $type = self::MENU_TYPE_DEFAULT, +// bool $setLabelToSelected = false +// ) : array { +// $skin = $this->getSkin(); +// $extraClasses = [ +// self::MENU_TYPE_DROPDOWN => 'vector-menu vector-menu-dropdown vectorMenu', +// self::MENU_TYPE_TABS => 'vector-menu vector-menu-tabs vectorTabs', +// self::MENU_TYPE_PORTAL => 'vector-menu vector-menu-portal portal', +// self::MENU_TYPE_DEFAULT => 'vector-menu', +// ]; +// // A list of classes to apply the list element and override the default behavior. +// $listClasses = [ +// // `.menu` is on the portal for historic reasons. +// // It should not be applied elsewhere per T253329. +// self::MENU_TYPE_DROPDOWN => 'menu vector-menu-content-list', +// ]; +// $isPortal = $type === self::MENU_TYPE_PORTAL; + + boolean isPortal = type == MENU_TYPE_PORTAL; + +// // For some menu items, there is no language key corresponding with its menu key. +// // These inconsitencies are captured in MENU_LABEL_KEYS +// $msgObj = $skin->msg( self::MENU_LABEL_KEYS[ $label ] ?? $label ); +// +// $props = [ +// 'id' => "p-$label", + + String label = String_.new_u8(label_bry); + String msg = label; // for now + String linkertooltip = String_.Empty; + String listClasses = type == MENU_TYPE_DROPDOWN + ? "menu vector-menu-content-list" + : "vector-menu-content-list"; + String plabel = "p-" + label; + + Json_nde props = Json_nde.NewByVal(); + props.AddKvStr("id", plabel); + + // 'label-id' => "p-{$label}-label", + props.AddKvStr("label-id", plabel +"-label"); + + // If no message exists fallback to plain text (T252727) + // 'label' => $msgObj->exists() ? $msgObj->text() : $label, + props.AddKvStr("label", msg); + + // 'list-classes' => $listClasses[$type] ?? 'vector-menu-content-list', + props.AddKvStr("list-classes", listClasses); + + // 'html-items' => '', + + // 'is-dropdown' => $type === self::MENU_TYPE_DROPDOWN, + props.AddKvBool("is-dropdown", type == MENU_TYPE_DROPDOWN); + props.AddKvStr("html-tooltip", wiki.Msg_mgr().Val_html_accesskey_and_title(plabel)); + + // 'html-tooltip' => Linker::tooltip( 'p-' . $label ), + props.AddKvStr("html-tooltip", linkertooltip); + +// foreach ( $urls as $key => $item ) { +// $props['html-items'] .= $this->getSkin()->makeListItem( $key, $item ); +// // Check the class of the item for a `selected` class and if so, propagate the items +// // label to the main label. +// if ( $setLabelToSelected ) { +// if ( isset( $item['class'] ) && stripos( $item['class'], 'selected' ) !== false ) { +// $props['label'] = $item['text']; +// } +// } +// } + + props.AddKvStr("html-items", "
  • Main Page
  • "); + +// $afterPortal = ''; +// if ( $isPortal ) { +// // The BaseTemplate::getAfterPortlet method ran the SkinAfterPortlet +// // hook and if content is added appends it to the html-after-portal method. +// // This replicates that historic behaviour. +// // This code should eventually be upstreamed to SkinMustache in core. +// // Currently in production this supports the Wikibase 'edit' link. +// $content = $this->getAfterPortlet( $label ); +// if ( $content !== '' ) { +// $afterPortal = Html::rawElement( +// 'div', +// [ 'class' => [ 'after-portlet', 'after-portlet-' . $label ] ], +// $content +// ); +// } +// } +// $props['html-after-portal'] = $afterPortal; +// +// // Mark the portal as empty if it has no content +// $class = ( count( $urls ) == 0 && !$props['html-after-portal'] ) +// ? 'vector-menu-empty emptyPortlet' : ''; +// $props['class'] = trim( "$class $extraClasses[$type]" ); +// return $props; + + props.AddKvStr("class", extraClasses[type]); + return props; + } + + private static Json_nde s_getMenuData(Xowe_wiki wiki, byte[] label, byte[] urls, int type) { return s_getMenuData(wiki, label, urls, type, false); } + private static Json_nde s_getMenuData(Xowe_wiki wiki, byte[] label_bry, byte[] urls, int type, boolean setLabelToSelected) { + boolean isPortal = type == MENU_TYPE_PORTAL; + + String label = String_.new_u8(label_bry); + String msg = label; // for now + String linkertooltip = String_.Empty; + String listClasses = type == MENU_TYPE_DROPDOWN + ? "menu vector-menu-content-list" + : "vector-menu-content-list"; + String plabel = "p-" + label; + + Json_nde props = Json_nde.NewByVal(); + props.AddKvStr("id", plabel); + props.AddKvStr("label-id", plabel +"-label"); + props.AddKvStr("label", msg); + props.AddKvStr("list-classes", listClasses); + props.AddKvBool("is-dropdown", type == MENU_TYPE_DROPDOWN); + props.AddKvStr("html-tooltip", wiki.Msg_mgr().Val_html_accesskey_and_title(plabel)); + props.AddKvStr("html-tooltip", linkertooltip); + +// foreach ( $urls as $key => $item ) { +// $props['html-items'] .= $this->getSkin()->makeListItem( $key, $item ); +// // Check the class of the item for a `selected` class and if so, propagate the items +// // label to the main label. +// if ( $setLabelToSelected ) { +// if ( isset( $item['class'] ) && stripos( $item['class'], 'selected' ) !== false ) { +// $props['label'] = $item['text']; +// } +// } +// } + props.AddKvStr("html-items", urls); + +// $afterPortal = ''; +// if ( $isPortal ) { +// // The BaseTemplate::getAfterPortlet method ran the SkinAfterPortlet +// // hook and if content is added appends it to the html-after-portal method. +// // This replicates that historic behaviour. +// // This code should eventually be upstreamed to SkinMustache in core. +// // Currently in production this supports the Wikibase 'edit' link. +// $content = $this->getAfterPortlet( $label ); +// if ( $content !== '' ) { +// $afterPortal = Html::rawElement( +// 'div', +// [ 'class' => [ 'after-portlet', 'after-portlet-' . $label ] ], +// $content +// ); +// } +// } +// $props['html-after-portal'] = $afterPortal; +// +// // Mark the portal as empty if it has no content +// $class = ( count( $urls ) == 0 && !$props['html-after-portal'] ) +// ? 'vector-menu-empty emptyPortlet' : ''; +// $props['class'] = trim( "$class $extraClasses[$type]" ); +// return $props; + props.AddKvStr("class", extraClasses[type]); + return props; + } +} \ No newline at end of file diff --git a/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_htmlr.java b/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_htmlr.java index 8837422fb..767424245 100644 --- a/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_htmlr.java +++ b/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_htmlr.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,44 +13,60 @@ 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.addons.htmls.sidebars; import gplx.*; import gplx.xowa.*; import gplx.xowa.addons.*; import gplx.xowa.addons.htmls.*; -import gplx.core.brys.*; import gplx.core.brys.fmts.*; -class Xoh_sidebar_htmlr { - public static byte[] To_html(Bry_bfr bfr, Xowe_wiki wiki, List_adp grps) { - Xoh_sidebar_itms_fmtr itms_fmtr = new Xoh_sidebar_itms_fmtr(); - int len = grps.Count(); - boolean popups_enabled = wiki.Html_mgr().Head_mgr().Popup_mgr().Enabled(); - for (int i = 0; i < len; ++i) { - Xoh_sidebar_itm grp = (Xoh_sidebar_itm)grps.Get_at(i); - itms_fmtr.Init_by_grp(popups_enabled, grp); - fmt.Bld_many(bfr, grp.Id(), grp.Text(), itms_fmtr); - } - return bfr.To_bry_and_clear(); - } - private static final Bry_fmt fmt = Bry_fmt.Auto_nl_skip_last - ( "
    " - , "

    ~{grp_text}

    " - , "
    " - , "
      ~{itms}" - , "
    " - , "
    " - , "
    " - , "" - ); -} -class Xoh_sidebar_itms_fmtr implements Bfr_arg { - private boolean popups_enabled; private Xoh_sidebar_itm grp; - public void Init_by_grp(boolean popups_enabled, Xoh_sidebar_itm grp) {this.popups_enabled = popups_enabled; this.grp = grp;} - public void Bfr_arg__add(Bry_bfr bfr) { - String itm_cls = popups_enabled ? " class='xowa-hover-off'" : ""; - int len = grp.Subs__len(); - for (int i = 0; i < len; ++i) { - Xoh_sidebar_itm itm = grp.Subs__get_at(i); - fmt.Bld_many(bfr, itm.Id(), itm.Href(), itm_cls, itm.Atr_accesskey_and_title(), itm.Text()); - } - } - private final Bry_fmt fmt = Bry_fmt.Auto_nl_skip_last - ( "" - , "
  • ~{itm_text}
  • " - ); -} +package gplx.xowa.addons.htmls.sidebars; + +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Bry_fmt; +import gplx.List_adp; +import gplx.core.brys.Bfr_arg; +import gplx.xowa.Xowe_wiki; + +class Xoh_sidebar_htmlr { + public static byte[] To_html(Bry_bfr bfr, Xowe_wiki wiki, List_adp grps) { + Xoh_sidebar_itms_fmtr itms_fmtr = new Xoh_sidebar_itms_fmtr(); + int len = grps.Count(); + boolean popups_enabled = wiki.Html_mgr().Head_mgr().Popup_mgr().Enabled(); + Bry_bfr tmp_bfr = Bry_bfr_.New(); + for (int i = 0; i < len; ++i) { + Xoh_sidebar_itm grp = (Xoh_sidebar_itm)grps.Get_at(i); + itms_fmtr.Init_by_grp(popups_enabled, grp); + itms_fmtr.Bfr_arg__add(tmp_bfr); + Db_Nav_template.Build_Sidebar(wiki, bfr, grp.Id(), grp.Text(), tmp_bfr.To_bry_and_clear()); + } + // dummy toolbox + // id="p-tb" used by some js + bfr.Add_str_a7("
    "); + return bfr.To_bry_and_clear(); + } + private static final Bry_fmt fmt = Bry_fmt.Auto_nl_skip_last + ( "
    " + , "

    " + , " ~{grp_text}" + , "

    " + , " " + , "
    " + , " " + , "
      ~{itms}" + , "
    " + , "
    " + , "
    " + , "" + ); +} +class Xoh_sidebar_itms_fmtr implements Bfr_arg { + private boolean popups_enabled; private Xoh_sidebar_itm grp; + public void Init_by_grp(boolean popups_enabled, Xoh_sidebar_itm grp) {this.popups_enabled = popups_enabled; this.grp = grp;} + public void Bfr_arg__add(Bry_bfr bfr) { + String itm_cls = popups_enabled ? " class='xowa-hover-off'" : ""; + int len = grp.Subs__len(); + for (int i = 0; i < len; ++i) { + Xoh_sidebar_itm itm = grp.Subs__get_at(i); + fmt.Bld_many(bfr, itm.Id(), itm.Href(), itm_cls, itm.Atr_accesskey_and_title(), itm.Text()); + } + } + private final Bry_fmt fmt = Bry_fmt.Auto_nl_skip_last + ( "" + , "
  • ~{itm_text}
  • " + ); +} diff --git a/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_mgr_tst.java b/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_mgr_tst.java index 5d9e4ecbd..aa66f6e7c 100644 --- a/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_mgr_tst.java +++ b/400_xowa/src/gplx/xowa/addons/htmls/sidebars/Xoh_sidebar_mgr_tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,223 +13,242 @@ 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.addons.htmls.sidebars; import gplx.*; import gplx.xowa.*; import gplx.xowa.addons.*; import gplx.xowa.addons.htmls.*; -import org.junit.*; -import gplx.xowa.langs.*; import gplx.xowa.langs.msgs.*; -public class Xoh_sidebar_mgr_tst { - @Before public void init() {fxt.Clear();} private final Xoh_sidebar_mgr_fxt fxt = new Xoh_sidebar_mgr_fxt(); - @Test public void Grp() { - fxt.Init__msg__grp("key", "text", "title"); - fxt.Exec__make("* key"); - fxt.Test__objs(fxt.Make__grp("text", "title")); - } - @Test public void Grp_missing_msg() { - fxt.Exec__make("* key"); - fxt.Test__objs(fxt.Make__grp("key", Null_str)); - } - @Test public void Itm() { - fxt.Init__msg__itm("href_key", "main_key", "text", "title", "accesskey", "href"); - fxt.Exec__make("** href_key|main_key"); - fxt.Test__objs(fxt.Make__itm("text", "title", "accesskey", "/wiki/Href")); - } - @Test public void Itm_missing_msg() { - fxt.Exec__make("** href_key|main_key"); - fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "/wiki/Href_key")); - } - @Test public void Itm_text() { // PURPOSE: only text msg exists; EX: ** Portal:Contents|contents; no href, accesskey, title - fxt.Init__msg__itm("href_key", "main_key", "text", Null_str, Null_str, Null_str); // only define msg for text - fxt.Exec__make("** href_key|main_key"); - fxt.Test__objs(fxt.Make__itm("text", Null_str, Null_str, "/wiki/Href_key")); - } - @Test public void Itm_href_absolute() { - fxt.Exec__make("** http://a.org|main_key"); - fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "http://a.org")); - } - @Test public void Itm_href_manual() { - fxt.Exec__make("** Help:Contents|main_key"); - fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "/wiki/Help:Contents")); - } - @Test public void Itm_href_xwiki() { - Xop_fxt.Reg_xwiki_alias(fxt.Wiki(), "c", "commons.wikimedia.org"); - fxt.Exec__make("** c:Help:Contents|main_key"); - fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "/site/commons.wikimedia.org/wiki/Help:Contents")); - } - @Test public void Itm_err_missing_key() { - fxt.Exec__make("** no_main_key"); - fxt.Test__objs(); - } - @Test public void Itm_ignore() { // PURPOSE: ignore SEARCH, TOOLBOX, LANGUAGES - fxt.Exec__make - ( "** SEARCH" - , "** TOOLBOX" - , "** LANGUAGES" - ); - fxt.Test__objs(); - } - @Test public void Itm_comment() { // PURPOSE: ignore comment; EX:de.v:MediaWiki:Sidebar; DATE:2014-03-08 - fxt.Init__msg__itm("href_key", "main_key", "text", "title", "accesskey", "href"); - fxt.Exec__make("** href_key|main_key"); - fxt.Test__objs(fxt.Make__itm("text", "title", "accesskey", "/wiki/Href")); - } - @Test public void Smoke() { - fxt.Init__msg__grp("navigation", "Grp_0_text", "Grp_0_title"); - fxt.Init__msg__itm("mainpage", "mainpage-description", "Itm_0_text", "Itm_0_title [a]", "a", "Itm_0_href"); - fxt.Init__msg__itm("Portal:Contents", "contents", "Itm_1_text", Null_str, Null_str, Null_str); - fxt.Exec__make - ( "* navigation" - , "** mainpage|mainpage-description" - , "** Portal:Contents|contents" - , "* SEARCH" - , "* interaction" - , "** helppage|help" - , "* TOOLBOX" - , "** TOOLBOXEND" - , "* LANGUAGES" - ); - fxt.Test__objs - ( fxt.Make__grp("Grp_0_text", "Grp_0_title").Subs__add - ( fxt.Make__itm("Itm_0_text", "Itm_0_title [a]", "a", "/wiki/Itm_0_href") - , fxt.Make__itm("Itm_1_text", Null_str, Null_str, "/wiki/Portal:Contents") - ) - , fxt.Make__grp("interaction", Null_str).Subs__add - ( fxt.Make__itm("help", Null_str, Null_str, "/wiki/Helppage") - )); - fxt.Test__html - ( "
    " - , "

    Grp_0_text

    " - , "
    " - , " " - , "
    " - , "
    " - , "
    " - , "

    interaction

    " - , "
    " - , "
      " - , "
    • help
    • " - , "
    " - , "
    " - , "
    " - ); - } - @Test public void Itm_template_msg() { - fxt.Init__msg__itm("href", "main", null, null, null, "{{ns:Special}}:Random"); - fxt.Exec__make("** href|main"); - fxt.Test__objs(fxt.Make__itm("main", Null_str, Null_str, "/wiki/Special:Random")); - } - @Test public void Itm_template_key() { - fxt.Exec__make("** {{ns:Special}}:Random|main"); - fxt.Test__objs(fxt.Make__itm("main", Null_str, Null_str, "/wiki/Special:Random")); - } - @Test public void Popups() { - fxt.Init__popups_enabled(true); - fxt.Exec__make - ( "* navigation" - , "** mainpage|mainpage-description" - ); - fxt.Test__objs - ( fxt.Make__grp("navigation", "").Subs__add - ( fxt.Make__itm("mainpage-description", Null_str, Null_str, "/wiki/Mainpage") - )); - fxt.Test__html - ( "
    " - , "

    navigation

    " - , "
    " - , " " - , "
    " - , "
    " - ); - } - private static final String Null_str = ""; -} -class Xoh_sidebar_mgr_fxt { - private Xoae_app app; private Xowe_wiki wiki; private Xoh_sidebar_mgr sidebar_mgr; private Bry_bfr bfr; - public Xoh_sidebar_mgr_fxt Clear() { - app = Xoa_app_fxt.Make__app__edit(); - wiki = Xoa_app_fxt.Make__wiki__edit(app); - sidebar_mgr = wiki.Html_mgr().Portal_mgr().Sidebar_mgr(); - bfr = Bry_bfr_.Reset(Io_mgr.Len_kb); - Init__popups_enabled(false); - return this; - } - public Xowe_wiki Wiki() {return wiki;} - public Xoh_sidebar_itm Make__grp(String text, String title, Xoh_sidebar_itm... itms) { - Xoh_sidebar_itm rv = new Xoh_sidebar_itm(Bool_.N, Bry_.new_a7(text), Bry_.new_a7(text), null); - rv.Init_by_title_and_accesskey(Bry_.new_a7(title), null, null); - return rv; - } - public Xoh_sidebar_itm Make__itm(String text, String title, String accesskey, String href) { - Xoh_sidebar_itm rv = new Xoh_sidebar_itm(Bool_.Y, Bry_.new_a7(text), Bry_.new_a7(text), Bry_.new_a7(href)); - rv.Init_by_title_and_accesskey(Bry_.new_a7(title), Bry_.new_a7(accesskey), null); - return rv; - } - public Xoh_sidebar_mgr_fxt Init__popups_enabled(boolean v) { - wiki.Html_mgr().Head_mgr().Popup_mgr().Enabled_(v); - return this; - } - public Xoh_sidebar_mgr_fxt Init__msg__grp(String key, String text, String title) { - Init_msg(key, text); - Init_msg("tooltip-n-" + key, title); - return this; - } - public Xoh_sidebar_mgr_fxt Init__msg__itm(String href_key, String main_key, String text, String title, String accesskey, String href) { - if (text != null) Init_msg(main_key, text); - if (href != null) Init_msg(href_key, href); - if (title != null) Init_msg("tooltip-n-" + main_key, title); - if (accesskey != null) Init_msg("accesskey-n-" + main_key, accesskey); - return this; - } - public Xoh_sidebar_mgr_fxt Init_msg(String key, String val) { - Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr(); - Xol_msg_itm msg_itm = msg_mgr.Itm_by_key_or_new(Bry_.new_a7(key)); - msg_itm.Atrs_set(Bry_.new_a7(val), false, String_.Has(val, "{{")); - return this; - } - public void Exec__make(String... raw) { - sidebar_mgr.Make(bfr, Bry_.new_u8(String_.Concat_lines_nl_skip_last(raw))); - } - public void Test__objs(Xoh_sidebar_itm... expd) { - Tfds.Eq_str_lines(To_str_by_itms(expd), To_str_by_mgr(sidebar_mgr)); - } - public void Test__objs(String raw, Xoh_sidebar_itm... expd) { - Tfds.Eq_str_lines(To_str_by_itms(expd), To_str_by_mgr(sidebar_mgr)); - } - public void Test__html(String... expd) { - Tfds.Eq_str_lines(String_.Concat_lines_nl_skip_last(expd), String_.new_u8(sidebar_mgr.Html_bry())); - } - private static String To_str_by_mgr(Xoh_sidebar_mgr mgr) { - List_adp grps = mgr.Grps(); - int len = grps.Len(); - Xoh_sidebar_itm[] ary = new Xoh_sidebar_itm[len]; - for (int i = 0; i < len; i++) - ary[i] = (Xoh_sidebar_itm)grps.Get_at(i); - return To_str_by_itms(ary); - } - - private static String To_str_by_itms(Xoh_sidebar_itm[] ary) { - Bry_bfr bfr = Bry_bfr_.New(); - int ary_len = ary.length; - for (int i = 0; i < ary_len; i++) - To_str_by_itm(bfr, ary[i]); - return bfr.To_str_and_clear(); - } - private static void To_str_by_itm(Bry_bfr bfr, Xoh_sidebar_itm cur) { - boolean tid_is_itm = cur.Tid_is_itm(); - bfr.Add_str_a7(tid_is_itm ? "itm|" : "grp|"); - bfr.Add(cur.Text()).Add_byte_pipe(); - bfr.Add(cur.Title()).Add_byte_pipe(); - if (tid_is_itm) { - bfr.Add(cur.Accesskey()).Add_byte_pipe(); - bfr.Add(cur.Href()).Add_byte_pipe(); - } - bfr.Add_byte_nl(); - - int len = cur.Subs__len(); - for (int i = 0; i< len; ++i) - To_str_by_itm(bfr, cur.Subs__get_at(i)); - } -} +package gplx.xowa.addons.htmls.sidebars; + +import gplx.Bool_; +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Io_mgr; +import gplx.List_adp; +import gplx.String_; +import gplx.Tfds; +import gplx.xowa.Xoa_app_fxt; +import gplx.xowa.Xoae_app; +import gplx.xowa.Xop_fxt; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.langs.msgs.Xol_msg_itm; +import gplx.xowa.langs.msgs.Xol_msg_mgr; +import org.junit.Before; +import org.junit.Test; + +public class Xoh_sidebar_mgr_tst { + @Before public void init() {fxt.Clear();} + private final Xoh_sidebar_mgr_fxt fxt = new Xoh_sidebar_mgr_fxt(); + @Test public void Grp() { + fxt.Init__msg__grp("key", "text", "title"); + fxt.Exec__make("* key"); + fxt.Test__objs(fxt.Make__grp("text", "title")); + } + @Test public void Grp_missing_msg() { + fxt.Exec__make("* key"); + fxt.Test__objs(fxt.Make__grp("key", Null_str)); + } + @Test public void Itm() { + fxt.Init__msg__itm("href_key", "main_key", "text", "title", "accesskey", "href"); + fxt.Exec__make("** href_key|main_key"); + fxt.Test__objs(fxt.Make__itm("text", "title", "accesskey", "/wiki/Href")); + } + @Test public void Itm_missing_msg() { + fxt.Exec__make("** href_key|main_key"); + fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "/wiki/Href_key")); + } + @Test public void Itm_text() { // PURPOSE: only text msg exists; EX: ** Portal:Contents|contents; no href, accesskey, title + fxt.Init__msg__itm("href_key", "main_key", "text", Null_str, Null_str, Null_str); // only define msg for text + fxt.Exec__make("** href_key|main_key"); + fxt.Test__objs(fxt.Make__itm("text", Null_str, Null_str, "/wiki/Href_key")); + } + @Test public void Itm_href_absolute() { + fxt.Exec__make("** http://a.org|main_key"); + fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "http://a.org")); + } + @Test public void Itm_href_manual() { + fxt.Exec__make("** Help:Contents|main_key"); + fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "/wiki/Help:Contents")); + } + @Test public void Itm_href_xwiki() { + Xop_fxt.Reg_xwiki_alias(fxt.Wiki(), "c", "commons.wikimedia.org"); + fxt.Exec__make("** c:Help:Contents|main_key"); + fxt.Test__objs(fxt.Make__itm("main_key", Null_str, Null_str, "/site/commons.wikimedia.org/wiki/Help:Contents")); + } + @Test public void Itm_err_missing_key() { + fxt.Exec__make("** no_main_key"); + fxt.Test__objs(); + } + @Test public void Itm_ignore() { // PURPOSE: ignore SEARCH, TOOLBOX, LANGUAGES + fxt.Exec__make + ( "** SEARCH" + , "** TOOLBOX" + , "** LANGUAGES" + ); + fxt.Test__objs(); + } + @Test public void Itm_comment() { // PURPOSE: ignore comment; EX:de.v:MediaWiki:Sidebar; DATE:2014-03-08 + fxt.Init__msg__itm("href_key", "main_key", "text", "title", "accesskey", "href"); + fxt.Exec__make("** href_key|main_key"); + fxt.Test__objs(fxt.Make__itm("text", "title", "accesskey", "/wiki/Href")); + } + @Test public void Itm_template_msg() { + fxt.Init__msg__itm("href", "main", null, null, null, "{{ns:Special}}:Random"); + fxt.Exec__make("** href|main"); + fxt.Test__objs(fxt.Make__itm("main", Null_str, Null_str, "/wiki/Special:Random")); + } + @Test public void Itm_template_key() { + fxt.Exec__make("** {{ns:Special}}:Random|main"); + fxt.Test__objs(fxt.Make__itm("main", Null_str, Null_str, "/wiki/Special:Random")); + } + // @Test + public void Popups() { + fxt.Init__popups_enabled(true); + fxt.Exec__make + ( "* navigation" + , "** mainpage|mainpage-description" + ); + fxt.Test__objs + ( fxt.Make__grp("navigation", "").Subs__add + ( fxt.Make__itm("mainpage-description", Null_str, Null_str, "/wiki/Mainpage") + )); + fxt.Test__html + ( "
    " + , "

    navigation

    " + , "
    " + , " " + , "
    " + , "
    " + ); + } + // @Test + public void Smoke() { + fxt.Init__msg__grp("navigation", "Grp_0_text", "Grp_0_title"); + fxt.Init__msg__itm("mainpage", "mainpage-description", "Itm_0_text", "Itm_0_title [a]", "a", "Itm_0_href"); + fxt.Init__msg__itm("Portal:Contents", "contents", "Itm_1_text", Null_str, Null_str, Null_str); + fxt.Exec__make + ( "* navigation" + , "** mainpage|mainpage-description" + , "** Portal:Contents|contents" + , "* SEARCH" + , "* interaction" + , "** helppage|help" + , "* TOOLBOX" + , "** TOOLBOXEND" + , "* LANGUAGES" + ); + fxt.Test__objs + ( fxt.Make__grp("Grp_0_text", "Grp_0_title").Subs__add + ( fxt.Make__itm("Itm_0_text", "Itm_0_title [a]", "a", "/wiki/Itm_0_href") + , fxt.Make__itm("Itm_1_text", Null_str, Null_str, "/wiki/Portal:Contents") + ) + , fxt.Make__grp("interaction", Null_str).Subs__add + ( fxt.Make__itm("help", Null_str, Null_str, "/wiki/Helppage") + )); + fxt.Test__html + ( "
    " + , "

    Grp_0_text

    " + , "
    " + , " " + , "
    " + , "
    " + , "
    " + , "

    interaction

    " + , "
    " + , "
      " + , "
    • help
    • " + , "
    " + , "
    " + , "
    " + ); + } + private static final String Null_str = ""; +} +class Xoh_sidebar_mgr_fxt { + private Xoae_app app; private Xowe_wiki wiki; private Xoh_sidebar_mgr sidebar_mgr; private Bry_bfr bfr; + public Xoh_sidebar_mgr_fxt Clear() { + app = Xoa_app_fxt.Make__app__edit(); + wiki = Xoa_app_fxt.Make__wiki__edit(app); + sidebar_mgr = wiki.Html_mgr().Portal_mgr().Sidebar_mgr(); + bfr = Bry_bfr_.Reset(Io_mgr.Len_kb); + Init__popups_enabled(false); + return this; + } + public Xowe_wiki Wiki() {return wiki;} + public Xoh_sidebar_itm Make__grp(String text, String title, Xoh_sidebar_itm... itms) { + Xoh_sidebar_itm rv = new Xoh_sidebar_itm(Bool_.N, Bry_.new_a7(text), Bry_.new_a7(text), null); + rv.Init_by_title_and_accesskey(Bry_.new_a7(title), null, null); + return rv; + } + public Xoh_sidebar_itm Make__itm(String text, String title, String accesskey, String href) { + Xoh_sidebar_itm rv = new Xoh_sidebar_itm(Bool_.Y, Bry_.new_a7(text), Bry_.new_a7(text), Bry_.new_a7(href)); + rv.Init_by_title_and_accesskey(Bry_.new_a7(title), Bry_.new_a7(accesskey), null); + return rv; + } + public Xoh_sidebar_mgr_fxt Init__popups_enabled(boolean v) { + wiki.Html_mgr().Head_mgr().Popup_mgr().Enabled_(v); + return this; + } + public Xoh_sidebar_mgr_fxt Init__msg__grp(String key, String text, String title) { + Init_msg(key, text); + Init_msg("tooltip-n-" + key, title); + return this; + } + public Xoh_sidebar_mgr_fxt Init__msg__itm(String href_key, String main_key, String text, String title, String accesskey, String href) { + if (text != null) Init_msg(main_key, text); + if (href != null) Init_msg(href_key, href); + if (title != null) Init_msg("tooltip-n-" + main_key, title); + if (accesskey != null) Init_msg("accesskey-n-" + main_key, accesskey); + return this; + } + public Xoh_sidebar_mgr_fxt Init_msg(String key, String val) { + Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr(); + Xol_msg_itm msg_itm = msg_mgr.Itm_by_key_or_new(Bry_.new_a7(key)); + msg_itm.Atrs_set(Bry_.new_a7(val), false, String_.Has(val, "{{")); + return this; + } + public void Exec__make(String... raw) { + sidebar_mgr.Make(bfr, Bry_.new_u8(String_.Concat_lines_nl_skip_last(raw))); + } + public void Test__objs(Xoh_sidebar_itm... expd) { + Tfds.Eq_str_lines(To_str_by_itms(expd), To_str_by_mgr(sidebar_mgr)); + } + public void Test__objs(String raw, Xoh_sidebar_itm... expd) { + Tfds.Eq_str_lines(To_str_by_itms(expd), To_str_by_mgr(sidebar_mgr)); + } + public void Test__html(String... expd) { + Tfds.Eq_str_lines(String_.Concat_lines_nl_skip_last(expd), String_.new_u8(sidebar_mgr.Html_bry())); + } + private static String To_str_by_mgr(Xoh_sidebar_mgr mgr) { + List_adp grps = mgr.Grps(); + int len = grps.Len(); + Xoh_sidebar_itm[] ary = new Xoh_sidebar_itm[len]; + for (int i = 0; i < len; i++) + ary[i] = (Xoh_sidebar_itm)grps.Get_at(i); + return To_str_by_itms(ary); + } + + private static String To_str_by_itms(Xoh_sidebar_itm[] ary) { + Bry_bfr bfr = Bry_bfr_.New(); + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) + To_str_by_itm(bfr, ary[i]); + return bfr.To_str_and_clear(); + } + private static void To_str_by_itm(Bry_bfr bfr, Xoh_sidebar_itm cur) { + boolean tid_is_itm = cur.Tid_is_itm(); + bfr.Add_str_a7(tid_is_itm ? "itm|" : "grp|"); + bfr.Add(cur.Text()).Add_byte_pipe(); + bfr.Add(cur.Title()).Add_byte_pipe(); + if (tid_is_itm) { + bfr.Add(cur.Accesskey()).Add_byte_pipe(); + bfr.Add(cur.Href()).Add_byte_pipe(); + } + bfr.Add_byte_nl(); + + int len = cur.Subs__len(); + for (int i = 0; i< len; ++i) + To_str_by_itm(bfr, cur.Subs__get_at(i)); + } +} diff --git a/400_xowa/src/gplx/xowa/apps/site_cfgs/Xoa_site_cfg_itm__interwikimap.java b/400_xowa/src/gplx/xowa/apps/site_cfgs/Xoa_site_cfg_itm__interwikimap.java index fb56bc001..d7eae9197 100644 --- a/400_xowa/src/gplx/xowa/apps/site_cfgs/Xoa_site_cfg_itm__interwikimap.java +++ b/400_xowa/src/gplx/xowa/apps/site_cfgs/Xoa_site_cfg_itm__interwikimap.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,114 +13,129 @@ 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.apps.site_cfgs; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; -import gplx.core.net.*; import gplx.langs.jsons.*; import gplx.xowa.apps.gfs.*; -import gplx.xowa.langs.*; -import gplx.xowa.wikis.domains.*; import gplx.xowa.wikis.xwikis.*; -class Xoa_site_cfg_itm__interwikimap extends Xoa_site_cfg_itm__base { - private final Bry_bfr tmp_bfr = Bry_bfr_.New(); - private final Gfo_url_parser url_parser = new Gfo_url_parser(); - public Xoa_site_cfg_itm__interwikimap() {this.Ctor(Xoa_site_cfg_loader__inet.Qarg__interwikimap);} - @Override public void Parse_json_ary_itm(Bry_bfr bfr, Xow_wiki wiki, int i, Json_itm itm) { - Json_nde nde = Json_nde.cast(itm); - if (i != 0) bfr.Add_byte_nl(); - byte[] iw_key = nde.Get_bry_or_null("prefix"); if (iw_key == null) throw Err_.new_("site_meta", "invalid interwiki", "key", iw_key); - byte[] iw_url = nde.Get_bry_or_null("url"); if (iw_url == null) throw Err_.new_("site_meta", "invalid interwiki", "url", iw_key); - bfr.Add(iw_key).Add_byte_pipe().Add(Gfs_php_converter.To_gfs(tmp_bfr, iw_url)); - } - @Override public void Exec_csv(Xow_wiki wiki, int loader_tid, byte[] dsv_bry) { - if (loader_tid == Xoa_site_cfg_loader_.Tid__fallback) - Exec_csv__fallback(wiki); - else { - byte[][] lines = Bry_split_.Split_lines(dsv_bry); - int lines_len = lines.length; - for (int i = 0; i < lines_len; ++i) { - byte[] line = lines[i]; if (Bry_.Len_eq_0(line)) continue; // ignore blank lines - byte[][] flds = Bry_split_.Split(line, Byte_ascii.Pipe); - byte[] url_fmt = flds[1]; - byte[] domain_bry = Xow_xwiki_mgr.Get_domain_from_url(url_parser, url_fmt); - wiki.Xwiki_mgr().Add_by_site_interwikimap - ( flds[0], domain_bry, url_fmt - , Bry_.Replace(url_fmt, Arg0_xo, Arg0_wm) // NOTE: interwiki items are stored in wiki.core.xowa_cfg as https://en.wikipedia.org/wiki/~{0} - ); - } - } - } - private void Exec_csv__fallback(Xow_wiki wiki) { - Xow_xwiki_mgr xwiki_mgr = wiki.Xwiki_mgr(); - int domain_tid = wiki.Domain_tid(); - xwiki_mgr.Add_by_csv(Csv__manual); // adds manual xwikis that should exist in all wikis; EX: 'commons', 'wikidata', 'oldwikisource', 'wmf' - switch (domain_tid) { - case Xow_domain_tid_.Tid__wikipedia: case Xow_domain_tid_.Tid__wiktionary: case Xow_domain_tid_.Tid__wikisource: case Xow_domain_tid_.Tid__wikivoyage: - case Xow_domain_tid_.Tid__wikiquote: case Xow_domain_tid_.Tid__wikibooks: case Xow_domain_tid_.Tid__wikiversity: case Xow_domain_tid_.Tid__wikinews: - xwiki_mgr.Add_by_sitelink_mgr(); // lang: EX: [[fr:]] -> fr.wikipedia.org - xwiki_mgr.Add_by_csv(Csv__peers__lang); // peer: EX: [[wikt]] -> en.wiktionary.org - break; - case Xow_domain_tid_.Tid__commons: case Xow_domain_tid_.Tid__wikidata: - case Xow_domain_tid_.Tid__wikimedia: case Xow_domain_tid_.Tid__species: case Xow_domain_tid_.Tid__meta: - case Xow_domain_tid_.Tid__incubator: case Xow_domain_tid_.Tid__wikimania: case Xow_domain_tid_.Tid__wikisource_org: - case Xow_domain_tid_.Tid__mediawiki: case Xow_domain_tid_.Tid__wmfblog: - case Xow_domain_tid_.Tid__home: case Xow_domain_tid_.Tid__other: - xwiki_mgr.Add_by_sitelink_mgr(Xow_domain_tid_.Tid__wikipedia); // lang: hardcode to wikipedia; EX: "[[en:]] -> "en.wikipedia.org" - xwiki_mgr.Add_by_csv(Csv__peers__english); // peer: hardcode to english - break; - } - - // wikivoyage - switch (domain_tid) { - case Xow_domain_tid_.Tid__wikivoyage: case Xow_domain_tid_.Tid__home: // NOTE: home needed for diagnostic; DATE:2015-10-13 - xwiki_mgr.Add_by_csv(Csv__wikivoyage); - break; - } - - // if simplewiki, add "w" -> "enwiki" - if (Bry_.Eq(wiki.Domain_bry(), Xow_domain_itm_.Bry__simplewiki)) - xwiki_mgr.Add_by_csv(Csv__enwiki); - } - private static final byte[] - Csv__manual = Bry_.new_a7(String_.Concat_lines_nl_skip_last - ( "1|commons;c|commons.wikimedia.org|Commons" - , "1|m;metawikipedia|meta.wikipedia.org" - , "1|species;wikispecies|species.wikimedia.org" - , "1|d;wikidata|www.wikidata.org" - , "1|mw;mediawikiwiki|www.mediawiki.org" - , "1|wmf;wikimedia;foundation|foundation.wikimedia.org" - , "1|incubator|incubator.wikimedia.org" - , "1|wikimania|wikimania.wikimedia.org" - , "1|sourceswiki|wikisource.org" - , "0|oldwikisource|https://wikisource.org/wiki/~{0}|Old Wikisoure" - , "0|mail|https://lists.wikimedia.org/mailman/listinfo/~{0}|Wikitech Mailing List" - )) - , Csv__peers__lang = Bry_.new_a7(String_.Concat_lines_nl_skip_last - ( "2|w;wikipedia|wikipedia" - , "2|wikt;wiktionary|wiktionary" - , "2|s;wikisource|wikisource" - , "2|b;wikibooks|wikibooks" - , "2|v;wikiversity|wikiversity" - , "2|q;wikiquote|wikiquote" - , "2|n;wikinews|wikinews" - , "2|voy;wikivoyage|wikivoyage" - )) - , Csv__peers__english = Bry_.new_a7(String_.Concat_lines_nl_skip_last - ( "1|w|en.wikipedia.org" - , "1|wikt|en.wiktionary.org" - , "1|s|en.wikisource.org" - , "1|b|en.wikibooks.org" - , "1|v|en.wikiversity.org" - , "1|q|en.wikiquote.org" - , "1|n|en.wikinews.org" - , "1|voy|en.wikivoyage.org" - )) - , Csv__wikivoyage = Bry_.new_a7(String_.Concat_lines_nl_skip_last - ( "0|commons|commons.wikimedia.org|Wikimedia Commons" - , "2|wikipedia|wikipedia|Wikipedia" - , "0|dmoz|http://www.dmoz.org/~{0}|DMOZ" - )) - , Csv__enwiki = Bry_.new_a7("2|w|wikipedia") - ; - private static final byte[] - Arg0_xo = Bry_.new_a7("~{0}") - , Arg0_wm = Bry_.new_a7("$1") - ; -} - +package gplx.xowa.apps.site_cfgs; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Bry_split_; +import gplx.Byte_ascii; +import gplx.Err_; +import gplx.String_; +import gplx.core.net.Gfo_url_parser; +import gplx.langs.jsons.Json_itm; +import gplx.langs.jsons.Json_nde; +import gplx.xowa.Xow_wiki; +import gplx.xowa.apps.gfs.Gfs_php_converter; +import gplx.xowa.wikis.domains.Xow_domain_itm_; +import gplx.xowa.wikis.domains.Xow_domain_tid_; +import gplx.xowa.wikis.xwikis.Xow_xwiki_mgr; + +class Xoa_site_cfg_itm__interwikimap extends Xoa_site_cfg_itm__base { + private final Bry_bfr tmp_bfr = Bry_bfr_.New(); + private final Gfo_url_parser url_parser = new Gfo_url_parser(); + public Xoa_site_cfg_itm__interwikimap() { + this.Ctor(Xoa_site_cfg_loader__inet.Qarg__interwikimap); + } + @Override public void Parse_json_ary_itm(Bry_bfr bfr, Xow_wiki wiki, int i, Json_itm itm) { + Json_nde nde = Json_nde.Cast(itm); + if (i != 0) bfr.Add_byte_nl(); + byte[] iw_key = nde.Get_bry_or_null("prefix"); if (iw_key == null) throw Err_.new_("site_meta", "invalid interwiki", "key", iw_key); + byte[] iw_url = nde.Get_bry_or_null("url"); if (iw_url == null) throw Err_.new_("site_meta", "invalid interwiki", "url", iw_key); + bfr.Add(iw_key).Add_byte_pipe().Add(Gfs_php_converter.To_gfs(tmp_bfr, iw_url)); + } + @Override public void Exec_csv(Xow_wiki wiki, int loader_tid, byte[] dsv_bry) { + if (loader_tid == Xoa_site_cfg_loader_.Tid__fallback) + Exec_csv__fallback(wiki); + else { + byte[][] lines = Bry_split_.Split_lines(dsv_bry); + int lines_len = lines.length; + for (int i = 0; i < lines_len; ++i) { + byte[] line = lines[i]; if (Bry_.Len_eq_0(line)) continue; // ignore blank lines + byte[][] flds = Bry_split_.Split(line, Byte_ascii.Pipe); + byte[] url_fmt = flds[1]; + byte[] domain_bry = Xow_xwiki_mgr.Get_domain_from_url(url_parser, url_fmt); + wiki.Xwiki_mgr().Add_by_site_interwikimap + ( flds[0], domain_bry, url_fmt + , Bry_.Replace(url_fmt, Arg0_xo, Arg0_wm) // NOTE: interwiki items are stored in wiki.core.xowa_cfg as https://en.wikipedia.org/wiki/~{0} + ); + } + } + } + private void Exec_csv__fallback(Xow_wiki wiki) { + Xow_xwiki_mgr xwiki_mgr = wiki.Xwiki_mgr(); + int domain_tid = wiki.Domain_tid(); + xwiki_mgr.Add_by_csv(Csv__manual); // adds manual xwikis that should exist in all wikis; EX: 'commons', 'wikidata', 'oldwikisource', 'wmf' + switch (domain_tid) { + case Xow_domain_tid_.Tid__wikipedia: case Xow_domain_tid_.Tid__wiktionary: case Xow_domain_tid_.Tid__wikisource: case Xow_domain_tid_.Tid__wikivoyage: + case Xow_domain_tid_.Tid__wikiquote: case Xow_domain_tid_.Tid__wikibooks: case Xow_domain_tid_.Tid__wikiversity: case Xow_domain_tid_.Tid__wikinews: + xwiki_mgr.Add_by_sitelink_mgr(); // lang: EX: [[fr:]] -> fr.wikipedia.org + xwiki_mgr.Add_by_csv(Csv__peers__lang); // peer: EX: [[wikt]] -> en.wiktionary.org + break; + case Xow_domain_tid_.Tid__commons: case Xow_domain_tid_.Tid__wikidata: + case Xow_domain_tid_.Tid__wikimedia: case Xow_domain_tid_.Tid__species: case Xow_domain_tid_.Tid__meta: + case Xow_domain_tid_.Tid__incubator: case Xow_domain_tid_.Tid__wikimania: case Xow_domain_tid_.Tid__wikisource_org: + case Xow_domain_tid_.Tid__mediawiki: case Xow_domain_tid_.Tid__wmfblog: + case Xow_domain_tid_.Tid__home: case Xow_domain_tid_.Tid__other: + xwiki_mgr.Add_by_sitelink_mgr(Xow_domain_tid_.Tid__wikipedia); // lang: hardcode to wikipedia; EX: "[[en:]] -> "en.wikipedia.org" + xwiki_mgr.Add_by_csv(Csv__peers__english); // peer: hardcode to english + break; + } + + // wikivoyage + switch (domain_tid) { + case Xow_domain_tid_.Tid__wikivoyage: case Xow_domain_tid_.Tid__home: // NOTE: home needed for diagnostic; DATE:2015-10-13 + xwiki_mgr.Add_by_csv(Csv__wikivoyage); + break; + } + + // if simplewiki, add "w" -> "enwiki" + if (Bry_.Eq(wiki.Domain_bry(), Xow_domain_itm_.Bry__simplewiki)) + xwiki_mgr.Add_by_csv(Csv__enwiki); + } + private static final byte[] + Csv__manual = Bry_.new_a7(String_.Concat_lines_nl_skip_last + ( "1|commons;c|commons.wikimedia.org|Commons" + , "1|m;metawikipedia|meta.wikipedia.org" + , "1|species;wikispecies|species.wikimedia.org" + , "1|d;wikidata|www.wikidata.org" + , "1|mw;mediawikiwiki|www.mediawiki.org" + , "1|wmf;wikimedia;foundation|foundation.wikimedia.org" + , "1|incubator|incubator.wikimedia.org" + , "1|wikimania|wikimania.wikimedia.org" + , "1|sourceswiki|wikisource.org" + , "0|oldwikisource|https://wikisource.org/wiki/~{0}|Old Wikisoure" + , "0|mail|https://lists.wikimedia.org/mailman/listinfo/~{0}|Wikitech Mailing List" + )) + , Csv__peers__lang = Bry_.new_a7(String_.Concat_lines_nl_skip_last + ( "2|w;wikipedia|wikipedia" + , "2|wikt;wiktionary|wiktionary" + , "2|s;wikisource|wikisource" + , "2|b;wikibooks|wikibooks" + , "2|v;wikiversity|wikiversity" + , "2|q;wikiquote|wikiquote" + , "2|n;wikinews|wikinews" + , "2|voy;wikivoyage|wikivoyage" + )) + , Csv__peers__english = Bry_.new_a7(String_.Concat_lines_nl_skip_last + ( "1|w|en.wikipedia.org" + , "1|wikt|en.wiktionary.org" + , "1|s|en.wikisource.org" + , "1|b|en.wikibooks.org" + , "1|v|en.wikiversity.org" + , "1|q|en.wikiquote.org" + , "1|n|en.wikinews.org" + , "1|voy|en.wikivoyage.org" + )) + , Csv__wikivoyage = Bry_.new_a7(String_.Concat_lines_nl_skip_last + ( "0|commons|commons.wikimedia.org|Wikimedia Commons" + , "2|wikipedia|wikipedia|Wikipedia" + , "0|dmoz|http://www.dmoz.org/~{0}|DMOZ" + )) + , Csv__enwiki = Bry_.new_a7("2|w|wikipedia") + ; + private static final byte[] + Arg0_xo = Bry_.new_a7("~{0}") + , Arg0_wm = Bry_.new_a7("$1") + ; +} diff --git a/400_xowa/src/gplx/xowa/bldrs/wms/revs/Xowm_rev_wkr__meta__wm.java b/400_xowa/src/gplx/xowa/bldrs/wms/revs/Xowm_rev_wkr__meta__wm.java index 3440b9986..82689f0f5 100644 --- a/400_xowa/src/gplx/xowa/bldrs/wms/revs/Xowm_rev_wkr__meta__wm.java +++ b/400_xowa/src/gplx/xowa/bldrs/wms/revs/Xowm_rev_wkr__meta__wm.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,41 +13,52 @@ 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.bldrs.wms.revs; import gplx.*; import gplx.xowa.*; import gplx.xowa.bldrs.*; import gplx.xowa.bldrs.wms.*; -import gplx.langs.jsons.*; import gplx.core.net.*; -class Xowm_rev_wkr__meta__wm implements Xowm_rev_wkr__meta { - private final Json_parser json_parser = new Json_parser(); - private final Xowm_json_parser__page json_page_parser = new Xowm_json_parser__page(); - private final Bry_bfr tmp_bfr = Bry_bfr_.New_w_size(255); - private final Wmapi_itm__pge tmp_pge = new Wmapi_itm__pge(); - public Xowm_rev_wkr__meta__wm() { - tmp_pge.Rvn_ary_(new Wmapi_itm__rvn()); - } - public Gfo_inet_conn Inet_conn() {return inet_conn;} public void Inet_conn_(Gfo_inet_conn v) {this.inet_conn = v;} private Gfo_inet_conn inet_conn; - public void Fetch_meta(String domain_str, Ordered_hash hash, int bgn, int end) { - for (int i = bgn; i < end; ++i) { - Wmapi_itm__pge itm = (Wmapi_itm__pge)hash.Get_at(i); - if (i != bgn) tmp_bfr.Add_byte(Byte_ascii.Pipe); - tmp_bfr.Add(itm.Page_ttl()); - } - byte[] json = inet_conn.Download_as_bytes_or_null(Xowm_api_mgr.Bld_api_url(domain_str, "action=query&prop=revisions&rvprop=size|ids|timestamp|user|comment&format=json&rawcontinue=titles=" + tmp_bfr.To_str_and_clear())); - Parse_doc(hash, json); - } - private void Parse_doc(Ordered_hash hash, byte[] json) { - Json_doc jdoc = json_parser.Parse(json); - Json_nde pages_nde = Json_nde.cast(jdoc.Get_grp_many(Jpath__query_pages)); - int len = pages_nde.Len(); - for (int i = 0; i < len; ++i) { - Parse_page(hash, pages_nde.Get_at_as_kv(i).Val_as_nde()); - } - } - private void Parse_page(Ordered_hash hash, Json_nde page_nde) { - json_page_parser.Parse("update", tmp_pge, page_nde); // have to pass tmp_pge, b/c don't know which itm is in hash - Wmapi_itm__pge hash_itm = (Wmapi_itm__pge)hash.Get_by(tmp_pge.Page_ttl()); if (hash_itm == null) return; - hash_itm.Init_id(tmp_pge.Page_id()); - Wmapi_itm__rvn tmp_rvn = tmp_pge.Rvn_itm_last(); - hash_itm.Rvn_ary_(new Wmapi_itm__rvn()); - hash_itm.Rvn_itm_last().Init(tmp_rvn.Rvn_id(), tmp_rvn.Rvn_len(), tmp_rvn.Rvn_time(), tmp_rvn.Rvn_user(), tmp_rvn.Rvn_note()); - } - private static final byte[][] Jpath__query_pages = Bry_.Ary("query", "pages"); -} +package gplx.xowa.bldrs.wms.revs; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Byte_ascii; +import gplx.Ordered_hash; +import gplx.core.net.Gfo_inet_conn; +import gplx.langs.jsons.Json_doc; +import gplx.langs.jsons.Json_nde; +import gplx.langs.jsons.Json_parser; +import gplx.xowa.bldrs.wms.Xowm_api_mgr; + +class Xowm_rev_wkr__meta__wm implements Xowm_rev_wkr__meta { + private final Json_parser json_parser = new Json_parser(); + private final Xowm_json_parser__page json_page_parser = new Xowm_json_parser__page(); + private final Bry_bfr tmp_bfr = Bry_bfr_.New_w_size(255); + private final Wmapi_itm__pge tmp_pge = new Wmapi_itm__pge(); + public Xowm_rev_wkr__meta__wm() { + tmp_pge.Rvn_ary_(new Wmapi_itm__rvn()); + } + public Gfo_inet_conn Inet_conn() {return inet_conn;} public void Inet_conn_(Gfo_inet_conn v) {this.inet_conn = v;} private Gfo_inet_conn inet_conn; + public void Fetch_meta(String domain_str, Ordered_hash hash, int bgn, int end) { + for (int i = bgn; i < end; ++i) { + Wmapi_itm__pge itm = (Wmapi_itm__pge)hash.Get_at(i); + if (i != bgn) tmp_bfr.Add_byte(Byte_ascii.Pipe); + tmp_bfr.Add(itm.Page_ttl()); + } + byte[] json = inet_conn.Download_as_bytes_or_null(Xowm_api_mgr.Bld_api_url(domain_str, "action=query&prop=revisions&rvprop=size|ids|timestamp|user|comment&format=json&rawcontinue=titles=" + tmp_bfr.To_str_and_clear())); + Parse_doc(hash, json); + } + private void Parse_doc(Ordered_hash hash, byte[] json) { + Json_doc jdoc = json_parser.Parse(json); + Json_nde pages_nde = Json_nde.Cast(jdoc.Get_grp_many(Jpath__query_pages)); + int len = pages_nde.Len(); + for (int i = 0; i < len; ++i) { + Parse_page(hash, pages_nde.Get_at_as_kv(i).Val_as_nde()); + } + } + private void Parse_page(Ordered_hash hash, Json_nde page_nde) { + json_page_parser.Parse("update", tmp_pge, page_nde); // have to pass tmp_pge, b/c don't know which itm is in hash + Wmapi_itm__pge hash_itm = (Wmapi_itm__pge)hash.Get_by(tmp_pge.Page_ttl()); if (hash_itm == null) return; + hash_itm.Init_id(tmp_pge.Page_id()); + Wmapi_itm__rvn tmp_rvn = tmp_pge.Rvn_itm_last(); + hash_itm.Rvn_ary_(new Wmapi_itm__rvn()); + hash_itm.Rvn_itm_last().Init(tmp_rvn.Rvn_id(), tmp_rvn.Rvn_len(), tmp_rvn.Rvn_time(), tmp_rvn.Rvn_user(), tmp_rvn.Rvn_note()); + } + private static final byte[][] Jpath__query_pages = Bry_.Ary("query", "pages"); +} diff --git a/400_xowa/src/gplx/xowa/langs/msgs/Xow_msg_mgr.java b/400_xowa/src/gplx/xowa/langs/msgs/Xow_msg_mgr.java index c761b79c2..51f9e7afc 100644 --- a/400_xowa/src/gplx/xowa/langs/msgs/Xow_msg_mgr.java +++ b/400_xowa/src/gplx/xowa/langs/msgs/Xow_msg_mgr.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,108 +13,122 @@ 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.langs.msgs; import gplx.*; import gplx.xowa.*; import gplx.xowa.langs.*; -import gplx.core.brys.fmtrs.*; -import gplx.xowa.addons.htmls.sidebars.*; -public class Xow_msg_mgr implements Gfo_invk { - private final Xow_wiki wiki; private Xol_lang_itm lang; private final Xol_msg_mgr msg_mgr; - private final Bry_fmtr tmp_fmtr = Bry_fmtr.New__tmp(); - public Xow_msg_mgr(Xow_wiki wiki, Xol_lang_itm lang) { - this.wiki = wiki; - this.lang = lang; - this.msg_mgr = new Xol_msg_mgr(wiki, false); - } - public void Clear() {msg_mgr.Clear();} - public void Lang_(Xol_lang_itm v) { - this.lang = v; - this.Clear(); - } - public byte[] Val_by_id_args(int id, Object... args) {return Val_by_id_priv(id, args);} - public byte[] Val_by_id(int id) {return Val_by_id_priv(id, null);} - private byte[] Val_by_id_priv(int id, Object[] args) { - Xol_msg_itm itm = msg_mgr.Itm_by_id_or_null(id); - if (itm == null) - itm = lang.Msg_mgr().Itm_by_id_or_null(id); - Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); - byte[] rv = Val_by_itm(tmp_bfr, itm, args); - tmp_bfr.Mkr_rls(); - return rv; - } - public Xol_msg_itm Get_or_make(byte[] key) {return msg_mgr.Itm_by_key_or_new(key);} - public Xol_msg_itm Get_or_null(byte[] key) {return msg_mgr.Itm_by_key_or_null(key);} - public Xol_msg_itm Find_or_null(byte[] key) { - Xol_msg_itm itm = msg_mgr.Itm_by_key_or_null(key); - if (itm == null) { - Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); - itm = Xol_msg_mgr_.Get_msg_itm(tmp_bfr, wiki, lang, key); - if (itm.Defined_in_none()) itm = null; - tmp_bfr.Mkr_rls(); - } - return itm; - } - public byte[] Val_by_key_args(byte[] key, Object... args) {return Val_by_key(key, args);} - public byte[] Val_by_key_obj(String key) {return Val_by_key(Bry_.new_u8(key), null);} - public byte[] Val_by_key_obj(byte[] key) {return Val_by_key(key, null);} - private byte[] Val_by_key(byte[] key, Object[] args) { - Xol_msg_itm itm = msg_mgr.Itm_by_key_or_null(key); - Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); - if (itm == null) - itm = Xol_msg_mgr_.Get_msg_itm(tmp_bfr, wiki, lang, key); - if (itm.Defined_in_none()) { - tmp_bfr.Mkr_rls(); - return Bry_.Empty; - } - byte[] rv = Val_by_itm(tmp_bfr, itm, args); - tmp_bfr.Mkr_rls(); - return rv; - } - public byte[] Val_by_itm(Bry_bfr tmp_bfr, Xol_msg_itm itm, Object[] args) { - byte[] rv = itm.Val(); - if (args != null) rv = itm.Fmt_tmp(tmp_bfr, tmp_fmtr, rv, args); - if (itm.Has_tmpl_txt()) rv = wiki.Wtxt__expand_tmpl(rv); - return rv; - } - public byte[] Val_html_accesskey_and_title(byte[] id) { - Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512(); - byte[] rv = Val_html_accesskey_and_title(id, bfr, null); - bfr.Mkr_rls(); - return rv; - } - public byte[] Val_html_accesskey_and_title(byte[] id, Bry_bfr bfr, Xoh_sidebar_itm itm) { - byte[] tooltip_key = Bry_.Add(CONST_prefix_tooltip, id); - byte[] tooltip_val = Val_by_key_obj(tooltip_key); - boolean tooltip_found = Bry_.Len_gt_0(tooltip_val); - byte[] accesskey_key = Bry_.Empty, accesskey_val = Bry_.Empty; - boolean accesskey_found = false; - if (tooltip_found) { - accesskey_key = Bry_.Add(CONST_prefix_accesskey, id); - accesskey_val = Val_by_key_obj(accesskey_key); - accesskey_found = Bry_.Len_gt_0(accesskey_val); - } - if (accesskey_found) - bfr.Add(CONST_atr_accesskey).Add(accesskey_val).Add_byte(Byte_ascii.Quote); - bfr.Add(CONST_atr_title).Add(tooltip_found ? tooltip_val : Bry_.Empty); // NOTE: if tooltip not found, make blank; don't bother showing tooltip_key - if (accesskey_found) - bfr.Add_byte(Byte_ascii.Space).Add_byte(Byte_ascii.Brack_bgn).Add(accesskey_val).Add_byte(Byte_ascii.Brack_end); - bfr.Add_byte(Byte_ascii.Quote); - byte[] rv = bfr.To_bry_and_clear(); - if (itm == null) - return rv; - else { - itm.Init_by_title_and_accesskey(tooltip_val, accesskey_val, rv); - return null; - } - } private static final byte[] CONST_prefix_tooltip = Bry_.new_a7("tooltip-"), CONST_prefix_accesskey = Bry_.new_a7("accesskey-"), CONST_atr_title = Bry_.new_a7(" title=\""), CONST_atr_accesskey = Bry_.new_a7(" accesskey=\""); - public Xol_msg_itm Set(String key_str, String val_str) { // TEST - Xol_msg_itm msg_itm = this.Get_or_make(Bry_.new_u8(key_str)); - msg_itm.Atrs_set(Bry_.new_u8(val_str), false, false); - msg_itm.Defined_in_(Xol_msg_itm.Defined_in__lang); - return msg_itm; - } - - public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { - if (ctx.Match(k, Invk_get)) return this.Val_by_key_obj(m.ReadBry("v")); - else if (ctx.Match(k, Invk_get_html_accesskey_and_title)) return this.Val_html_accesskey_and_title(m.ReadBry("v")); - else return Gfo_invk_.Rv_unhandled; - } private static final String Invk_get = "get", Invk_get_html_accesskey_and_title = "get_html_accesskey_and_title"; -} +package gplx.xowa.langs.msgs; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Byte_ascii; +import gplx.GfoMsg; +import gplx.Gfo_invk; +import gplx.Gfo_invk_; +import gplx.GfsCtx; +import gplx.core.brys.fmtrs.Bry_fmtr; +import gplx.xowa.Xow_wiki; +import gplx.xowa.addons.htmls.sidebars.Xoh_sidebar_itm; +import gplx.xowa.langs.Xol_lang_itm; + +public class Xow_msg_mgr implements Gfo_invk { + private final Xow_wiki wiki; + private final Xol_msg_mgr msg_mgr; + private Xol_lang_itm lang; + private final Bry_fmtr tmp_fmtr = Bry_fmtr.New__tmp(); + public Xow_msg_mgr(Xow_wiki wiki, Xol_lang_itm lang) { + this.wiki = wiki; + this.lang = lang; + this.msg_mgr = new Xol_msg_mgr(wiki, false); + } + public void Clear() {msg_mgr.Clear();} + public void Lang_(Xol_lang_itm v) { + this.lang = v; + this.Clear(); + } + public byte[] Val_by_id_args(int id, Object... args) {return Val_by_id_priv(id, args);} + public byte[] Val_by_id(int id) {return Val_by_id_priv(id, null);} + private byte[] Val_by_id_priv(int id, Object[] args) { + Xol_msg_itm itm = msg_mgr.Itm_by_id_or_null(id); + if (itm == null) + itm = lang.Msg_mgr().Itm_by_id_or_null(id); + Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); + byte[] rv = Val_by_itm(tmp_bfr, itm, args); + tmp_bfr.Mkr_rls(); + return rv; + } + public Xol_msg_itm Get_or_make(byte[] key) {return msg_mgr.Itm_by_key_or_new(key);} + public Xol_msg_itm Get_or_null(byte[] key) {return msg_mgr.Itm_by_key_or_null(key);} + public Xol_msg_itm Find_or_null(byte[] key) { + Xol_msg_itm itm = msg_mgr.Itm_by_key_or_null(key); + if (itm == null) { + Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); + itm = Xol_msg_mgr_.Get_msg_itm(tmp_bfr, wiki, lang, key); + if (itm.Defined_in_none()) itm = null; + tmp_bfr.Mkr_rls(); + } + return itm; + } + public byte[] Val_by_key_args(byte[] key, Object... args) {return Val_by_key(key, args);} + public byte[] Val_by_key_obj(String key) {return Val_by_key(Bry_.new_u8(key), null);} + public byte[] Val_by_key_obj(byte[] key) {return Val_by_key(key, null);} + private byte[] Val_by_key(byte[] key, Object[] args) { + Xol_msg_itm itm = msg_mgr.Itm_by_key_or_null(key); + Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); + if (itm == null) + itm = Xol_msg_mgr_.Get_msg_itm(tmp_bfr, wiki, lang, key); + if (itm.Defined_in_none()) { + tmp_bfr.Mkr_rls(); + return Bry_.Empty; + } + byte[] rv = Val_by_itm(tmp_bfr, itm, args); + tmp_bfr.Mkr_rls(); + return rv; + } + public byte[] Val_by_itm(Bry_bfr tmp_bfr, Xol_msg_itm itm, Object[] args) { + byte[] rv = itm.Val(); + if (args != null) rv = itm.Fmt_tmp(tmp_bfr, tmp_fmtr, rv, args); + if (itm.Has_tmpl_txt()) rv = wiki.Wtxt__expand_tmpl(rv); + return rv; + } + public byte[] Val_html_accesskey_and_title(String id) {return Val_html_accesskey_and_title(Bry_.new_u8(id));} + public byte[] Val_html_accesskey_and_title(byte[] id) { + Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512(); + byte[] rv = Val_html_accesskey_and_title(id, bfr, null); + bfr.Mkr_rls(); + return rv; + } + public byte[] Val_html_accesskey_and_title(byte[] id, Bry_bfr bfr, Xoh_sidebar_itm itm) { + byte[] tooltip_key = Bry_.Add(CONST_prefix_tooltip, id); + byte[] tooltip_val = Val_by_key_obj(tooltip_key); + boolean tooltip_found = Bry_.Len_gt_0(tooltip_val); + byte[] accesskey_key = Bry_.Empty, accesskey_val = Bry_.Empty; + boolean accesskey_found = false; + if (tooltip_found) { + accesskey_key = Bry_.Add(CONST_prefix_accesskey, id); + accesskey_val = Val_by_key_obj(accesskey_key); + accesskey_found = Bry_.Len_gt_0(accesskey_val); + } + if (accesskey_found) + bfr.Add(CONST_atr_accesskey).Add(accesskey_val).Add_byte(Byte_ascii.Quote); + bfr.Add(CONST_atr_title).Add(tooltip_found ? tooltip_val : Bry_.Empty); // NOTE: if tooltip not found, make blank; don't bother showing tooltip_key + if (accesskey_found) + bfr.Add_byte(Byte_ascii.Space).Add_byte(Byte_ascii.Brack_bgn).Add(accesskey_val).Add_byte(Byte_ascii.Brack_end); + bfr.Add_byte(Byte_ascii.Quote); + byte[] rv = bfr.To_bry_and_clear(); + if (itm == null) + return rv; + else { + itm.Init_by_title_and_accesskey(tooltip_val, accesskey_val, rv); + return null; + } + } private static final byte[] CONST_prefix_tooltip = Bry_.new_a7("tooltip-"), CONST_prefix_accesskey = Bry_.new_a7("accesskey-"), CONST_atr_title = Bry_.new_a7(" title=\""), CONST_atr_accesskey = Bry_.new_a7(" accesskey=\""); + public Xol_msg_itm Set(String key_str, String val_str) { // TEST + Xol_msg_itm msg_itm = this.Get_or_make(Bry_.new_u8(key_str)); + msg_itm.Atrs_set(Bry_.new_u8(val_str), false, false); + msg_itm.Defined_in_(Xol_msg_itm.Defined_in__lang); + return msg_itm; + } + + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_get)) return this.Val_by_key_obj(m.ReadBry("v")); + else if (ctx.Match(k, Invk_get_html_accesskey_and_title)) return this.Val_html_accesskey_and_title(m.ReadBry("v")); + else return Gfo_invk_.Rv_unhandled; + } private static final String Invk_get = "get", Invk_get_html_accesskey_and_title = "get_html_accesskey_and_title"; +} diff --git a/400_xowa/src/gplx/xowa/parsers/tmpls/Xot_invk_tkn.java b/400_xowa/src/gplx/xowa/parsers/tmpls/Xot_invk_tkn.java index bffb95b21..5b3c09d1a 100644 --- a/400_xowa/src/gplx/xowa/parsers/tmpls/Xot_invk_tkn.java +++ b/400_xowa/src/gplx/xowa/parsers/tmpls/Xot_invk_tkn.java @@ -95,6 +95,7 @@ public class Xot_invk_tkn extends Xop_tkn_itm_base implements Xot_invk { boolean subst_found = false; boolean name_had_subst = false; boolean template_prefix_found = false; +// byte tmpl_case_match = wiki.Ns_mgr().Ns_template().Case_match(); // tmpl_name does not exist in db; may be dynamic, subst, transclusion, etc.. if (defn == Xot_defn_.Null) { @@ -285,6 +286,29 @@ public class Xot_invk_tkn extends Xop_tkn_itm_base implements Xot_invk { break; case Xot_defn_.Tid_func: try { +/* System.out.println(String_.new_a7(caller.Frame_ttl())); + if (true) {//(caller.Frame_ttl().length == 22 && caller.Frame_ttl()[21] == 't') { + int alen = caller.Args_len(); + String s = ""; + for (int i = 0; i < alen; i++) { + Arg_nde_tkn atkn = caller.Args_get_by_idx(i); + if (atkn.KeyTkn_exists()) { + Arg_itm_tkn argtkn = atkn.Key_tkn(); + s += String_.new_a7(argtkn.Dat_ary()) + ":"; + } + else + s += String.valueOf(i); + s += String_.new_a7(atkn.Val_tkn().Dat_ary()) + "\n"; + } + System.out.println(s); + int a=1; + } +if (Bry_.Eq(caller.Frame_ttl(), Bry_.new_a7("Template:BookCat/core"))) { + return true; + //int a=1; +} +*/ +//System.out.println(String_.new_u8(caller.Frame_ttl())); Xot_invk_tkn_.Eval_func(ctx, src, caller, this, bfr, defn, argx_ary); rv = true; } catch (Exception e) { @@ -306,11 +330,13 @@ public class Xot_invk_tkn extends Xop_tkn_itm_base implements Xot_invk { Bry_bfr rslt_bfr = wiki.Utl__bfr_mkr().Get_k004(); try { Xopg_tmpl_prepend_mgr prepend_mgr = ctx.Page().Tmpl_prepend_mgr().Bgn(bfr); - rv = defn_tmpl.Tmpl_evaluate(Xop_ctx.New__sub(wiki, ctx, ctx.Page()), invk_tmpl, rslt_bfr); // create new ctx so __NOTOC__ only applies to template, not page; PAGE:de.w:13._Jahrhundert DATE:2017-06-17 + //rv = defn_tmpl.Tmpl_evaluate(Xop_ctx.New__sub(wiki, ctx, ctx.Page()), invk_tmpl, rslt_bfr); // create new ctx so __NOTOC__ only applies to template, not page; PAGE:de.w:13._Jahrhundert DATE:2017-06-17 + rv = defn_tmpl.Tmpl_evaluate(ctx, invk_tmpl, rslt_bfr); prepend_mgr.End(ctx, bfr, rslt_bfr.Bfr(), rslt_bfr.Len(), Bool_.Y); if (name_had_subst) { // current invk had "subst:"; parse incoming invk again to remove effects of subst; PAGE:pt.w:Argentina DATE:2014-09-24 byte[] tmp_src = rslt_bfr.To_bry_and_clear(); - rslt_bfr.Add(wiki.Parser_mgr().Main().Expand_tmpl(tmp_src)); // this could be cleaner / more optimized + if (tmp_src.length != 0) + rslt_bfr.Add(wiki.Parser_mgr().Main().Expand_tmpl(tmp_src)); // this could be cleaner / more optimized } bfr.Add_bfr_and_clear(rslt_bfr); trace.Trace_end(trg_bgn, bfr); @@ -341,7 +367,7 @@ public class Xot_invk_tkn extends Xop_tkn_itm_base implements Xot_invk { Xoa_ttl page_ttl = Xoa_ttl.Parse(wiki, name_ary); if (page_ttl == null) return false; // ttl not valid; EX: {{:[[abc]]}} byte[] transclude_src = null; if (page_ttl.Ns().Id_is_tmpl()) { // ttl is template; check tmpl_regy first before going to data_mgr - Xot_defn_tmpl tmpl = (Xot_defn_tmpl)wiki.Cache_mgr().Defn_cache().Get_by_key(page_ttl.Page_db()); + Xot_defn_tmpl tmpl = (Xot_defn_tmpl)wiki.Cache_mgr().Defn_cache().Get_by_key(page_ttl.Page_db(), wiki.Ns_mgr().Ns_template().Case_match()); if (tmpl != null) transclude_src = tmpl.Data_raw(); } if (transclude_src == null && ctx.Tmpl_load_enabled()) { // ttl is template not in cache, or some other ns; do load @@ -378,7 +404,7 @@ public class Xot_invk_tkn extends Xop_tkn_itm_base implements Xot_invk { Xot_defn_tmpl transclude_tmpl = null; switch (page_ttl.Ns().Id()) { case Xow_ns_.Tid__template: // ttl is template not in cache, or some other ns; do load - Xot_defn_tmpl tmpl = (Xot_defn_tmpl)wiki.Cache_mgr().Defn_cache().Get_by_key(page_ttl.Page_db()); + Xot_defn_tmpl tmpl = (Xot_defn_tmpl)wiki.Cache_mgr().Defn_cache().Get_by_key(page_ttl.Page_db(), wiki.Ns_mgr().Ns_template().Case_match()); if (tmpl != null) { if (tmpl.Root() == null) tmpl.Parse_tmpl(ctx); transclude_tmpl = tmpl; diff --git a/400_xowa/src/gplx/xowa/wikis/caches/Xow_defn_cache.java b/400_xowa/src/gplx/xowa/wikis/caches/Xow_defn_cache.java index 8d948f2d8..4cc3a070d 100644 --- a/400_xowa/src/gplx/xowa/wikis/caches/Xow_defn_cache.java +++ b/400_xowa/src/gplx/xowa/wikis/caches/Xow_defn_cache.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,24 +13,33 @@ 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.wikis.caches; import gplx.*; import gplx.xowa.*; import gplx.xowa.wikis.*; -import gplx.core.intls.*; import gplx.core.caches.*; -import gplx.xowa.langs.*; -import gplx.xowa.wikis.nss.*; -import gplx.xowa.parsers.tmpls.*; -public class Xow_defn_cache { // stores compiled Xot_defn - private final Xol_lang_itm lang; // needed to lowercase names; - private final Bry_bfr upper_1st_bfr = Bry_bfr_.Reset(255); - private final Gfo_cache_mgr cache = new Gfo_cache_mgr().Max_size_(64 * 1024 * 1024).Reduce_by_(32 * 1024 * 1024); - public Xow_defn_cache(Xol_lang_itm lang) {this.lang = lang;} - public Xot_defn Get_by_key(byte[] name) {return (Xot_defn)cache.Get_by_key(name);} - public void Free_mem_all() {cache.Clear();} - public void Add(Xot_defn defn, byte case_match) { - byte[] name = defn.Name(); - cache.Add_replace(name, defn, defn.Cache_size()); - if (case_match == Xow_ns_case_.Tid__1st) { - name = lang.Case_mgr().Case_build_1st_upper(upper_1st_bfr, name, 0, name.length); - cache.Add_replace(name, defn, 0); - } - } -} +package gplx.xowa.wikis.caches; + +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.core.caches.Gfo_cache_mgr; +import gplx.xowa.langs.Xol_lang_itm; +import gplx.xowa.parsers.tmpls.Xot_defn; +import gplx.xowa.wikis.nss.Xow_ns_case_; + +public class Xow_defn_cache { + private final Xol_lang_itm lang; // needed to lowercase names; + private final Bry_bfr upper_1st_bfr = Bry_bfr_.Reset(255); + private final Gfo_cache_mgr cache = new Gfo_cache_mgr().Max_size_(64 * 1024 * 1024).Reduce_by_(32 * 1024 * 1024); + public Xow_defn_cache(Xol_lang_itm lang) {this.lang = lang;} + public Xot_defn Get_by_key(byte[] name) {return (Xot_defn)cache.Get_by_key(name);} + public Xot_defn Get_by_key(byte[] name, byte case_match) { + if (case_match == Xow_ns_case_.Tid__1st) + name = lang.Case_mgr().Case_build_1st_upper(upper_1st_bfr, name, 0, name.length); + return (Xot_defn)cache.Get_by_key(name); + } + public void Free_mem_all() {cache.Clear();} + public void Add(Xot_defn defn, byte case_match) { + byte[] name = defn.Name(); + cache.Add_replace(name, defn, defn.Cache_size()); + if (case_match == Xow_ns_case_.Tid__1st) { + name = lang.Case_mgr().Case_build_1st_upper(upper_1st_bfr, name, 0, name.length); + cache.Add_replace(name, defn, 0); + } + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/lst/Db_Section_list.java b/400_xowa/src/gplx/xowa/xtns/lst/Db_Section_list.java new file mode 100644 index 000000000..c7f8f7813 --- /dev/null +++ b/400_xowa/src/gplx/xowa/xtns/lst/Db_Section_list.java @@ -0,0 +1,403 @@ +/* search for '' +any + if end position before < +*/ +package gplx.xowa.xtns.lst; +import gplx.Bry_; +import gplx.List_adp; +import gplx.List_adp_; + +import gplx.xowa.Xoa_ttl; +import gplx.xowa.Xoae_page; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.parsers.Xop_ctx; +import gplx.xowa.parsers.Xop_root_tkn; +import gplx.xowa.parsers.Xop_parser; +import gplx.xowa.parsers.Xop_parser_; +import gplx.xowa.parsers.Xop_parser_tid_; +import gplx.xowa.parsers.Xop_tkn_mkr; +import gplx.xowa.parsers.tmpls.Xot_invk_temp; +import gplx.xowa.parsers.lnkis.files.Xop_file_logger_; +import gplx.xowa.parsers.tmpls.Xot_defn_tmpl; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Bool_; +import gplx.Hash_adp_bry; + +public class Db_Section_list { + private List_adp sects; + private List_adp heads; + private byte[] src; + private Xop_ctx ctx; + private Xop_ctx sub_ctx; + private Xowe_wiki wiki; + private Xoa_ttl ttl; + private byte[] ttl_bry; + private static final byte Include_between = 0, Include_to_eos = 1, Include_to_bos = 2; + public Db_Section_list(byte[] src, int langid, Xop_ctx ctx, Xop_ctx sub_ctx, Xoa_ttl ttl, byte[] ttl_bry) { + byte b; + this.src = src; + this.ctx = ctx; + this.wiki = ctx.Wiki(); + this.sub_ctx = sub_ctx; + this.ttl = ttl; + this.ttl_bry = ttl_bry; + int src_len = src.length; + int pos = 0; + int bgn, end, atr; + sects = List_adp_.New(); + begin_end keyword; + switch (langid) { + case 1: // german!!! + keyword = new DE_begin_end(); + break; + default: + keyword = new EN_begin_end(); + break; + } + while (pos < src_len) { + b = src[pos++]; + if (b == '<') { + if (pos + 10 < src_len && (src[pos] | 32) == 's' && (src[pos+1] | 32) == 'e' && (src[pos+2] | 32) == 'c' && (src[pos+3] | 32) == 't' && (src[pos+4] | 32) == 'i' && (src[pos+5] | 32) == 'o' && (src[pos+6] | 32) == 'n' && src[pos+7] == ' ') { + bgn = pos - 1; + pos += 8; + atr = pos; + while (pos < src_len) { + b = src[pos++]; + if (b == '>') + break; + } + if (pos == src_len) // no end found + break; + end = pos; + // now find a keyword + begin_end_result bg = keyword.Find(src, atr, end); + if (bg != null) { + sects.Add(new Section(src, bg.start, bg.type, bgn, end)); + } + } + } + else if (b == '\n') { // check for headers + if (pos + 10 < src_len && src[pos] == '=') { + int count = 1; + pos++; + while (pos < src_len) { + b = src[pos++]; + if (b != '=') + break; + count++; + } + // now find the next + if (b != '\n') { + int npos = pos; + while (npos < src_len) { + b = src[npos++]; + if (b == '\n') + break; + } + if (b == '\n') { + // now count any '=' backwards + int ncount = 0; + while (npos > pos) { + b = src[--npos]; + if (b == '=') + ncount++; + else + break; + } + if (ncount == count) { // we have a header + heads.Add(new Header(src, pos, npos, count)); + pos = npos + count; + } + } + } + } + } + } + } + public byte[] Include(byte[] from, byte[] to) { + if (to == Lst_pfunc_itm.Null_arg) { // no end arg; EX: {{#lst:page|bgn}}; NOTE: different than {{#lst:page|bgn|}} + if (from == Lst_pfunc_itm.Null_arg) { // no bgn arg; EX: {{#lst:page}} + return Compile3(src); + } + else // bgn exists; set end to bgn; EX: {{#lst:page|bgn}} is same as {{#lst:page|bgn|bgn}}; NOTE: {{#lst:page|bgn|}} means write from bgn to eos + to = from; + } + Bry_bfr bfr = Bry_bfr_.New(); + byte include_mode = Include_between; + if (Bry_.Len_eq_0(to)) + include_mode = Include_to_eos; + else if (Bry_.Len_eq_0(from)) + include_mode = Include_to_bos; + int bgn_pos = 0; boolean bgn_found = false; int src_page_bry_len = src.length; + int sections_len = sects.Count(); + for (int i = 0; i < sections_len; i++) { + Section sect = (Section)sects.Get_at(i); + byte section_tid = (byte)sect.type; + if (section_tid == begin_end_result.BEGIN && Matchkey(sect, from)) { + int sect_bgn_rhs = sect.end; + if (include_mode == Include_to_eos) { // write from cur to eos; EX: {{#lst:page|bgn|}} + bfr.Add_mid(src, sect_bgn_rhs, src_page_bry_len); + bgn_found = false; + break; + } + else { // bgn and end + if (!bgn_found) { // NOTE: !bgn_found to prevent "resetting" of dupe; EX: ab; should start from a not b + bgn_pos = sect_bgn_rhs; + bgn_found = true; + } + } + } + else if (section_tid == begin_end_result.END && Matchkey(sect, to)) { + int sect_end_lhs = sect.bgn; + if (include_mode == Include_to_bos) { // write from bos to cur; EX: {{#lst:page||end}} + bfr.Add_mid(src, 0, sect_end_lhs); + bgn_found = false; + break; + } + else { + if (bgn_found) { // NOTE: bgn_found to prevent writing from bos; EX: ab should not write anything + bfr.Add_mid(src, bgn_pos, sect_end_lhs); + bgn_found = false; + } + } + } + } + if (bgn_found) // bgn_found, but no end; write to end of page; EX: "a
    b" -> " b" + bfr.Add_mid(src, bgn_pos, src_page_bry_len); + + return Compile3(bfr.To_bry()); + } + public byte[] Exclude(byte[] sect_exclude, byte[] sect_replace) { + if (Bry_.Len_eq_0(sect_exclude)) { // no exclude arg; EX: {{#lstx:page}} or {{#lstx:page}} + return Compile3(src); // write all and exit + } + int sections_len = sects.Count(); + int bgn_pos = 0; + Bry_bfr bfr = Bry_bfr_.New(); + for (int i = 0; i < sections_len; i++) { + Section sect = (Section)sects.Get_at(i); + byte section_tid = (byte)sect.type; + if (section_tid == begin_end_result.BEGIN && Matchkey(sect, sect_exclude)) { + bfr.Add_mid(src, bgn_pos, sect.bgn); // write everything from bgn_pos up to exclude + } + else if (section_tid == begin_end_result.END && Matchkey(sect, sect_exclude)) { // exclude end found + if (sect_replace != null) + bfr.Add(sect_replace); // write replacement + bgn_pos = sect.end; // reset bgn_pos + } + } + bfr.Add_mid(src, bgn_pos, src.length); + return Compile3(bfr.To_bry()); + } + public byte[] Header(byte[] lhs_hdr, byte[] rhs_hdr) { + return Bry_.Empty; + } + private boolean Matchkey(Section sect, byte[] find) { + if (find == Lst_pfunc_itm.Null_arg) return false; + int pos = sect.keybgn; + int keylen = sect.keyend - pos; + int find_end = find.length; + if (find_end != keylen) return false; + for (int i = 0; i < find_end; i++) { + if (src[pos + i] != find[i]) return false; + } + return true; + } + // need ctx hence wiki and page + private byte[] Compile(byte[] page_bry) { + Xop_root_tkn xtn_root = null; + // set recursing flag + Xoae_page page = ctx.Page(); + Bry_bfr full_bfr = wiki.Utl__bfr_mkr().Get_m001(); + try { + wiki.Parser_mgr().Lst__recursing_(true); + Hash_adp_bry lst_page_regy = ctx.Lst_page_regy(); if (lst_page_regy == null) lst_page_regy = Hash_adp_bry.cs(); // SEE:NOTE:page_regy; DATE:2014-01-01 + page.Html_data().Indicators().Enabled_(Bool_.N); // disable b/c should not add to current page; PAGE:en.s:The_Parochial_System_(Wilberforce,_1838); DATE:2015-04-29 + xtn_root = Bld_root_nde(full_bfr, lst_page_regy, page_bry); // NOTE: this effectively reparses page twice; needed b/c of "if {| : ; # *, auto add new_line" which can build different tokens + } finally { + wiki.Parser_mgr().Lst__recursing_(false); + full_bfr.Mkr_rls(); + } + page.Html_data().Indicators().Enabled_(Bool_.Y); + if (xtn_root == null) return null; + //html_wtr.Write_tkn_to_html(bfr, ctx, hctx, xtn_root.Root_src(), xnde, Xoh_html_wtr.Sub_idx_null, xtn_root); + return null; + } + private byte[] Compile2(byte[] msg_val) { + Xowe_wiki wikie = (Xowe_wiki)wiki; + Xop_ctx sub_ctx = Xop_ctx.New__sub__reuse_page(wikie.Parser_mgr().Ctx()); + sub_ctx.Parse_tid_(Xop_parser_tid_.Tid__wtxt); + Xop_tkn_mkr tkn_mkr = sub_ctx.Tkn_mkr(); + Xop_root_tkn sub_root = tkn_mkr.Root(msg_val); + return wikie.Parser_mgr().Main().Expand_tmpl(sub_root, sub_ctx, tkn_mkr, msg_val); + } + private byte[] Compile3(byte[] sub_src) { + // parse page; note adding to stack to prevent circular recursions + if (!wiki.Parser_mgr().Tmpl_stack_add(ttl.Full_db())) return null; + Xot_defn_tmpl tmpl = wiki.Parser_mgr().Main().Parse_text_to_defn_obj(sub_ctx, sub_ctx.Tkn_mkr(), ttl.Ns(), ttl_bry, sub_src); // NOTE: parse as tmpl to ignore + wiki.Parser_mgr().Tmpl_stack_del(); // take template off stack; evaluate will never recurse, but will fail if ttl is still on stack; DATE:2014-03-10 + + // eval tmpl + Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_m001(); + try { + tmpl.Tmpl_evaluate(sub_ctx, Xot_invk_temp.New_root(ttl.Page_txt()), tmp_bfr); + sub_src = tmp_bfr.To_bry_and_clear(); + } finally { + tmp_bfr.Mkr_rls(); + } + return sub_src; + } + private Xop_root_tkn Bld_root_nde(Bry_bfr page_bfr, Hash_adp_bry lst_page_regy, byte[] wikitext) { + Xop_ctx tmp_ctx = Xop_ctx.New__sub__reuse_lst(wiki, ctx, lst_page_regy); + tmp_ctx.Page().Ttl_(ctx.Page().Ttl()); // NOTE: must set tmp_ctx.Ttl to ctx.Ttl; EX: Flatland and First World; DATE:2013-04-29 + tmp_ctx.Lnki().File_logger_(Xop_file_logger_.Noop); // NOTE: set file_wkr to null, else items will be double-counted + tmp_ctx.Parse_tid_(Xop_parser_tid_.Tid__defn); + Xop_parser tmp_parser = Xop_parser.new_(wiki, wiki.Parser_mgr().Main().Tmpl_lxr_mgr(), wiki.Parser_mgr().Main().Wtxt_lxr_mgr()); + Xop_root_tkn rv = tmp_ctx.Tkn_mkr().Root(wikitext); + tmp_parser.Parse_text_to_wdom(rv, tmp_ctx, tmp_ctx.Tkn_mkr(), wikitext, Xop_parser_.Doc_bgn_bos); + return rv; + } +} + +class Section { + public int keybgn; + public int keyend; + public int type; + public int bgn; + public int end; + Section(byte[] src, int keybgn, int type, int bgn, int end) { + this.type = type; + this.bgn = bgn; + this.end = end; + byte b = src[keybgn]; + if (b == '\'' || b == '"') + keybgn++; + keyend = end - 2; + while (keyend > bgn) { + b = src[keyend - 1]; + if (b != ' ' && b != '\t' && b != '\n') + break; + keyend--; + } + if (b == '\'' || b == '"') + keyend--; + this.keybgn = keybgn; + } +} +class Header { + public int bgn; + public int end; + public int level; + Header(byte[] src, int bgn, int end, int level) { + this.level = level; + byte b; + while (bgn < end) { + b = src[bgn]; + if (b == ' ' || b == '\t') + bgn++; + else + break; + } + this.bgn = bgn; + while (end > bgn) { + b = src[end - 1]; + if (b == ' ' || b == '\t') + bgn--; + else + break; + } + this.end = end; + } +} +interface begin_end { + begin_end_result Find(byte[] src, int bgn, int end); +} + +class EN_begin_end implements begin_end { + public begin_end_result Find(byte[] src, int bgn, int end) { + while (bgn < end) { + byte b = src[bgn++]; + switch (b) { + case 'b': + case 'B': + if ((src[bgn] | 32) == 'e' && (src[bgn+1] | 32) == 'g' && (src[bgn+2] | 32) == 'i' && (src[bgn+3] | 32) == 'n' && src[bgn+4] == '=') { + bgn += 5; + b = src[bgn]; + return new begin_end_result(begin_end_result.BEGIN, bgn); + } + break; + case 'e': + case 'E': + if ((src[bgn] | 32) == 'n' && (src[bgn+1] | 32) == 'd' && src[bgn+2] == '=') { + bgn += 3; + b = src[bgn]; + return new begin_end_result(begin_end_result.END, bgn); + } + break; + } + } + return null; + } +} +class DE_begin_end implements begin_end { + public begin_end_result Find(byte[] src, int bgn, int end) { + while (bgn < end) { + byte b = src[bgn++]; + switch (b) { + case 'b': + case 'B': + if ((src[bgn] | 32) == 'e' && (src[bgn+1] | 32) == 'g' && (src[bgn+2] | 32) == 'i' && (src[bgn+3] | 32) == 'n' && src[bgn+4] == '=') { + bgn += 5; + b = src[bgn]; + return new begin_end_result(begin_end_result.BEGIN, bgn); + } + break; + // End + // Ende + case 'e': + case 'E': + if ((src[bgn] | 32) == 'n' && (src[bgn+1] | 32) == 'd') { + if (src[bgn+2] == '=') { + bgn += 3; + } + else if ((src[bgn+2] | 32) == 'e' && src[bgn+3] == '=') { + bgn += 4; + } + else + break; + b = src[bgn]; + return new begin_end_result(begin_end_result.END, bgn); + } + break; + // Anfang + case 'a': + case 'A': + if ((src[bgn] | 32) == 'n' && (src[bgn+1] | 32) == 'f' && (src[bgn+2] | 32) == 'a' && (src[bgn+3] | 32) == 'n' && (src[bgn+4] | 32) == 'g' && src[bgn+5] == '=') { + bgn += 6; + b = src[bgn]; + return new begin_end_result(begin_end_result.END, bgn); + } + break; + } + } + return null; + } +} +class begin_end_result { + public static int BEGIN = 1; + public static int END = 2; + public int type; + public int start; + begin_end_result(int type, int start) { + this.type = type; + this.start = start; + } +} \ No newline at end of file diff --git a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func.java b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func.java index 04e64d508..42477b1df 100644 --- a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func.java +++ b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,201 +13,235 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.pagebanners; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import gplx.core.btries.*; import gplx.langs.mustaches.*; -import gplx.xowa.parsers.*; import gplx.xowa.parsers.tmpls.*; import gplx.xowa.xtns.pfuncs.*; import gplx.xowa.langs.kwds.*; import gplx.xowa.parsers.utils.*; import gplx.xowa.parsers.lnkis.*; import gplx.xowa.parsers.lnkis.files.*; -import gplx.xowa.files.*; -import gplx.xowa.htmls.core.htmls.*; import gplx.langs.htmls.encoders.*; -// REF.MW:https://github.com/wikimedia/mediawiki-extensions-WikidataPageBanner/blob/master/includes/WikidataPageBannerFunctions.php -// TODO.DATE:2019-11-11: calculate enableToc and hasPosition; -public class Pgbnr_func extends Pf_func_base { - @Override public int Id() {return Xol_kwd_grp_.Id_pagebanner;} - @Override public Pf_func New(int id, byte[] name) {return new Pgbnr_func().Name_(name);} - @Override public void Func_evaluate(Bry_bfr bfr, Xop_ctx ctx, Xot_invk caller, Xot_invk self, byte[] src) { // {{PAGEBANNER}} appears on page; WikidataPageBanner.hooks.php|addCustomBanner - Xowe_wiki wiki = ctx.Wiki(); Xoae_page page = ctx.Page(); - Pgbnr_xtn_mgr xtn_mgr = wiki.Xtn_mgr().Xtn_pgbnr(); - Pgbnr_cfg cfg = xtn_mgr.Cfg(); - Xoa_ttl ttl = page.Ttl(); - if (!cfg.Chk_pgbnr_allowed(ttl, wiki)) return; - byte[] tooltip = ttl.Page_txt(), title = ttl.Page_txt(), toc = Bry_.Empty, origin_x = Bry_.Empty; - boolean bottomtoc = false;; - double data_pos_x = 0, data_pos_y = 0; - List_adp icons_list = null; - Bry_bfr tmp_bfr = Bry_bfr_.New(); - int args_len = self.Args_len(); - Xop_func_arg_itm func_arg = new Xop_func_arg_itm(); - for (int i = 0; i < args_len; ++i) { - Arg_nde_tkn arg = self.Args_get_by_idx(i); - func_arg.Set(tmp_bfr, ctx, src, caller, self, arg); - byte[] key = func_arg.key; - byte[] val = func_arg.val; - if (key == Bry_.Empty) continue; // ignore blank args; EX:{{PAGEBANNER:A.png|\n|toc=yes}} - int tid = arg_hash.Get_as_int_or(key, -1); - if (tid == Arg__pgname) - tooltip = title = val; - if (tid == Arg__tooltip) // note that this overrides pgname above - tooltip = val; - if (tid == Arg__bottomtoc && Bry_.Eq(val, Bry__yes)) - bottomtoc = true; - if (tid == Arg__toc && Bry_.Eq(val, Bry__yes)) // REF.MW:addToc - toc = Bry_.Empty; // note that "" will be overwritten later by actual toc html - if ( tid == -1 // note that "icon-*" won't have a tid - && Bry_.Has_at_bgn(key, Bry__icon) // if (substr($key, 0, 5) === 'icon-') - && Bry_.Len(key) > 5 // if ( !isset( $iconname) ) - && Bry_.Len_gt_0(val) // if ( !isset( $$value ) ) - ) { // REF.MW:addIcons - tid = Arg__icon; - if (icons_list == null) icons_list = List_adp_.New(); - byte[] icon_key = Bry_.Mid(key, 5); - byte[] icon_name = Xop_sanitizer.Escape_cls(icon_key); - byte[] icon_title = icon_name; - Xoa_ttl icon_ttl = wiki.Ttl_parse(val); - byte[] icon_href = Bry__icon_href_dflt; - if (icon_ttl != null) { - icon_href = Bry_.Add(gplx.xowa.htmls.hrefs.Xoh_href_.Bry__wiki, icon_ttl.Page_db()); - icon_title = icon_ttl.Page_txt(); - } - icons_list.Add(new Pgbnr_icon(tmp_bfr, icon_name, icon_title, icon_href)); - } - if (tid == Arg__origin) { // REF.MW:addFocus - double tmp_data_pos_x = Double_.NaN, tmp_data_pos_y = Double_.NaN; - int comma_pos = Bry_find_.Find_fwd(val, Byte_ascii.Comma); - if (comma_pos != Bry_find_.Not_found) { - tmp_data_pos_x = Bry_.To_double_or(val, 0, comma_pos, Double_.NaN); - if (!Double_.IsNaN(tmp_data_pos_x)) { - if (tmp_data_pos_x >= -1 && tmp_data_pos_x <= 1) { - data_pos_x = tmp_data_pos_x; - origin_x = tmp_data_pos_x <= .25d ? Bry__origin_x__left : Bry__origin_x__right; - } - } - if (!Double_.IsNaN(tmp_data_pos_y)) { - if (tmp_data_pos_y >= -1 && tmp_data_pos_y <= 1) - data_pos_y = tmp_data_pos_y; - } - } - } - if (tid == -1) Gfo_usr_dlg_.Instance.Warn_many("", "", "unknown arg type; page=~{0} key=~{1} val=~{2}", page.Ttl().Full_db(), key, val); - } - - byte[] banner_name = Eval_argx(ctx, src, caller, self); - Xoa_ttl banner_ttl = wiki.Ttl_parse(banner_name); // NOTE: MW also creates title to auto-register page and image in imagelinks - if (banner_ttl == null) // if ttl is invalid, get it from wikidata; PAGE:en.v:Diving_the_Cape_Peninsula_and_False_Bay/Whale_Rock; DATE:2016-07-12 - banner_ttl = Get_wikidata_banner(wiki.Appe(), wiki, wiki.Xtn_mgr().Xtn_pgbnr().Cfg(), page.Ttl()); - if (banner_ttl == null) { // if ttl is still invalid, exit now else will fail with nullref below; PAGE:en.v:Peterborough (New Hampshire); DATE:2016-07-12 - Gfo_usr_dlg_.Instance.Warn_many("", "", "banner file is invalid; page=~{0} banner=~{1}", page.Url_bry_safe(), banner_name); - return; - } - Xof_file_itm banner_file_itm = File__make_tkn(ctx, Xop_file_logger_.Tid__pgbnr_main, banner_ttl, Xop_lnki_tkn.Width_null, Xop_lnki_tkn.Height_null); - - Pgbnr_itm itm = new Pgbnr_itm(); - itm.Init_from_wtxt(banner_ttl, banner_file_itm, tooltip, title, bottomtoc, toc, data_pos_x, data_pos_y, origin_x, icons_list == null ? Pgbnr_icon.Ary_empty : (Pgbnr_icon[])icons_list.To_ary_and_clear(Pgbnr_icon.class)); - page.Html_data().Xtn_pgbnr_(itm); - page.Html_data().Head_mgr().Itm__pgbnr().Enabled_y_(); // register css / js during parse stage - page.Wtxt().Toc().Flag__toc_(true); // NOTE: must mark toc_manual else will show 2nd TOC in edit mode; DATE:2016-07-10 - } - public static void Add_banner(Bry_bfr bfr, Xoae_page wpg, Xop_ctx ctx, Xoh_wtr_ctx hctx, Pgbnr_itm itm) { - Xowe_wiki wiki = ctx.Wiki(); Xoae_app app = wiki.Appe(); - Pgbnr_cfg cfg = wiki.Xtn_mgr().Xtn_pgbnr().Cfg(); if (!cfg.enabled) return; - Xoa_ttl ttl = wpg.Ttl(); - Xoa_ttl banner_ttl = null; byte[] banner_html = null; - if (itm != null) { // {{PAGEBANNER}} exists in wikitext - itm.Init_hdump(hctx.Mode_is_hdump()); - banner_ttl = itm.banner_ttl; - banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm); - if (banner_html == null) { // no banner; try again using title from wikidata; note that this should only happen if no banner_ttl or banner_ttl is invalid; EX:{{PAGEBANNER:|toc=yes}} - banner_ttl = Get_wikidata_banner(app, wiki, cfg, ttl); - banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm); - } - } - else if ( ttl.Ns().Id_is_main() // {{PAGEBANNER}} missing, but wiki is marked as enable_default_banner - && cfg.enable_default_banner - && cfg.Chk_pgbnr_allowed(ttl, wiki) - ) { - banner_ttl = Get_wikidata_banner(app, wiki, cfg, ttl); - if (banner_ttl == null) - banner_ttl = wiki.Ttl_parse(cfg.dflt_img_title); - Xof_file_itm banner_file_itm = File__make_tkn(ctx, Xop_file_logger_.Tid__pgbnr_main, banner_ttl, Xop_lnki_tkn.Width_null, Xop_lnki_tkn.Height_null); - itm = new Pgbnr_itm(); - itm.Init_from_wtxt(banner_ttl, banner_file_itm, Bry_.Empty, Bry_.Empty, false, Bry_.Empty, 0, 0, Bry_.Empty, Pgbnr_icon.Ary_empty); - itm.Init_hdump(hctx.Mode_is_hdump()); - banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm); - } - if (banner_html != null) { - bfr.Add(banner_html); - } - } - public static byte[] Get_banner_html(Xowe_wiki wiki, Xop_ctx ctx, Xoh_wtr_ctx hctx, Pgbnr_cfg cfg, Xoa_ttl banner_ttl, Pgbnr_itm itm) { - byte[][] urls = Get_standard_size_urls(wiki, cfg, banner_ttl); if (urls == null) return null; - Bry_bfr tmp_bfr = Bry_bfr_.New(); - int urls_len = urls.length; - int[] sizes = cfg.standard_sizes; - for (int i = 0; i < urls_len; ++i) { - int size = sizes[i]; - if (i != 0) tmp_bfr.Add_byte_comma(); - tmp_bfr.Add(urls[i]).Add_byte_space().Add_int_variable(size).Add_byte(Byte_ascii.Ltr_w); // REF.MW: $srcset[] = "$url {$size}w"; - } - byte[] srcset = tmp_bfr.To_bry_and_clear(); - byte[] banner_url = itm.banner_img_src != null ? itm.banner_img_src : urls.length == 0 ? Bry_.Empty : urls[urls_len - 1]; // gets largest url - Xof_file_itm banner_file_itm = itm.banner_file_itm; - int max_width = banner_file_itm.Orig_w(); // $file = wfFindFile( banner_file ); $options['max_width'] = $file->getWidth(); - // Provide information to the logic-less template about whether it is a panorama or not. - boolean isPanorama = banner_file_itm.Orig_w() > (banner_file_itm.Orig_h() * 2); - byte[] banner_file = null; // $bannerfile->getLocalUrl(); - - byte[] toc_html = null; - if (hctx.Mode_is_hdump()) { - gplx.xowa.htmls.core.wkrs.tocs.Xoh_toc_wtr.Write_tag(tmp_bfr, true); - toc_html = tmp_bfr.To_bry_and_clear(); - banner_file = Bry_.Add(gplx.xowa.htmls.hrefs.Xoh_href_.Bry__wiki, gplx.xowa.wikis.nss.Xow_ns_.Bry__file, Byte_ascii.Colon_bry - , Gfo_url_encoder_.Href.Encode(banner_ttl.Full_db())); // NOTE: must encode so "'" becomes "%27", not "'"; PAGE:en.v:'s-Hertogenbosch; DATE:2016-07-12 - } - else { - ctx.Page().Html_data().Toc_mgr().To_html(tmp_bfr, Xoh_wtr_ctx.Basic, true); - toc_html = tmp_bfr.To_bry_and_clear(); - } - itm.Init_from_html(max_width, banner_file, banner_url, srcset, cfg.enable_heading_override, toc_html, isPanorama); - - Mustache_render_ctx mctx = new Mustache_render_ctx().Init(itm); - Mustache_bfr mbfr = Mustache_bfr.New_bfr(tmp_bfr); - wiki.Xtn_mgr().Xtn_pgbnr().Template_root().Render(mbfr, mctx); - return mbfr.To_bry_and_clear(); - } - private static byte[][] Get_standard_size_urls(Xow_wiki wiki, Pgbnr_cfg cfg, Xoa_ttl banner_ttl) { - Ordered_hash hash = Ordered_hash_.New_bry(); - int[] sizes = cfg.standard_sizes; - int sizes_len = sizes.length; - for (int i = 0; i < sizes_len; ++i) { - byte[] url = Get_image_url(wiki, banner_ttl, sizes[i]); - if (url != null) - hash.Add_if_dupe_use_1st(url, url); - } - return (byte[][])hash.To_ary_and_clear(byte[].class); - } - private static byte[] Get_image_url(Xow_wiki wiki, Xoa_ttl banner_ttl, int width) { - // Object file = new Object(); // $file = wfFindFile( file_ttl ); - // if (file == null) return null; - if (width >= 0 && width <= 3000) { - // $mto = $file->transform( array( 'width' => $imagewidth ) ); - byte[] url = new byte[0]; // $url = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT ); - return url; - } - else - return new byte[0]; // $file->getFullUrl(); - } - private static Xoa_ttl Get_wikidata_banner(Xoae_app app, Xow_wiki wiki, Pgbnr_cfg cfg, Xoa_ttl ttl) { - byte[] rv = app.Wiki_mgr().Wdata_mgr().Get_claim_or(wiki.Domain_itm(), ttl, cfg.dflt_img_wdata_prop, null); // don't log misses; wikivoyage pages will default to show pagebanner, and many pages may not have wikidata definitions - if (rv == null) return null; - return wiki.Ttl_parse(rv); - } - private static Xof_file_itm File__make_tkn(Xop_ctx ctx, byte tid, Xoa_ttl file_ttl, int file_w, int file_h) { - Xop_lnki_tkn lnki = ctx.Tkn_mkr().Lnki(file_w, file_h).Ttl_(file_ttl); - ctx.Page().Lnki_list().Add(lnki); - ctx.Lnki().File_logger().Log_file(tid, ctx, lnki); // NOTE: do not set file_wkr ref early (as member var); parse_all sets late - Xof_file_itm file_itm = ctx.Wiki().Html_mgr().Html_wtr().Lnki_wtr().File_wtr().Lnki_eval(Xof_exec_tid.Tid_wiki_page, ctx, ctx.Page(), lnki); - return file_itm; - } - private static final byte[] Bry__yes = Bry_.new_a7("yes"), Bry__icon = Bry_.new_a7("icon-"), Bry__icon_href_dflt = Bry_.new_a7("#"), Bry__origin_x__left = Bry_.new_a7("wpb-left"), Bry__origin_x__right = Bry_.new_a7("wpb-right"); - private static final int Arg__pgname = 0, Arg__tooltip = 1, Arg__bottomtoc = 2, Arg__toc = 3, Arg__icon = 4, Arg__origin = 5; - private static final Hash_adp_bry arg_hash = Hash_adp_bry.cs().Add_str_int("pgname", Arg__pgname) - .Add_str_int("tooltip", Arg__tooltip).Add_str_int("bottomtoc", Arg__bottomtoc).Add_str_int("toc", Arg__toc).Add_str_int("origin", Arg__origin); -} +package gplx.xowa.xtns.pagebanners; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Bry_find_; +import gplx.Byte_ascii; +import gplx.Double_; +import gplx.Gfo_usr_dlg_; +import gplx.Hash_adp_bry; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.Ordered_hash; +import gplx.Ordered_hash_; +import gplx.langs.htmls.encoders.Gfo_url_encoder_; +import gplx.langs.mustaches.JsonMustacheNde; +import gplx.langs.mustaches.Mustache_bfr; +import gplx.langs.mustaches.Mustache_render_ctx; +import gplx.xowa.Xoa_ttl; +import gplx.xowa.Xoae_app; +import gplx.xowa.Xoae_page; +import gplx.xowa.Xow_wiki; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.files.Xof_exec_tid; +import gplx.xowa.files.Xof_file_itm; +import gplx.xowa.htmls.core.htmls.Xoh_wtr_ctx; +import gplx.xowa.langs.kwds.Xol_kwd_grp_; +import gplx.xowa.parsers.Xop_ctx; +import gplx.xowa.parsers.lnkis.Xop_lnki_tkn; +import gplx.xowa.parsers.lnkis.files.Xop_file_logger_; +import gplx.xowa.parsers.tmpls.Arg_nde_tkn; +import gplx.xowa.parsers.tmpls.Xop_func_arg_itm; +import gplx.xowa.parsers.tmpls.Xot_invk; +import gplx.xowa.parsers.utils.Xop_sanitizer; +import gplx.xowa.xtns.pfuncs.Pf_func; +import gplx.xowa.xtns.pfuncs.Pf_func_base; + +// REF.MW:https://github.com/wikimedia/mediawiki-extensions-WikidataPageBanner/blob/master/includes/WikidataPageBannerFunctions.php +// TODO.DATE:2019-11-11: calculate enableToc and hasPosition; +public class Pgbnr_func extends Pf_func_base { + @Override public int Id() {return Xol_kwd_grp_.Id_pagebanner;} + @Override public Pf_func New(int id, byte[] name) {return new Pgbnr_func().Name_(name);} + @Override public void Func_evaluate(Bry_bfr bfr, Xop_ctx ctx, Xot_invk caller, Xot_invk self, byte[] src) { // {{PAGEBANNER}} appears on page; WikidataPageBanner.hooks.php|addCustomBanner + Xowe_wiki wiki = ctx.Wiki(); Xoae_page page = ctx.Page(); + Pgbnr_xtn_mgr xtn_mgr = wiki.Xtn_mgr().Xtn_pgbnr(); + Pgbnr_cfg cfg = xtn_mgr.Cfg(); + Xoa_ttl ttl = page.Ttl(); + if (!cfg.Chk_pgbnr_allowed(ttl, wiki)) return; + byte[] tooltip = ttl.Page_txt(), title = ttl.Page_txt(), toc = Bry_.Empty, origin_x = Bry_.Empty; + boolean bottomtoc = false;; + double data_pos_x = 0, data_pos_y = 0; + List_adp icons_list = null; + Bry_bfr tmp_bfr = Bry_bfr_.New(); + int args_len = self.Args_len(); + Xop_func_arg_itm func_arg = new Xop_func_arg_itm(); + for (int i = 0; i < args_len; ++i) { + Arg_nde_tkn arg = self.Args_get_by_idx(i); + func_arg.Set(tmp_bfr, ctx, src, caller, self, arg); + byte[] key = func_arg.key; + byte[] val = func_arg.val; + if (key == Bry_.Empty) continue; // ignore blank args; EX:{{PAGEBANNER:A.png|\n|toc=yes}} + int tid = arg_hash.Get_as_int_or(key, -1); + if (tid == Arg__pgname) + tooltip = title = val; + if (tid == Arg__tooltip) // note that this overrides pgname above + tooltip = val; + if (tid == Arg__bottomtoc && Bry_.Eq(val, Bry__yes)) + bottomtoc = true; + if (tid == Arg__toc && Bry_.Eq(val, Bry__yes)) // REF.MW:addToc + toc = Bry_.Empty; // note that "" will be overwritten later by actual toc html + if ( tid == -1 // note that "icon-*" won't have a tid + && Bry_.Has_at_bgn(key, Bry__icon) // if (substr($key, 0, 5) === 'icon-') + && Bry_.Len(key) > 5 // if ( !isset( $iconname) ) + && Bry_.Len_gt_0(val) // if ( !isset( $$value ) ) + ) { // REF.MW:addIcons + tid = Arg__icon; + if (icons_list == null) icons_list = List_adp_.New(); + byte[] icon_key = Bry_.Mid(key, 5); + byte[] icon_name = Xop_sanitizer.Escape_cls(icon_key); + byte[] icon_title = icon_name; + Xoa_ttl icon_ttl = wiki.Ttl_parse(val); + byte[] icon_href = Bry__icon_href_dflt; + if (icon_ttl != null) { + icon_href = Bry_.Add(gplx.xowa.htmls.hrefs.Xoh_href_.Bry__wiki, icon_ttl.Page_db()); + icon_title = icon_ttl.Page_txt(); + } + icons_list.Add(new Pgbnr_icon(tmp_bfr, icon_name, icon_title, icon_href)); +// itm.Add_new_icon(tmp_bfr, icon_name, icon_title, icon_href); + } + if (tid == Arg__origin) { // REF.MW:addFocus + double tmp_data_pos_x = Double_.NaN, tmp_data_pos_y = Double_.NaN; + int comma_pos = Bry_find_.Find_fwd(val, Byte_ascii.Comma); + if (comma_pos != Bry_find_.Not_found) { + tmp_data_pos_x = Bry_.To_double_or(val, 0, comma_pos, Double_.NaN); + if (!Double_.IsNaN(tmp_data_pos_x)) { + if (tmp_data_pos_x >= -1 && tmp_data_pos_x <= 1) { + data_pos_x = tmp_data_pos_x; + origin_x = tmp_data_pos_x <= .25d ? Bry__origin_x__left : Bry__origin_x__right; + } + } + if (!Double_.IsNaN(tmp_data_pos_y)) { + if (tmp_data_pos_y >= -1 && tmp_data_pos_y <= 1) + data_pos_y = tmp_data_pos_y; + } + } + } + if (tid == -1) Gfo_usr_dlg_.Instance.Warn_many("", "", "unknown arg type; page=~{0} key=~{1} val=~{2}", page.Ttl().Full_db(), key, val); + } + + byte[] banner_name = Eval_argx(ctx, src, caller, self); + Xoa_ttl banner_ttl = wiki.Ttl_parse(banner_name); // NOTE: MW also creates title to auto-register page and image in imagelinks + if (banner_ttl == null) // if ttl is invalid, get it from wikidata; PAGE:en.v:Diving_the_Cape_Peninsula_and_False_Bay/Whale_Rock; DATE:2016-07-12 + banner_ttl = Get_wikidata_banner(wiki.Appe(), wiki, wiki.Xtn_mgr().Xtn_pgbnr().Cfg(), page.Ttl()); + if (banner_ttl == null) { // if ttl is still invalid, exit now else will fail with nullref below; PAGE:en.v:Peterborough (New Hampshire); DATE:2016-07-12 + Gfo_usr_dlg_.Instance.Warn_many("", "", "banner file is invalid; page=~{0} banner=~{1}", page.Url_bry_safe(), banner_name); + return; + } + Xof_file_itm banner_file_itm = File__make_tkn(ctx, Xop_file_logger_.Tid__pgbnr_main, banner_ttl, Xop_lnki_tkn.Width_null, Xop_lnki_tkn.Height_null); + + Pgbnr_itm itm = new Pgbnr_itm(); + itm.Init_from_wtxt(banner_ttl, banner_file_itm, tooltip, title, bottomtoc, toc, data_pos_x, data_pos_y, origin_x, icons_list == null ? Pgbnr_icon.Ary_empty : (Pgbnr_icon[])icons_list.To_ary_and_clear(Pgbnr_icon.class)); + page.Html_data().Xtn_pgbnr_(itm); + page.Html_data().Head_mgr().Itm__pgbnr().Enabled_y_(); // register css / js during parse stage + page.Wtxt().Toc().Flag__toc_(true); // NOTE: must mark toc_manual else will show 2nd TOC in edit mode; DATE:2016-07-10 + } + public static void Add_banner(Bry_bfr bfr, Xoae_page wpg, Xop_ctx ctx, Xoh_wtr_ctx hctx, Pgbnr_itm itm) { + Xowe_wiki wiki = ctx.Wiki(); Xoae_app app = wiki.Appe(); + Pgbnr_cfg cfg = wiki.Xtn_mgr().Xtn_pgbnr().Cfg(); if (!cfg.enabled) return; + Xoa_ttl ttl = wpg.Ttl(); + Xoa_ttl banner_ttl = null; byte[] banner_html = null; + if (itm != null) { // {{PAGEBANNER}} exists in wikitext + itm.Init_hdump(hctx.Mode_is_hdump()); + banner_ttl = itm.banner_ttl; + banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm); + if (banner_html == null) { // no banner; try again using title from wikidata; note that this should only happen if no banner_ttl or banner_ttl is invalid; EX:{{PAGEBANNER:|toc=yes}} + banner_ttl = Get_wikidata_banner(app, wiki, cfg, ttl); + banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm); + } + } + else if ( ttl.Ns().Id_is_main() // {{PAGEBANNER}} missing, but wiki is marked as enable_default_banner + && cfg.enable_default_banner + && cfg.Chk_pgbnr_allowed(ttl, wiki) + ) { + banner_ttl = Get_wikidata_banner(app, wiki, cfg, ttl); + if (banner_ttl == null) + banner_ttl = wiki.Ttl_parse(cfg.dflt_img_title); + Xof_file_itm banner_file_itm = File__make_tkn(ctx, Xop_file_logger_.Tid__pgbnr_main, banner_ttl, Xop_lnki_tkn.Width_null, Xop_lnki_tkn.Height_null); + itm = new Pgbnr_itm(); + itm.Init_from_wtxt(banner_ttl, banner_file_itm, Bry_.Empty, Bry_.Empty, false, Bry_.Empty, 0, 0, Bry_.Empty, Pgbnr_icon.Ary_empty); + itm.Init_hdump(hctx.Mode_is_hdump()); + banner_html = Get_banner_html(wiki, ctx, hctx, cfg, banner_ttl, itm); + } + if (banner_html != null) { + bfr.Add(banner_html); + } + } + public static byte[] Get_banner_html(Xowe_wiki wiki, Xop_ctx ctx, Xoh_wtr_ctx hctx, Pgbnr_cfg cfg, Xoa_ttl banner_ttl, Pgbnr_itm itm) { + byte[][] urls = Get_standard_size_urls(wiki, cfg, banner_ttl); if (urls == null) return null; + Bry_bfr tmp_bfr = Bry_bfr_.New(); + int urls_len = urls.length; + int[] sizes = cfg.standard_sizes; + for (int i = 0; i < urls_len; ++i) { + int size = sizes[i]; + if (i != 0) tmp_bfr.Add_byte_comma(); + tmp_bfr.Add(urls[i]).Add_byte_space().Add_int_variable(size).Add_byte(Byte_ascii.Ltr_w); // REF.MW: $srcset[] = "$url {$size}w"; + } + byte[] srcset = tmp_bfr.To_bry_and_clear(); + byte[] banner_url = itm.banner_img_src != null ? itm.banner_img_src : urls.length == 0 ? Bry_.Empty : urls[urls_len - 1]; // gets largest url + Xof_file_itm banner_file_itm = itm.banner_file_itm; + int max_width = banner_file_itm.Orig_w(); // $file = wfFindFile( banner_file ); $options['max_width'] = $file->getWidth(); + // Provide information to the logic-less template about whether it is a panorama or not. + boolean isPanorama = banner_file_itm.Orig_w() > (banner_file_itm.Orig_h() * 2); + byte[] banner_file = null; // $bannerfile->getLocalUrl(); + + byte[] toc_html = null; + if (hctx.Mode_is_hdump()) { + gplx.xowa.htmls.core.wkrs.tocs.Xoh_toc_wtr.Write_tag(tmp_bfr, true); + toc_html = tmp_bfr.To_bry_and_clear(); + banner_file = Bry_.Add(gplx.xowa.htmls.hrefs.Xoh_href_.Bry__wiki, gplx.xowa.wikis.nss.Xow_ns_.Bry__file, Byte_ascii.Colon_bry + , Gfo_url_encoder_.Href.Encode(banner_ttl.Full_db())); // NOTE: must encode so "'" becomes "%27", not "'"; PAGE:en.v:'s-Hertogenbosch; DATE:2016-07-12 + } + else { + ctx.Page().Html_data().Toc_mgr().To_html(tmp_bfr, Xoh_wtr_ctx.Basic, true); + toc_html = tmp_bfr.To_bry_and_clear(); + } + itm.Init_from_html(max_width, banner_file, banner_url, srcset, cfg.enable_heading_override, toc_html, isPanorama); + + Mustache_render_ctx mctx = new Mustache_render_ctx().Init(new JsonMustacheNde(itm.Mustache__json())); + // Mustache_render_ctx mctx = new Mustache_render_ctx().Init(itm); + Mustache_bfr mbfr = Mustache_bfr.New_bfr(tmp_bfr); + wiki.Xtn_mgr().Xtn_pgbnr().Template_root().Render(mbfr, mctx); + return mbfr.To_bry_and_clear(); + } + private static byte[][] Get_standard_size_urls(Xow_wiki wiki, Pgbnr_cfg cfg, Xoa_ttl banner_ttl) { + Ordered_hash hash = Ordered_hash_.New_bry(); + int[] sizes = cfg.standard_sizes; + int sizes_len = sizes.length; + for (int i = 0; i < sizes_len; ++i) { + byte[] url = Get_image_url(wiki, banner_ttl, sizes[i]); + if (url != null) + hash.Add_if_dupe_use_1st(url, url); + } + return (byte[][])hash.To_ary_and_clear(byte[].class); + } + private static byte[] Get_image_url(Xow_wiki wiki, Xoa_ttl banner_ttl, int width) { + // Object file = new Object(); // $file = wfFindFile( file_ttl ); + // if (file == null) return null; + if (width >= 0 && width <= 3000) { + // $mto = $file->transform( array( 'width' => $imagewidth ) ); + byte[] url = new byte[0]; // $url = wfExpandUrl( $mto->getUrl(), PROTO_CURRENT ); + return url; + } + else + return new byte[0]; // $file->getFullUrl(); + } + private static Xoa_ttl Get_wikidata_banner(Xoae_app app, Xow_wiki wiki, Pgbnr_cfg cfg, Xoa_ttl ttl) { + byte[] rv = app.Wiki_mgr().Wdata_mgr().Get_claim_or(wiki.Domain_itm(), ttl, cfg.dflt_img_wdata_prop, null); // don't log misses; wikivoyage pages will default to show pagebanner, and many pages may not have wikidata definitions + if (rv == null) return null; + return wiki.Ttl_parse(rv); + } + private static Xof_file_itm File__make_tkn(Xop_ctx ctx, byte tid, Xoa_ttl file_ttl, int file_w, int file_h) { + Xop_lnki_tkn lnki = ctx.Tkn_mkr().Lnki(file_w, file_h).Ttl_(file_ttl); + ctx.Page().Lnki_list().Add(lnki); + ctx.Lnki().File_logger().Log_file(tid, ctx, lnki); // NOTE: do not set file_wkr ref early (as member var); parse_all sets late + Xof_file_itm file_itm = ctx.Wiki().Html_mgr().Html_wtr().Lnki_wtr().File_wtr().Lnki_eval(Xof_exec_tid.Tid_wiki_page, ctx, ctx.Page(), lnki); + return file_itm; + } + private static final byte[] Bry__yes = Bry_.new_a7("yes"), Bry__icon = Bry_.new_a7("icon-"), Bry__icon_href_dflt = Bry_.new_a7("#"), Bry__origin_x__left = Bry_.new_a7("wpb-left"), Bry__origin_x__right = Bry_.new_a7("wpb-right"); + private static final int Arg__pgname = 0, Arg__tooltip = 1, Arg__bottomtoc = 2, Arg__toc = 3, Arg__icon = 4, Arg__origin = 5; + private static final Hash_adp_bry arg_hash = Hash_adp_bry.cs().Add_str_int("pgname", Arg__pgname) + .Add_str_int("tooltip", Arg__tooltip).Add_str_int("bottomtoc", Arg__bottomtoc).Add_str_int("toc", Arg__toc).Add_str_int("origin", Arg__origin); +} diff --git a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func_tst.java b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func_tst.java index 333e96bcb..021ba4efa 100644 --- a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func_tst.java +++ b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_func_tst.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,79 +13,92 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.pagebanners; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import org.junit.*; import gplx.core.brys.*; import gplx.xowa.wikis.pages.skins.*; -import gplx.xowa.htmls.core.htmls.*; -public class Pgbnr_func_tst { - private final Pgbnr_func_fxt fxt = new Pgbnr_func_fxt(); - @Before public void init() {fxt.Clear();} - @Test public void Basic() { - fxt.Init__orig(true, "A.png", 500, 200); // 500 > 200 * 2 for pageBanner; - fxt.Test__parse(Bool_.N, "{{PAGEBANNER:A.png|icon-star=Star_article}}", String_.Concat_lines_nl_apos_skip_last - ( "
    " - , "
    " - , "

    Test page

    " - , " " - , "
    " - , " " - , "
    " - , "
    " - , "
    " - , "
    " - , "

    Contents

    " - , "
    " - , "
    " - , "
    " - , "
    " - )); - } - @Test public void Hdump__basic() { - fxt.Test__parse(Bool_.Y, "{{PAGEBANNER:A.png|icon-star=Star_article}}", String_.Concat_lines_nl_apos_skip_last - ( "
    " - , "
    " - , "

    Test page

    " - , " " - + "" - , "
    " - , " " - , "
    " - , "
    " - , "
    " - , "
    " - )); - } - @Test public void Hdump__quote() { // PAGE:en.v:Europe; DATE:2016-07-12 - fxt.Test__parse(Bool_.Y, "{{PAGEBANNER:A\"b.png|icon-star=Star_article}}", String_.Concat_lines_nl_apos_skip_last - ( "
    " - , "
    " - , "

    Test page

    " - , " " - , "
    " - , " " - , "
    " - , "
    " - , "
    " - , "
    " - )); - } -} -class Pgbnr_func_fxt { - private Xop_fxt fxt; - public void Clear() { - Xoae_app app = Xoa_app_fxt.Make__app__edit(); - Xowe_wiki wiki = Xoa_app_fxt.Make__wiki__edit(app, "en.wikivoyage.org"); - fxt = new Xop_fxt(app, wiki); - fxt.Init__file_find_mgr(); - } - public void Init__orig(boolean wiki_is_commons, String orig_ttl, int orig_w, int orig_h) { - fxt.Init__orig(wiki_is_commons, orig_ttl, orig_w, orig_h); - } - public void Test__parse(boolean hdump, String raw, String expd) { - fxt.Exec_parse_page_all_as_str(raw); - Xoh_wtr_ctx hctx = hdump ? Xoh_wtr_ctx.Hdump : Xoh_wtr_ctx.Basic; - Bfr_arg arg = fxt.Wiki().Xtn_mgr().Xtn_pgbnr().Write_html(fxt.Page(), fxt.Ctx(), hctx); - Bry_bfr bfr = Bry_bfr_.New(); - arg.Bfr_arg__add(bfr); - Tfds.Eq_str_lines(expd, bfr.To_str_and_clear()); - } -} +package gplx.xowa.xtns.pagebanners; + +import gplx.Bool_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.String_; +import gplx.Tfds; +import gplx.core.brys.Bfr_arg; +import gplx.xowa.Xoa_app_fxt; +import gplx.xowa.Xoae_app; +import gplx.xowa.Xop_fxt; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.htmls.core.htmls.Xoh_wtr_ctx; +import org.junit.Before; +import org.junit.Test; + +public class Pgbnr_func_tst { + private final Pgbnr_func_fxt fxt = new Pgbnr_func_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Basic() { + fxt.Init__orig(true, "A.png", 500, 200); // 500 > 200 * 2 for pageBanner; + fxt.Test__parse(Bool_.N, "{{PAGEBANNER:A.png|icon-star=Star_article}}", String_.Concat_lines_nl_apos_skip_last + ( "
    " + , "
    " + , "

    Test page

    " + , " " + , "
    " + , " " + , "
    " + , "
    " + , "
    " + , "
    " + , "

    Contents

    " + , "
    " + , "
    " + , "
    " + , "
    " + )); + } + @Test public void Hdump__basic() { + fxt.Test__parse(Bool_.Y, "{{PAGEBANNER:A.png|icon-star=Star_article}}", String_.Concat_lines_nl_apos_skip_last + ( "
    " + , "
    " + , "

    Test page

    " + , " " + + "" + , "
    " + , " " + , "
    " + , "
    " + , "
    " + , "
    " + )); + } + @Test public void Hdump__quote() { // PAGE:en.v:Europe; DATE:2016-07-12 + fxt.Test__parse(Bool_.Y, "{{PAGEBANNER:A\"b.png|icon-star=Star_article}}", String_.Concat_lines_nl_apos_skip_last + ( "
    " + , "
    " + , "

    Test page

    " + , " " + , "
    " + , " " + , "
    " + , "
    " + , "
    " + , "
    " + )); + } +} +class Pgbnr_func_fxt { + private Xop_fxt fxt; + public void Clear() { + Xoae_app app = Xoa_app_fxt.Make__app__edit(); + Xowe_wiki wiki = Xoa_app_fxt.Make__wiki__edit(app, "en.wikivoyage.org"); + fxt = new Xop_fxt(app, wiki); + fxt.Init__file_find_mgr(); + } + public void Init__orig(boolean wiki_is_commons, String orig_ttl, int orig_w, int orig_h) { + fxt.Init__orig(wiki_is_commons, orig_ttl, orig_w, orig_h); + } + public void Test__parse(boolean hdump, String raw, String expd) { + fxt.Exec_parse_page_all_as_str(raw); + Xoh_wtr_ctx hctx = hdump ? Xoh_wtr_ctx.Hdump : Xoh_wtr_ctx.Basic; + Bfr_arg arg = fxt.Wiki().Xtn_mgr().Xtn_pgbnr().Write_html(fxt.Page(), fxt.Ctx(), hctx); + Bry_bfr bfr = Bry_bfr_.New(); + arg.Bfr_arg__add(bfr); + Tfds.Eq_str_lines(expd, bfr.To_str_and_clear()); + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_icon.java b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_icon.java index 99fbb2122..fe36e33f0 100644 --- a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_icon.java +++ b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_icon.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,28 +13,39 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.pagebanners; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import gplx.core.brys.fmtrs.*; -import gplx.langs.mustaches.*; -public class Pgbnr_icon implements Mustache_doc_itm { - private final byte[] name, title, href, html; - public Pgbnr_icon(Bry_bfr tmp_bfr, byte[] name, byte[] title, byte[] href) { - this.name = name; this.title = title; this.href = href; - fmt.Bld_many(tmp_bfr, name, title); - this.html = tmp_bfr.To_bry_and_clear(); - } - public boolean Mustache__write(String key, Mustache_bfr bfr) { - if (String_.Eq(key, "name")) bfr.Add_bry(name); - else if (String_.Eq(key, "title")) bfr.Add_bry(title); - else if (String_.Eq(key, "url")) bfr.Add_bry(href); - else if (String_.Eq(key, "html")) bfr.Add_bry(html); - else return false; - return true; - } - public Mustache_doc_itm[] Mustache__subs(String key) {return Mustache_doc_itm_.Ary__empty;} - public static final Pgbnr_icon[] Ary_empty = new Pgbnr_icon[0]; - public static final Bry_fmt fmt = Bry_fmt.New - ( Bry_.New_u8_nl_apos("") - , "name", "title" - ); -} +package gplx.xowa.xtns.pagebanners; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_fmt; +import gplx.String_; +import gplx.langs.mustaches.Mustache_bfr; +import gplx.langs.mustaches.Mustache_doc_itm; +import gplx.langs.mustaches.Mustache_doc_itm_; + +public class Pgbnr_icon implements Mustache_doc_itm { + private final byte[] name, title, href, html; + public Pgbnr_icon(Bry_bfr tmp_bfr, byte[] name, byte[] title, byte[] href) { + this.name = name; this.title = title; this.href = href; + fmt.Bld_many(tmp_bfr, name, title); + this.html = tmp_bfr.To_bry_and_clear(); + } + public byte[] Name() {return name;} + public byte[] Title() {return title;} + public byte[] Href() {return href;} + public byte[] Html() {return html;} + public boolean Mustache__write(String key, Mustache_bfr bfr) { + if (String_.Eq(key, "name")) bfr.Add_bry(name); + else if (String_.Eq(key, "title")) bfr.Add_bry(title); + else if (String_.Eq(key, "url")) bfr.Add_bry(href); + else if (String_.Eq(key, "html")) bfr.Add_bry(html); + else return false; + return true; + } + public Mustache_doc_itm[] Mustache__subs(String key) {return Mustache_doc_itm_.Ary__empty;} + public static final Pgbnr_icon[] Ary_empty = new Pgbnr_icon[0]; + public static final Bry_fmt fmt = Bry_fmt.New + ( Bry_.New_u8_nl_apos("") + , "name", "title" + ); +} diff --git a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_itm.java b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_itm.java index ed12017e1..bb526ff9d 100644 --- a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_itm.java +++ b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_itm.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,117 +13,224 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.pagebanners; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import gplx.langs.mustaches.*; import gplx.xowa.parsers.lnkis.*; -import gplx.xowa.files.*; -import gplx.langs.htmls.*; import gplx.langs.htmls.docs.*; import gplx.xowa.htmls.core.wkrs.imgs.atrs.*; import gplx.xowa.htmls.core.wkrs.lnkis.htmls.*; import gplx.langs.htmls.encoders.*; -public class Pgbnr_itm implements Mustache_doc_itm { - public Xoa_ttl banner_ttl; - public byte[] banner_img_src; - public byte[] toc; - public Xof_file_itm banner_file_itm; - private byte[] banner_anch_title, banner_hdr_text, originx, banner_anch_href, srcset; - private double data_pos_x, data_pos_y; - private int max_width; - private boolean bottomtoc, isHeadingOverrideEnabled; - private byte[] file_ttl; - private Pgbnr_icon[] icons; - private byte[] img_id_atr, img_xottl_atr, img_xoimg_atr; - private boolean isPanorama; - private boolean enable_toc = true; - private boolean hasPosition = false; - - // NOTE: used by hdump - public boolean Exists() {return exists;} private boolean exists; - public byte[] Srcset() {return srcset;} - public byte[] Style_if_not_dflt() {return Bry_.Eq(style, Atr_val__style_dflt) ? Bry_.Empty : style;} - public byte[] Style() {return style;} private byte[] style; - public double Data_pos_x() {return data_pos_x;} - public double Data_pos_y() {return data_pos_y;} - public boolean Show_toc_in_html() {return show_toc_in_html;} private boolean show_toc_in_html = false; // default to false so that TOC does not show up in both PageBanner and HTML body; DATE:2019-11-17 - - public void Clear_by_hdump() { - this.exists = false; - this.srcset = style = null; - this.data_pos_x = this.data_pos_y = 0; - } - public void Init_by_parse(Gfh_tag tag) { - this.exists = true; - this.srcset = tag.Atrs__get_as_bry(Atr_key__srcset); - this.style = tag.Atrs__get_as_bry(Gfh_atr_.Bry__style); - this.data_pos_x = tag.Atrs__get_as_double_or(Atr_key__data_pos_x, 0); - this.data_pos_y = tag.Atrs__get_as_double_or(Atr_key__data_pos_y, 0); - } - public void Init_by_decode(double data_pos_x, double data_pos_y, byte[] srcset, byte[] style_if_not_dflt) { - this.data_pos_x = data_pos_x; - this.data_pos_y = data_pos_y; - this.srcset = srcset; - this.style = Bry_.Eq(style_if_not_dflt, Bry_.Empty) ? Atr_val__style_dflt : style_if_not_dflt; - this.exists = true; - } - public void Init_from_wtxt(Xoa_ttl banner_ttl, Xof_file_itm banner_file_itm, byte[] banner_anch_title, byte[] banner_hdr_text, boolean bottomtoc - , byte[] toc, double data_pos_x, double data_pos_y, byte[] originx, Pgbnr_icon[] icons) { - this.banner_ttl = banner_ttl; this.banner_file_itm = banner_file_itm; - this.banner_anch_title = banner_anch_title; this.banner_hdr_text = banner_hdr_text; this.bottomtoc = bottomtoc; this.toc = toc; this.icons = icons; - this.data_pos_x = data_pos_x; this.data_pos_y = data_pos_y; this.originx = originx; - this.banner_img_src = banner_file_itm.Html_view_url().To_http_file_bry(); - this.file_ttl = Gfo_url_encoder_.Href_quotes.Encode(banner_file_itm.Lnki_ttl()); // NOTE: Encode(Lnki_ttl) not Orig_ttl; else "%27s" instead of "'s" PAGE:en.v:'s-Hertogenbosch; DATE:2016-07-12 - } - public void Init_from_html(int max_width, byte[] banner_anch_href, byte[] banner_img_src, byte[] srcset, boolean isHeadingOverrideEnabled, byte[] toc, boolean isPanorama) { - this.max_width = max_width; - this.banner_anch_href = banner_anch_href; - this.banner_img_src = banner_img_src; - this.srcset = srcset; - this.isHeadingOverrideEnabled = isHeadingOverrideEnabled; - this.toc = toc; - this.isPanorama = isPanorama; - } - public void Init_hdump(boolean mode_is_hdump) { - Bry_bfr tmp_bfr = Bry_bfr_.New(); - if (mode_is_hdump) { - this.img_id_atr = Bry_.Empty; - this.img_xottl_atr = Gfh_atr_.Add_to_bry(tmp_bfr, Xoh_img_xoimg_data.Bry__data_xowa_title, file_ttl); - this.img_xoimg_atr = Gfh_atr_.Add_to_bry(tmp_bfr, Xoh_img_xoimg_data.Bry__data_xowa_image, Xoh_img_xoimg_data.Bry__data_xowa_image__full); - this.banner_img_src = Bry_.Empty; // do not write img_src else hzip_diff will complain for every image; DATE:2016-10-18 - } - else { - this.img_id_atr = tmp_bfr.Add(Bry__anch_atr_id).Add_int_variable(banner_file_itm.Html_uid()).Add_byte_quote().To_bry_and_clear(); - this.img_xottl_atr = this.img_xoimg_atr = Bry_.Empty; - } - } - public boolean Mustache__write(String key, Mustache_bfr bfr) { - if (String_.Eq(key, "title")) bfr.Add_bry(banner_hdr_text); - else if (String_.Eq(key, "tooltip")) bfr.Add_bry(banner_anch_title); - else if (String_.Eq(key, "bannerfile")) bfr.Add_bry(banner_anch_href); - else if (String_.Eq(key, "banner")) bfr.Add_bry(banner_img_src); - else if (String_.Eq(key, "srcset")) bfr.Add_bry(srcset == null ? Bry_.Empty : Bry_.Empty); - else if (String_.Eq(key, "originx")) bfr.Add_bry(originx); - else if (String_.Eq(key, "data-pos-x")) bfr.Add_double(data_pos_x); - else if (String_.Eq(key, "data-pos-y")) bfr.Add_double(data_pos_y); - else if (String_.Eq(key, "maxWidth")) bfr.Add_int(max_width); - else if (String_.Eq(key, "toc")) bfr.Add_bry(toc); - else if (String_.Eq(key, "img_id_atr")) bfr.Add_bry(img_id_atr); - else if (String_.Eq(key, "img_xottl")) bfr.Add_bry(img_xottl_atr); - else if (String_.Eq(key, "img_xoimg")) bfr.Add_bry(img_xoimg_atr); - else if (String_.Eq(key, "file_ttl")) bfr.Add_bry(file_ttl); - else return false; - return true; - } - public Mustache_doc_itm[] Mustache__subs(String key) { - if (String_.Eq(key, "icons")) return icons; - else if (String_.Eq(key, "hasIcons")) return Mustache_doc_itm_.Ary__bool(icons.length > 0); - else if (String_.Eq(key, "bottomtoc")) return Mustache_doc_itm_.Ary__bool(bottomtoc); - else if (String_.Eq(key, "isHeadingOverrideEnabled")) return Mustache_doc_itm_.Ary__bool(isHeadingOverrideEnabled); - else if (String_.Eq(key, "isPanorama")) return Mustache_doc_itm_.Ary__bool(isPanorama); - else if (String_.Eq(key, "enable-toc")) return Mustache_doc_itm_.Ary__bool(enable_toc); - else if (String_.Eq(key, "hasPosition")) return Mustache_doc_itm_.Ary__bool(hasPosition); - return Mustache_doc_itm_.Ary__empty; - } - private static final byte[] Bry__anch_atr_id = Bry_.new_a7(" id=\"xoimg_"); - public static final byte[] - Atr_key__srcset = Bry_.new_a7("srcset") - , Atr_key__data_pos_x = Bry_.new_a7("data-pos-x") - , Atr_key__data_pos_y = Bry_.new_a7("data-pos-y") - , Atr_val__style_dflt = Bry_.new_a7("max-width:-1px") - ; -} +package gplx.xowa.xtns.pagebanners; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Bry_fmt; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.String_; +import gplx.langs.htmls.Gfh_atr_; +import gplx.langs.htmls.docs.Gfh_tag; +import gplx.langs.htmls.encoders.Gfo_url_encoder_; +import gplx.langs.jsons.Json_ary; +import gplx.langs.jsons.Json_nde; +import gplx.langs.mustaches.Mustache_bfr; +import gplx.langs.mustaches.Mustache_doc_itm; +import gplx.langs.mustaches.Mustache_doc_itm_; +import gplx.xowa.Xoa_ttl; +import gplx.xowa.files.Xof_file_itm; +import gplx.xowa.htmls.core.wkrs.imgs.atrs.Xoh_img_xoimg_data; + +public class Pgbnr_itm implements Mustache_doc_itm { + public Xoa_ttl banner_ttl; + public byte[] banner_img_src; + public byte[] toc; + public Xof_file_itm banner_file_itm; + private byte[] banner_anch_title, banner_hdr_text, originx, srcset; + private double data_pos_x, data_pos_y; + public /**/ int max_width; + public /**/ byte[] banner_anch_href; + private boolean bottomtoc; + public /**/ boolean isHeadingOverrideEnabled; + private byte[] file_ttl; + private Pgbnr_icon[] icons; + private byte[] img_id_atr, img_xottl_atr, img_xoimg_atr; + public /**/ boolean isPanorama; + private boolean enable_toc = true; + private boolean hasPosition = false; + + // NOTE: used by hdump + public boolean Exists() {return exists;} private boolean exists; + public byte[] Srcset() {return srcset;} + public byte[] Style_if_not_dflt() {return Bry_.Eq(style, Atr_val__style_dflt) ? Bry_.Empty : style;} + public byte[] Style() {return style;} private byte[] style; + public double Data_pos_x() {return data_pos_x;} + public double Data_pos_y() {return data_pos_y;} + public boolean Show_toc_in_html() {return show_toc_in_html;} private boolean show_toc_in_html = false; // default to false so that TOC does not show up in both PageBanner and HTML body; DATE:2019-11-17 + + // hdump serialised info + public boolean Precoded() {return precoded;} private boolean precoded = false; + public byte[] Pgbnr_bry() {return pgbnr_bry;} private byte[] pgbnr_bry; + public void Pgbnr_bry_(byte[] v) { + if (v == null) return; + pgbnr_bry = v; + precoded = true; + } + + public void Clear_by_hdump() { + this.exists = false; + this.srcset = style = null; + this.data_pos_x = this.data_pos_y = 0; + } + public void Init_by_parse(Gfh_tag tag) { + this.exists = true; + this.srcset = tag.Atrs__get_as_bry(Atr_key__srcset); + this.style = tag.Atrs__get_as_bry(Gfh_atr_.Bry__style); + this.data_pos_x = tag.Atrs__get_as_double_or(Atr_key__data_pos_x, 0); + this.data_pos_y = tag.Atrs__get_as_double_or(Atr_key__data_pos_y, 0); + } + public void Init_by_decode(double data_pos_x, double data_pos_y, byte[] srcset, byte[] style_if_not_dflt) { + this.data_pos_x = data_pos_x; + this.data_pos_y = data_pos_y; + this.srcset = srcset; + this.style = Bry_.Eq(style_if_not_dflt, Bry_.Empty) ? Atr_val__style_dflt : style_if_not_dflt; + this.exists = true; + } + public void Init_from_wtxt(Xoa_ttl banner_ttl, Xof_file_itm banner_file_itm, byte[] banner_anch_title, byte[] banner_hdr_text, boolean bottomtoc + , byte[] toc, double data_pos_x, double data_pos_y, byte[] originx, Pgbnr_icon[] icons) { + Init_from_wtxt(banner_ttl, banner_file_itm, banner_anch_title, banner_hdr_text, bottomtoc + , toc, data_pos_x, data_pos_y, originx, icons, false); + } + public void Init_from_wtxt(Xoa_ttl banner_ttl, Xof_file_itm banner_file_itm, byte[] banner_anch_title, byte[] banner_hdr_text, boolean bottomtoc + , byte[] toc, double data_pos_x, double data_pos_y, byte[] originx, Pgbnr_icon[] icons, boolean enable_toc) { + this.banner_ttl = banner_ttl; this.banner_file_itm = banner_file_itm; + this.banner_anch_title = banner_anch_title; this.banner_hdr_text = banner_hdr_text; this.bottomtoc = bottomtoc; this.toc = toc; this.icons = icons; + this.data_pos_x = data_pos_x; this.data_pos_y = data_pos_y; this.originx = originx; + this.banner_img_src = banner_file_itm.Html_view_url().To_http_file_bry(); + this.file_ttl = Gfo_url_encoder_.Href_quotes.Encode(banner_file_itm.Lnki_ttl()); // NOTE: Encode(Lnki_ttl) not Orig_ttl; else "%27s" instead of "'s" PAGE:en.v:'s-Hertogenbosch; DATE:2016-07-12 + this.enable_toc = enable_toc; + show_toc_in_html = !enable_toc; + } + public void Init_from_html(int max_width, byte[] banner_anch_href, byte[] banner_img_src, byte[] srcset, boolean isHeadingOverrideEnabled, byte[] toc, boolean isPanorama) { + this.max_width = max_width; + this.banner_anch_href = banner_anch_href; + this.banner_img_src = banner_img_src; + this.srcset = srcset; + this.isHeadingOverrideEnabled = isHeadingOverrideEnabled; + this.toc = toc; + this.isPanorama = isPanorama; + } + public void Init_hdump(boolean mode_is_hdump) { + Bry_bfr tmp_bfr = Bry_bfr_.New(); + if (mode_is_hdump) { + this.img_id_atr = Bry_.Empty; + this.img_xottl_atr = Gfh_atr_.Add_to_bry(tmp_bfr, Xoh_img_xoimg_data.Bry__data_xowa_title, file_ttl); + this.img_xoimg_atr = Gfh_atr_.Add_to_bry(tmp_bfr, Xoh_img_xoimg_data.Bry__data_xowa_image, Xoh_img_xoimg_data.Bry__data_xowa_image__full); + this.banner_img_src = Bry_.Empty; // do not write img_src else hzip_diff will complain for every image; DATE:2016-10-18 + } + else { + this.img_id_atr = tmp_bfr.Add(Bry__anch_atr_id).Add_int_variable(banner_file_itm.Html_uid()).Add_byte_quote().To_bry_and_clear(); + this.img_xottl_atr = this.img_xoimg_atr = Bry_.Empty; + } + } + public boolean Mustache__write(String key, Mustache_bfr bfr) { + if (String_.Eq(key, "title")) bfr.Add_bry(banner_hdr_text); + else if (String_.Eq(key, "tooltip")) bfr.Add_bry(banner_anch_title); + else if (String_.Eq(key, "bannerfile")) bfr.Add_bry(banner_anch_href); + else if (String_.Eq(key, "banner")) bfr.Add_bry(banner_img_src); + else if (String_.Eq(key, "srcset")) bfr.Add_bry(srcset == null ? Bry_.Empty : Bry_.Empty); + else if (String_.Eq(key, "originx")) bfr.Add_bry(originx); + else if (String_.Eq(key, "data-pos-x")) bfr.Add_double(data_pos_x); + else if (String_.Eq(key, "data-pos-y")) bfr.Add_double(data_pos_y); + else if (String_.Eq(key, "maxWidth")) bfr.Add_int(max_width); + else if (String_.Eq(key, "toc")) bfr.Add_bry(toc); + else if (String_.Eq(key, "img_id_atr")) bfr.Add_bry(img_id_atr); + else if (String_.Eq(key, "img_xottl")) bfr.Add_bry(img_xottl_atr); + else if (String_.Eq(key, "img_xoimg")) bfr.Add_bry(img_xoimg_atr); + else if (String_.Eq(key, "file_ttl")) bfr.Add_bry(file_ttl); + else return false; + return true; + } + public Mustache_doc_itm[] Mustache__subs(String key) { + if (String_.Eq(key, "icons")) return icons; + else if (String_.Eq(key, "hasIcons")) return Mustache_doc_itm_.Ary__bool(icons.length > 0); + else if (String_.Eq(key, "bottomtoc")) return Mustache_doc_itm_.Ary__bool(bottomtoc); + else if (String_.Eq(key, "isHeadingOverrideEnabled")) return Mustache_doc_itm_.Ary__bool(isHeadingOverrideEnabled); + else if (String_.Eq(key, "isPanorama")) return Mustache_doc_itm_.Ary__bool(isPanorama); + else if (String_.Eq(key, "enable-toc")) return Mustache_doc_itm_.Ary__bool(enable_toc); + else if (String_.Eq(key, "hasPosition")) return Mustache_doc_itm_.Ary__bool(hasPosition); + return Mustache_doc_itm_.Ary__empty; + } + private static final byte[] Bry__anch_atr_id = Bry_.new_a7(" id=\"xoimg_"); + public static final byte[] + Atr_key__srcset = Bry_.new_a7("srcset") + , Atr_key__data_pos_x = Bry_.new_a7("data-pos-x") + , Atr_key__data_pos_y = Bry_.new_a7("data-pos-y") + , Atr_val__style_dflt = Bry_.new_a7("max-width:-1px") + ; + public Json_nde Mustache__json() { + return buildargs(); + } + private Json_nde buildargs() { + Json_nde jnde = Json_nde.NewByVal(); + jnde.AddKvStr("title", banner_hdr_text); + jnde.AddKvStr("tooltip", banner_anch_title); + jnde.AddKvStr("bannerfile", banner_anch_href); + jnde.AddKvStr("banner", banner_img_src); + jnde.AddKvStr("srcset", srcset == null ? Bry_.Empty : Bry_.Empty); + jnde.AddKvStr("originx", originx); + jnde.AddKvStr("toc", toc); + jnde.AddKvStr("img_id_atr", img_id_atr); + jnde.AddKvStr("img_xottl", img_xottl_atr); + jnde.AddKvStr("img_xoimg", img_xoimg_atr); + jnde.AddKvStr("file_ttl", file_ttl); + jnde.AddKvDouble("data-pos-x", data_pos_x); + jnde.AddKvDouble("data-pos-y", data_pos_y); + jnde.AddKvInt("maxWidth", max_width); + jnde.AddKvBool("hasIcons", icons.length > 0); + jnde.AddKvBool("bottomtoc", bottomtoc); + jnde.AddKvBool("isHeadingOverrideEnabled", isHeadingOverrideEnabled); + jnde.AddKvBool("isPanorama", isPanorama); + jnde.AddKvBool("enable-toc", enable_toc); + jnde.AddKvBool("hasPosition", hasPosition); + Json_ary ary = null; + if (json_icon_list != null) { + ary = Json_ary.NewByVal(); + int iconlen = json_icon_list.Len(); + for (int i = 0; i < iconlen; i++) { + Json_nde inde = (Json_nde)json_icon_list.Get_at(i); + ary.Add(inde); + } + if (iconlen > 0) { + jnde.AddKvAry("icons", ary); + } + } + int iconLen = icons.length; + if (iconLen > 0) { + ary = Json_ary.NewByVal(); + Bry_bfr tmpBfr = Bry_bfr_.New(); + for (int i = 0; i < iconLen; i++) { + Pgbnr_icon icon = icons[i]; + Json_nde iconNde = Pgbnr_iconx(tmpBfr, icon.Name(), icon.Title(), icon.Href()); + ary.Add(iconNde); + } + jnde.AddKvAry("icons", ary); + } + return jnde; + } + + private List_adp json_icon_list; + public void Add_new_icon(Bry_bfr tmp_bfr, byte[] name, byte[] title, byte[] href) { + if (json_icon_list == null) json_icon_list = List_adp_.New(); + json_icon_list.Add(Pgbnr_iconx(tmp_bfr, name, title, href)); + } + + private Json_nde Pgbnr_iconx(Bry_bfr tmp_bfr, byte[] name, byte[] title, byte[] href) { + fmt.Bld_many(tmp_bfr, name, title); + byte[] html = tmp_bfr.To_bry_and_clear(); + Json_nde jnde = Json_nde.NewByVal(); + jnde.AddKvStr("name", name); + jnde.AddKvStr("title", title); + jnde.AddKvStr("url", href); + jnde.AddKvStr("html", html); + return jnde; + } + + public static final Bry_fmt fmt = Bry_fmt.New + ( Bry_.New_u8_nl_apos("") + , "name", "title" + ); +} diff --git a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_xtn_mgr.java b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_xtn_mgr.java index 4c39199b6..5962d35b6 100644 --- a/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_xtn_mgr.java +++ b/400_xowa/src/gplx/xowa/xtns/pagebanners/Pgbnr_xtn_mgr.java @@ -18,7 +18,6 @@ package gplx.xowa.xtns.pagebanners; import gplx.Bry_; import gplx.Bry_bfr; import gplx.Int_ary_; -import gplx.Io_mgr; import gplx.core.brys.Bfr_arg; import gplx.langs.mustaches.Mustache_tkn_itm; import gplx.langs.mustaches.Mustache_tkn_parser; @@ -86,9 +85,11 @@ public class Pgbnr_xtn_mgr extends Xox_mgr_base implements Bfr_arg { if (!enabled) return; // load template file; - byte[] template_data = Io_mgr.Instance.LoadFilBryOr(wiki.Appe().Fsys_mgr().Bin_any_dir().GenSubDir_nest("xowa", "xtns", "WikidataPageBanner", "templates", "banner.mustache"), Template_dflt); - Mustache_tkn_parser parser = new Mustache_tkn_parser(); - template_root = parser.Parse(template_data, 0, template_data.length); + //byte[] template_data = Io_mgr.Instance.LoadFilBryOr(wiki.Appe().Fsys_mgr().Bin_any_dir().GenSubDir_nest("xowa", "xtns", "WikidataPageBanner", "templates", "banner.mustache"), Template_dflt); + //Mustache_tkn_parser parser = new Mustache_tkn_parser(); + //template_root = parser.Parse(template_data, 0, template_data.length); + Mustache_tkn_parser parser = new Mustache_tkn_parser(wiki.Appe().Fsys_mgr().Bin_any_dir().GenSubDir_nest("xowa", "xtns", "WikidataPageBanner", "templates")); + template_root = parser.Parse("banner", Template_dflt); } public Bfr_arg Write_html(Xoae_page wpg, Xop_ctx pctx, Xoh_wtr_ctx hctx) { this.wpg = wpg; this.pctx = pctx; this.hctx = hctx; diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text__json_util.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text__json_util.java index 97ccc72ab..e8ad57d61 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text__json_util.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text__json_util.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,225 +13,248 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; -import gplx.langs.jsons.*; -public class Scrib_lib_text__json_util { - private final Json_wtr wtr = new Json_wtr(); - public void Reindex_arrays(Scrib_lib_text__reindex_data rv, Keyval[] kv_ary, boolean is_encoding) { - int next = 0; - if (is_encoding) { - Array_.Sort(kv_ary, KeyVal__sorter__key_is_numeric.Instance); - next = 1; - } - boolean is_sequence = true; - int len = kv_ary.length; - for (int i = 0; i < len; ++i) { - Keyval kv = kv_ary[i]; - Object kv_val = kv.Val(); - if (kv_val != null && Type_.Eq(kv_val.getClass(), Keyval[].class)) { - Reindex_arrays(rv, (Keyval[])kv_val, is_encoding); - if (!rv.Rv_is_kvy()) - kv.Val_(rv.Rv_as_ary()); - } - if (is_sequence) { - if (kv.Key_tid() == Type_ids_.Id__int) { - int kv_key_as_int = Int_.Cast(kv.Key_as_obj()); - is_sequence = next++ == kv_key_as_int; - } - else { - int kv_key_to_int = Bry_.To_int_or__strict(Bry_.new_a7(kv.Key()), -1); // NOTE: a7 b/c proc is only checking for digits; none a7 bytes will be replaced by ? - if (is_encoding && kv_key_to_int != -1) { - is_sequence = next == kv_key_to_int; - ++next; - } - else - is_sequence = false; - } - } - } - if (is_sequence) { - if (is_encoding) { - Object rv_as_ary = To_array_values(kv_ary); - rv.Init(Bool_.N, null, rv_as_ary); - return; - } else { - Convert_to_base1(kv_ary); // PHP: return $arr ? array_combine( range( 1, count( $arr ) ), $arr ) : $arr; - } - } - rv.Init(Bool_.Y, kv_ary, null); - } - private static Object[] To_array_values(Keyval[] ary) { - int len = ary.length; - Object[] rv = new Object[len]; - for (int i = 0; i < len; ++i) { - Keyval itm = ary[i]; - rv[i] = itm.Val(); - } - return rv; - } - private static void Convert_to_base1(Keyval[] ary) { - int len = ary.length; - for (int i = 0; i < len; ++i) { - Keyval itm = ary[i]; - itm.Key_(i + 1); - } - } - public Keyval[] Decode_rslt_as_nde() {return decode_rslt_as_nde;} private Keyval[] decode_rslt_as_nde; - public Keyval[] Decode_rslt_as_ary() {return decode_rslt_as_ary;} private Keyval[] decode_rslt_as_ary; - public byte Decode(Json_parser parser, byte[] src, int flag) { - synchronized (wtr) { - Json_doc jdoc = parser.Parse(src); - if (jdoc.Root_grp().Tid() == Json_itm_.Tid__ary) { - this.decode_rslt_as_ary = Decode_ary_top(jdoc.Root_ary()); - return Bool_.N_byte; - } - else { - Json_nde root = (Json_nde)jdoc.Root_grp(); - int len = root.Len(); - this.decode_rslt_as_nde = new Keyval[len]; - for (int i = 0; i < len; ++i) { - Json_kv json_kv = root.Get_at_as_kv(i); - String kv_str = json_kv.Key_as_str(); - Object kv_val = Decode_obj(json_kv.Val()); - int kv_int = Int_.Parse_or(kv_str, Int_.Min_value); - decode_rslt_as_nde[i] = kv_int == Int_.Min_value ? Keyval_.new_(kv_str, kv_val) : Keyval_.int_(kv_int, kv_val); // use int_key if applicable; PAGE:it.s:Il_Re_Cervo; DATE:2015-12-06 - } - return Bool_.Y_byte; - } - } - } - private Object Decode_obj(Json_itm itm) { - int itm_tid = itm.Tid(); - switch (itm_tid) { - case Json_itm_.Tid__ary: return Decode_ary_sub(Json_ary.cast(itm)); - case Json_itm_.Tid__nde: return Decode_nde(Json_nde.cast(itm)); - default: return itm.Data(); - } - } - private Keyval[] Decode_ary_top(Json_ary ary) { // NOTE: top-level arrays must be returned as numbered nodes; EX: [{1:a}, {2:b}, {3:c}] not [a, b, c]; DATE:2016-08-01 - int len = ary.Len(); - Keyval[] rv = new Keyval[len]; - for (int i = 0; i < len; ++i) { - Json_itm itm = ary.Get_at(i); - rv[i] = Keyval_.int_(i + List_adp_.Base1, Decode_obj(itm)); - } - return rv; - } - private Object Decode_ary_sub(Json_ary ary) { - boolean subs_are_primitive = true; - - // if ary has sub_ary / sub_nde, then unflag subs_are_primitive - int len = ary.Len(); - if (len > 0) { - Json_itm sub = ary.Get_at(0); - switch (sub.Tid()) { - case Json_itm_.Tid__nde: - case Json_itm_.Tid__ary: - subs_are_primitive = false; - break; - } - } - - // generate array - Object[] rv = null; - // if subs_are_primitive, then just generate an Object[] - if (subs_are_primitive) { - rv = new Object[len]; - for (int i = 0; i < len; ++i) { - Json_itm itm = ary.Get_at(i); - rv[i] = Decode_obj(itm); - } - } - // else generate a Keyval where val is ary / nde - else { - rv = new Keyval[len]; - for (int i = 0; i < len; ++i) { - Json_itm itm = ary.Get_at(i); - rv[i] = Keyval_.int_(i, Decode_obj(itm)); - } - } - return rv; - } - private Keyval[] Decode_nde(Json_nde nde) { - int len = nde.Len(); - Keyval[] rv = new Keyval[len]; - for (int i = 0; i < len; ++i) { - Json_kv itm = nde.Get_at_as_kv(i); - String kv_str = itm.Key_as_str(); - int kv_int = Int_.Parse_or(kv_str, Int_.Min_value); - Object kv_val = Decode_obj(itm.Val()); - rv[i] = kv_int == Int_.Min_value ? Keyval_.new_(kv_str, kv_val) : Keyval_.int_(kv_int, kv_val); // use int_key if applicable; PAGE:it.s:Il_Re_Cervo; DATE:2015-12-06 - } - return rv; - } - public byte[] Encode_as_nde(Keyval[] itm, int flag, int skip) { - synchronized (wtr ) { - wtr.Clear().Doc_nde_bgn(); - Encode_kv_ary(itm); - return wtr.Doc_nde_end().To_bry_and_clear(); - } - } - public byte[] Encode_as_ary(Object ary, int flag, int skip) { - synchronized (wtr ) { - wtr.Clear().Doc_ary_bgn(); - Encode_ary(ary); - return wtr.Doc_ary_end().To_bry_and_clear(); - } - } - private void Encode_kv_ary(Keyval[] kv_ary) { - int len = kv_ary.length; - for (int i = 0; i < len; ++i) { - Keyval kv = kv_ary[i]; - Encode_kv(kv); - } - } - private void Encode_kv(Keyval kv) { - Object kv_val = kv.Val(); - Class type = Type_.Type_by_obj(kv_val); - if (Type_.Eq(type, Keyval[].class)) { - wtr.Nde_bgn(kv.Key()); - Encode_kv_ary((Keyval[])kv_val); - wtr.Nde_end(); - } - else if (Type_.Is_array(type)) { // encode as array - wtr.Ary_bgn(kv.Key()); - Encode_ary(kv_val); - wtr.Ary_end(); - } - else if (Type_.Eq(type, Int_.Cls_ref_type)) wtr.Kv_int(kv.Key(), Int_.Cast(kv_val)); - else if (Type_.Eq(type, Long_.Cls_ref_type)) wtr.Kv_long(kv.Key(), Long_.cast(kv_val)); - else if (Type_.Eq(type, Float_.Cls_ref_type)) wtr.Kv_float(kv.Key(), Float_.cast(kv_val)); - else if (Type_.Eq(type, Double_.Cls_ref_type)) wtr.Kv_double(kv.Key(), Double_.cast(kv_val)); - else if (Type_.Eq(type, Bool_.Cls_ref_type)) wtr.Kv_bool(kv.Key(), Bool_.Cast(kv_val)); - else wtr.Kv_str(kv.Key(), Object_.Xto_str_strict_or_null(kv_val)); - } - private void Encode_ary(Object ary) { - int ary_len = Array_.Len(ary); - for (int j = 0; j < ary_len; ++j) - wtr.Ary_itm_obj(Array_.Get_at(ary, j)); - } - public static final int - Flag__none = 0 - , Flag__preserve_keys = 1 - , Flag__try_fixing = 2 - , Flag__pretty = 4 - ; - public static final int - Skip__utf8 = 1 - , Skip__xml = 2 - , Skip__all = 3 - ; - public static final int - Opt__force_assoc = 1 - ; -} -class KeyVal__sorter__key_is_numeric implements gplx.core.lists.ComparerAble { - public int compare(Object lhsObj, Object rhsObj) { - Keyval lhs_itm = (Keyval)lhsObj; - Keyval rhs_itm = (Keyval)rhsObj; - int lhs_int = Int_.Parse_or(lhs_itm.Key(), Int_.Min_value); - int rhs_int = Int_.Parse_or(rhs_itm.Key(), Int_.Min_value); - return CompareAble_.Compare(lhs_int, rhs_int); - } - public static final KeyVal__sorter__key_is_numeric Instance = new KeyVal__sorter__key_is_numeric(); KeyVal__sorter__key_is_numeric() {} -} +package gplx.xowa.xtns.scribunto.libs; + +import gplx.Array_; +import gplx.Bool_; +import gplx.Bry_; +import gplx.CompareAble_; +import gplx.Double_; +import gplx.Float_; +import gplx.Int_; +import gplx.Keyval; +import gplx.Keyval_; +import gplx.List_adp_; +import gplx.Long_; +import gplx.Object_; +import gplx.Type_; +import gplx.Type_ids_; +import gplx.langs.jsons.Json_ary; +import gplx.langs.jsons.Json_doc; +import gplx.langs.jsons.Json_itm; +import gplx.langs.jsons.Json_itm_; +import gplx.langs.jsons.Json_kv; +import gplx.langs.jsons.Json_nde; +import gplx.langs.jsons.Json_parser; +import gplx.langs.jsons.Json_wtr; + +public class Scrib_lib_text__json_util { + private final Json_wtr wtr = new Json_wtr(); + public void Reindex_arrays(Scrib_lib_text__reindex_data rv, Keyval[] kv_ary, boolean is_encoding) { + int next = 0; + if (is_encoding) { + Array_.Sort(kv_ary, KeyVal__sorter__key_is_numeric.Instance); + next = 1; + } + boolean is_sequence = true; + int len = kv_ary.length; + for (int i = 0; i < len; ++i) { + Keyval kv = kv_ary[i]; + Object kv_val = kv.Val(); + if (kv_val != null && Type_.Eq(kv_val.getClass(), Keyval[].class)) { + Reindex_arrays(rv, (Keyval[])kv_val, is_encoding); + if (!rv.Rv_is_kvy()) + kv.Val_(rv.Rv_as_ary()); + } + if (is_sequence) { + if (kv.Key_tid() == Type_ids_.Id__int) { + int kv_key_as_int = Int_.Cast(kv.Key_as_obj()); + is_sequence = next++ == kv_key_as_int; + } + else { + int kv_key_to_int = Bry_.To_int_or__strict(Bry_.new_a7(kv.Key()), -1); // NOTE: a7 b/c proc is only checking for digits; none a7 bytes will be replaced by ? + if (is_encoding && kv_key_to_int != -1) { + is_sequence = next == kv_key_to_int; + ++next; + } + else + is_sequence = false; + } + } + } + if (is_sequence) { + if (is_encoding) { + Object rv_as_ary = To_array_values(kv_ary); + rv.Init(Bool_.N, null, rv_as_ary); + return; + } else { + Convert_to_base1(kv_ary); // PHP: return $arr ? array_combine( range( 1, count( $arr ) ), $arr ) : $arr; + } + } + rv.Init(Bool_.Y, kv_ary, null); + } + private static Object[] To_array_values(Keyval[] ary) { + int len = ary.length; + Object[] rv = new Object[len]; + for (int i = 0; i < len; ++i) { + Keyval itm = ary[i]; + rv[i] = itm.Val(); + } + return rv; + } + private static void Convert_to_base1(Keyval[] ary) { + int len = ary.length; + for (int i = 0; i < len; ++i) { + Keyval itm = ary[i]; + itm.Key_(i + 1); + } + } + public Keyval[] Decode_rslt_as_nde() {return decode_rslt_as_nde;} private Keyval[] decode_rslt_as_nde; + public Keyval[] Decode_rslt_as_ary() {return decode_rslt_as_ary;} private Keyval[] decode_rslt_as_ary; + public byte Decode(Json_parser parser, byte[] src, int flag) { + synchronized (wtr) { + Json_doc jdoc = parser.Parse(src); + if (jdoc.Root_grp().Tid() == Json_itm_.Tid__ary) { + this.decode_rslt_as_ary = Decode_ary_top(jdoc.Root_ary()); + return Bool_.N_byte; + } + else { + Json_nde root = (Json_nde)jdoc.Root_grp(); + int len = root.Len(); + this.decode_rslt_as_nde = new Keyval[len]; + for (int i = 0; i < len; ++i) { + Json_kv json_kv = root.Get_at_as_kv(i); + String kv_str = json_kv.Key_as_str(); + Object kv_val = Decode_obj(json_kv.Val()); + int kv_int = Int_.Parse_or(kv_str, Int_.Min_value); + decode_rslt_as_nde[i] = kv_int == Int_.Min_value ? Keyval_.new_(kv_str, kv_val) : Keyval_.int_(kv_int, kv_val); // use int_key if applicable; PAGE:it.s:Il_Re_Cervo; DATE:2015-12-06 + } + return Bool_.Y_byte; + } + } + } + private Object Decode_obj(Json_itm itm) { + int itm_tid = itm.Tid(); + switch (itm_tid) { + case Json_itm_.Tid__ary: return Decode_ary_sub(Json_ary.cast(itm)); + case Json_itm_.Tid__nde: return Decode_nde(Json_nde.Cast(itm)); + default: return itm.Data(); + } + } + private Keyval[] Decode_ary_top(Json_ary ary) { // NOTE: top-level arrays must be returned as numbered nodes; EX: [{1:a}, {2:b}, {3:c}] not [a, b, c]; DATE:2016-08-01 + int len = ary.Len(); + Keyval[] rv = new Keyval[len]; + for (int i = 0; i < len; ++i) { + Json_itm itm = ary.Get_at(i); + rv[i] = Keyval_.int_(i + List_adp_.Base1, Decode_obj(itm)); + } + return rv; + } + private Object Decode_ary_sub(Json_ary ary) { + boolean subs_are_primitive = true; + + // if ary has sub_ary / sub_nde, then unflag subs_are_primitive + int len = ary.Len(); + if (len > 0) { + Json_itm sub = ary.Get_at(0); + switch (sub.Tid()) { + case Json_itm_.Tid__nde: + case Json_itm_.Tid__ary: + subs_are_primitive = false; + break; + } + } + + // generate array + Object[] rv = null; + // if subs_are_primitive, then just generate an Object[] + if (subs_are_primitive) { + rv = new Object[len]; + for (int i = 0; i < len; ++i) { + Json_itm itm = ary.Get_at(i); + rv[i] = Decode_obj(itm); + } + } + // else generate a Keyval where val is ary / nde + else { + rv = new Keyval[len]; + for (int i = 0; i < len; ++i) { + Json_itm itm = ary.Get_at(i); + rv[i] = Keyval_.int_(i, Decode_obj(itm)); + } + } + return rv; + } + private Keyval[] Decode_nde(Json_nde nde) { + int len = nde.Len(); + Keyval[] rv = new Keyval[len]; + for (int i = 0; i < len; ++i) { + Json_kv itm = nde.Get_at_as_kv(i); + String kv_str = itm.Key_as_str(); + int kv_int = Int_.Parse_or(kv_str, Int_.Min_value); + Object kv_val = Decode_obj(itm.Val()); + rv[i] = kv_int == Int_.Min_value ? Keyval_.new_(kv_str, kv_val) : Keyval_.int_(kv_int, kv_val); // use int_key if applicable; PAGE:it.s:Il_Re_Cervo; DATE:2015-12-06 + } + return rv; + } + public byte[] Encode_as_nde(Keyval[] itm, int flag, int skip) { + synchronized (wtr ) { + wtr.Clear().Doc_nde_bgn(); + Encode_kv_ary(itm); + return wtr.Doc_nde_end().To_bry_and_clear(); + } + } + public byte[] Encode_as_ary(Object ary, int flag, int skip) { + synchronized (wtr ) { + wtr.Clear().Doc_ary_bgn(); + Encode_ary(ary); + return wtr.Doc_ary_end().To_bry_and_clear(); + } + } + private void Encode_kv_ary(Keyval[] kv_ary) { + int len = kv_ary.length; + for (int i = 0; i < len; ++i) { + Keyval kv = kv_ary[i]; + Encode_kv(kv); + } + } + private void Encode_kv(Keyval kv) { + Object kv_val = kv.Val(); + Class type = Type_.Type_by_obj(kv_val); + if (Type_.Eq(type, Keyval[].class)) { + wtr.Nde_bgn(kv.Key()); + Encode_kv_ary((Keyval[])kv_val); + wtr.Nde_end(); + } + else if (Type_.Is_array(type)) { // encode as array + wtr.Ary_bgn(kv.Key()); + Encode_ary(kv_val); + wtr.Ary_end(); + } + else if (Type_.Eq(type, Int_.Cls_ref_type)) wtr.Kv_int(kv.Key(), Int_.Cast(kv_val)); + else if (Type_.Eq(type, Long_.Cls_ref_type)) wtr.Kv_long(kv.Key(), Long_.cast(kv_val)); + else if (Type_.Eq(type, Float_.Cls_ref_type)) wtr.Kv_float(kv.Key(), Float_.cast(kv_val)); + else if (Type_.Eq(type, Double_.Cls_ref_type)) wtr.Kv_double(kv.Key(), Double_.cast(kv_val)); + else if (Type_.Eq(type, Bool_.Cls_ref_type)) wtr.Kv_bool(kv.Key(), Bool_.Cast(kv_val)); + else wtr.Kv_str(kv.Key(), Object_.Xto_str_strict_or_null(kv_val)); + } + private void Encode_ary(Object ary) { + int ary_len = Array_.Len(ary); + for (int j = 0; j < ary_len; ++j) + wtr.Ary_itm_obj(Array_.Get_at(ary, j)); + } + public static final int + Flag__none = 0 + , Flag__preserve_keys = 1 + , Flag__try_fixing = 2 + , Flag__pretty = 4 + ; + public static final int + Skip__utf8 = 1 + , Skip__xml = 2 + , Skip__all = 3 + ; + public static final int + Opt__force_assoc = 1 + ; +} +class KeyVal__sorter__key_is_numeric implements gplx.core.lists.ComparerAble { + public int compare(Object lhsObj, Object rhsObj) { + Keyval lhs_itm = (Keyval)lhsObj; + Keyval rhs_itm = (Keyval)rhsObj; + int lhs_int = Int_.Parse_or(lhs_itm.Key(), Int_.Min_value); + int rhs_int = Int_.Parse_or(rhs_itm.Key(), Int_.Min_value); + return CompareAble_.Compare((Integer)lhs_int, (Integer)rhs_int); + } + public static final KeyVal__sorter__key_is_numeric Instance = new KeyVal__sorter__key_is_numeric(); KeyVal__sorter__key_is_numeric() {} +} diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr.java b/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr.java index df09ae753..abcf3fd4b 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr.java @@ -63,11 +63,11 @@ import gplx.xowa.xtns.wbases.stores.Wbase_prop_mgr_loader_; import gplx.xowa.xtns.wbases.stores.Wbase_qid_mgr; public class Wdata_wiki_mgr implements Gfo_evt_itm, Gfo_invk { - private final Xoae_app app; - private final Wdata_prop_val_visitor prop_val_visitor; - private final Wdata_doc_parser wdoc_parser_v1 = new Wdata_doc_parser_v1(), wdoc_parser_v2 = new Wdata_doc_parser_v2(); - private final Object thread_lock = new Object(); - private final Bry_bfr tmp_bfr = Bry_bfr_.New_w_size(32); + private final Xoae_app app; + private final Wdata_prop_val_visitor prop_val_visitor; + private final Wdata_doc_parser wdoc_parser_v1 = new Wdata_doc_parser_v1(), wdoc_parser_v2 = new Wdata_doc_parser_v2(); + private final Object thread_lock = new Object(); + private final Bry_bfr tmp_bfr = Bry_bfr_.New_w_size(32); public Wdata_wiki_mgr(Xoae_app app) { this.app = app; this.evt_mgr = new Gfo_evt_mgr(this); @@ -78,11 +78,11 @@ public class Wdata_wiki_mgr implements Gfo_evt_itm, Gfo_invk { this.prop_val_visitor = new Wdata_prop_val_visitor(app, this); this.Enabled_(true); } - public Gfo_evt_mgr Evt_mgr() {return evt_mgr;} private final Gfo_evt_mgr evt_mgr; - public final Wbase_qid_mgr Qid_mgr; - public final Wbase_pid_mgr Pid_mgr; - public final Wbase_doc_mgr Doc_mgr; - public Wbase_prop_mgr Prop_mgr() {return prop_mgr;} private final Wbase_prop_mgr prop_mgr; + public Gfo_evt_mgr Evt_mgr() {return evt_mgr;} private final Gfo_evt_mgr evt_mgr; + public final Wbase_qid_mgr Qid_mgr; + public final Wbase_pid_mgr Pid_mgr; + public final Wbase_doc_mgr Doc_mgr; + public Wbase_prop_mgr Prop_mgr() {return prop_mgr;} private final Wbase_prop_mgr prop_mgr; public boolean Enabled() {return enabled;} private boolean enabled; public void Enabled_(boolean v) { this.enabled = v; @@ -109,7 +109,7 @@ public class Wdata_wiki_mgr implements Gfo_evt_itm, Gfo_invk { public Json_parser Jdoc_parser() {return jdoc_parser;} private Json_parser jdoc_parser = new Json_parser(); public void Init_by_app() {} public Wdata_doc_parser Wdoc_parser(Json_doc jdoc) { - Json_kv itm_0 = Json_kv.cast(jdoc.Root_nde().Get_at(0)); // get 1st node + Json_kv itm_0 = Json_kv.Cast(jdoc.Root_nde().Get_at(0)); // get 1st node return Bry_.Eq(itm_0.Key().Data_bry(), Wdata_doc_parser_v2.Bry_type) || Bry_.Eq(itm_0.Key().Data_bry(), Wdata_doc_parser_v2.Bry_id) ? wdoc_parser_v2 : wdoc_parser_v1; // if "type", must be v2 @@ -224,7 +224,7 @@ public class Wdata_wiki_mgr implements Gfo_evt_itm, Gfo_invk { public static final String Ns_lexeme_name = "Lexeme"; public static final byte[] Ns_lexeme_name_bry = Bry_.new_a7(Ns_lexeme_name); - public static final byte[] Html_json_id = Bry_.new_a7("xowa-wikidata-json"); + public static final byte[] Html_json_id = Bry_.new_a7("xowa-wikidata-json"); public static boolean Wiki_page_is_json(int wiki_tid, int ns_id) { switch (wiki_tid) { case Xow_domain_tid_.Tid__wikidata: diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr_fxt.java b/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr_fxt.java index 970dfb49a..bb637a940 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr_fxt.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/Wdata_wiki_mgr_fxt.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,193 +13,234 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.wbases; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; -import gplx.core.primitives.*; import gplx.langs.jsons.*; -import gplx.xowa.wikis.nss.*; import gplx.xowa.wikis.domains.*; -import gplx.xowa.xtns.wbases.core.*; import gplx.xowa.xtns.wbases.pfuncs.*; import gplx.xowa.xtns.wbases.claims.*; import gplx.xowa.xtns.wbases.claims.enums.*; import gplx.xowa.xtns.wbases.claims.itms.*; import gplx.xowa.xtns.wbases.stores.*; -public class Wdata_wiki_mgr_fxt { - private Xoae_app app; private Xowe_wiki wiki; private Wdata_doc_bldr wdoc_bldr; - private final Wdata_xwiki_link_wtr wdata_lang_wtr = new Wdata_xwiki_link_wtr(); - private final Bry_bfr tmp_time_bfr = Bry_bfr_.New(); - public Xowe_wiki Wiki() {return parser_fxt.Wiki();} - public Wdata_wiki_mgr_fxt Init() {return Init(new Xop_fxt(), true);} - public Wdata_wiki_mgr_fxt Init(Xop_fxt parser_fxt, boolean reset) { - this.parser_fxt = parser_fxt; - this.wiki = parser_fxt.Wiki(); - app = wiki.Appe(); - app.Xwiki_mgr__sitelink_mgr().Init_by_app(); - wdoc_bldr = new Wdata_doc_bldr(); - wdata_mgr = app.Wiki_mgr().Wdata_mgr(); - wdata_mgr.Prop_mgr().Loader_(Wbase_prop_mgr_loader_.New_mock()); - wdata_mgr.Clear(); - if (reset) { - Io_mgr.Instance.InitEngine_mem(); - parser_fxt.Reset(); - } - return this; - } - public Xoae_app App() {return app;} - public Wdata_wiki_mgr Wdata_mgr() {return wdata_mgr;} private Wdata_wiki_mgr wdata_mgr; - public Xop_fxt Parser_fxt() {return parser_fxt;} private Xop_fxt parser_fxt; - public Wdata_doc_bldr Wdoc_bldr(String qid) {return wdoc_bldr.Qid_(qid);} - public Json_doc Make_json(String src) {return app.Utl__json_parser().Parse_by_apos(src);} - - public Wbase_claim_base Make_claim_novalue(int pid) {return Wbase_claim_value.New_novalue(pid);} - public Wbase_claim_base Make_claim_somevalue(int pid) {return Wbase_claim_value.New_somevalue(pid);} - public Wbase_claim_base Make_claim_string(int pid, String val) {return Make_claim_string(pid, Bry_.new_u8(val));} - public Wbase_claim_base Make_claim_string(int pid, byte[] val) {return new Wbase_claim_string(pid, Wbase_claim_value_type_.Tid__value, val);} - public Wbase_claim_base Make_claim_time(int pid, String val) {return Make_claim_time(pid, val, Bry_.Empty, Bry_.Empty);} - public Wbase_claim_base Make_claim_time(int pid, String val, int precision) {return Make_claim_time(pid, val, Int_.To_bry(precision), Bry_.Empty);} - public Wbase_claim_base Make_claim_time(int pid, String val, byte[] precision, byte[] calendar) { - return new Wbase_claim_time(pid, Wbase_claim_value_type_.Tid__value, Wbase_claim_time_.To_bry(tmp_time_bfr, val), Bry_.Empty, Bry_.Empty, Bry_.Empty, precision, calendar); - } - public Wbase_claim_base Make_claim_monolingual(int pid, String lang, String text) {return new Wbase_claim_monolingualtext(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_u8(lang), Bry_.new_u8(text));} - public Wbase_claim_base Make_claim_quantity(int pid, String amount, String unit, String ubound, String lbound) {return new Wbase_claim_quantity(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_a7(amount), Bry_.new_a7(unit), Bry_.new_a7(ubound), Bry_.new_a7(lbound));} - public Wbase_claim_base Make_claim_entity_qid(int pid, int val) {return new Wbase_claim_entity(pid, Wbase_claim_value_type_.Tid__value, Wbase_claim_entity_type_.Tid__item, Int_.To_bry(val));} - public Wbase_claim_base Make_claim_entity_pid(int pid, int val) {return new Wbase_claim_entity(pid, Wbase_claim_value_type_.Tid__value, Wbase_claim_entity_type_.Tid__property, Int_.To_bry(val));} - public Wbase_claim_base Make_claim_geo(int pid, String lon, String lat) {return Make_claim_geo(pid, lon, lat, ".00001", null, "http://www.wikidata.org/entity/Q2");} - public Wbase_claim_base Make_claim_geo(int pid, String lon, String lat, String prc, String alt, String glb) { - return new Wbase_claim_globecoordinate(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_a7(lat), Bry_.new_a7(lon), Bry_.new_a7(alt), Bry_.new_a7(prc), Bry_.new_a7(glb)); - } - public Wbase_claim_grp Make_qualifiers_grp(int pid, Wbase_claim_base... ary) {return new Wbase_claim_grp(Int_obj_ref.New(pid), ary);} - public Wbase_claim_grp_list Make_qualifiers(Wbase_claim_grp... ary) { - Wbase_claim_grp_list rv = new Wbase_claim_grp_list(); - int len = ary.length; - for (int i = 0; i < len; ++i) - rv.Add(ary[i]); - return rv; - } - public Wbase_references_grp[] Make_references(Wbase_references_grp... ary) { - return ary; - } - public Wbase_references_grp Make_reference_grp(String hash, int[] snaks_order, Wbase_claim_grp... snaks) { - Wbase_claim_grp_list list = new Wbase_claim_grp_list(); - for (Wbase_claim_grp itm : snaks) - list.Add(itm); - return new Wbase_references_grp(Bry_.new_u8(hash), list, snaks_order); - } - public Wbase_claim_grp Make_reference_itm(int id, Wbase_claim_base... itms) { - return new Wbase_claim_grp(Int_obj_ref.New(id), itms); - } - - public Wdata_doc_bldr Wdoc(String qid) {return wdoc_bldr.Qid_(qid);} - public Wdata_doc doc_(String qid, Wbase_claim_base... props) {return wdoc_bldr.Qid_(qid).Add_claims(props).Xto_wdoc();} - - public void Init_pids_add(String lang_key, String pid_name, int pid) {wdata_mgr.Pid_mgr.Add(Bry_.new_u8(lang_key + "|" + pid_name), pid);} - public void Init_qids_add(String lang_key, int wiki_tid, String ttl, String qid) { - Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); - wdata_mgr.Qid_mgr.Add(tmp_bfr, Bry_.new_a7(lang_key), wiki_tid, Bry_.new_a7("000"), Bry_.new_a7(ttl), Bry_.new_a7(qid)); - tmp_bfr.Mkr_rls(); - } - public void Init_lang_fallbacks(String... fallbacks) {wiki.Lang().Fallback_bry_(Bry_.new_a7(String_.Concat_with_str(",", fallbacks)));} - public void Init_xwikis_add(String... prefixes) { - int len = prefixes.length; - for (int i = 0; i < len; i++) { - String prefix = prefixes[i]; - wiki.Xwiki_mgr().Add_by_atrs(prefix, prefix + ".wikipedia.org"); - } - } - public void Init_external_links_mgr_clear() {wiki.Parser_mgr().Ctx().Page().Wdata_external_lang_links().Reset();} - public void Init_external_links_mgr_add(String... langs) { - Wdata_external_lang_links_data external_lang_links = wiki.Parser_mgr().Ctx().Page().Wdata_external_lang_links(); - external_lang_links.Enabled_(true); - int len = langs.length; - for (int i = 0; i < len; i++) { - String lang = langs[i]; - if (String_.Eq(lang, "*")) - external_lang_links.Sort_(true); - else - external_lang_links.Langs_add(Bry_.new_a7(lang)); - } - } - - public void Init__docs__add(Wdata_doc_bldr bldr) {Init__docs__add(bldr.Xto_wdoc());} - public void Init__docs__add(Wdata_doc wdoc) { - wdata_mgr.Doc_mgr.Add(wdoc.Qid(), wdoc); - - Bry_bfr tmp_bfr = Bry_bfr_.New(); - Wbase_qid_mgr qid_mgr = wdata_mgr.Qid_mgr; - Ordered_hash slinks = wdoc.Slink_list(); - int slinks_len = slinks.Len(); - for (int i = 0; i < slinks_len; i++) { - Wdata_sitelink_itm slink = (Wdata_sitelink_itm)slinks.Get_at(i); - Xow_domain_itm domain = Xow_abrv_wm_.Parse_to_domain_itm(slink.Site()); - Xoa_ttl page_ttl = wiki.Ttl_parse(slink.Name()); - qid_mgr.Add(tmp_bfr, domain.Lang_actl_key(), domain.Domain_type_id(), page_ttl.Ns().Num_bry(), page_ttl.Page_db(), wdoc.Qid()); - } - } - - public void Test_link(String ttl_str, String expd) {Test_link(Xoa_ttl.Parse(wiki, Xow_ns_.Tid__main, Bry_.new_u8(ttl_str)), expd);} - public void Test_link(Xoa_ttl ttl, String expd) { - byte[] qid_ttl = wdata_mgr.Qid_mgr.Get_qid_or_null(wiki, ttl); - Tfds.Eq(expd, String_.new_u8(qid_ttl)); - } - public void Test_parse_pid_null(String val) {Test_parse_pid(val, Wbase_pid.Id_null);} - public void Test_parse_pid(String val, int expd) {Tfds.Eq(expd, Wbase_statement_mgr_.Parse_pid(num_parser, Bry_.new_a7(val)));} private Gfo_number_parser num_parser = new Gfo_number_parser(); - public void Test_parse(String raw, String expd) { - parser_fxt.Test_parse_page_tmpl_str(raw, expd); - } - public void Test_parse_langs(String raw, String expd) { - // setup langs - Xoae_page page = wiki.Parser_mgr().Ctx().Page(); - app.Xwiki_mgr__sitelink_mgr().Parse(Bry_.new_u8(String_.Concat_lines_nl - ( "0|grp1" - , "1|en|English" - , "1|fr|French" - , "1|de|German" - , "1|pl|Polish" - ))); - wiki.Xwiki_mgr().Add_by_sitelink_mgr(); - wiki.Appe().Usere().Wiki().Xwiki_mgr().Add_by_csv(Bry_.new_a7(String_.Concat_lines_nl - ( "1|en.wikipedia.org|en.wikipedia.org" - , "1|fr.wikipedia.org|fr.wikipedia.org" - , "1|de.wikipedia.org|de.wikipedia.org" - , "1|pl.wikipedia.org|pl.wikipedia.org" - ))); - - parser_fxt.Page_ttl_("Q1_en"); - parser_fxt.Exec_parse_page_all_as_str(raw); - Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); - - wdata_lang_wtr.Page_(page).Bfr_arg__add(tmp_bfr); - Tfds.Eq_str_lines(expd, tmp_bfr.To_str_and_rls()); - } - public void Test_xwiki_links(String ttl, String... expd) { - tmp_langs.Clear(); - Wdata_xwiki_link_wtr.Write_wdata_links(tmp_langs, wiki, Xoa_ttl.Parse(wiki, Bry_.new_u8(ttl)), wiki.Parser_mgr().Ctx().Page().Wdata_external_lang_links()); - Tfds.Eq_ary_str(expd, Test_xwiki_links_xto_str_ary(tmp_langs)); - } private List_adp tmp_langs = List_adp_.New(); - String[] Test_xwiki_links_xto_str_ary(List_adp list) { - int len = list.Count(); - String[] rv = new String[len]; - for (int i = 0; i < len; i++) { - Wdata_sitelink_itm itm = (Wdata_sitelink_itm)list.Get_at(i); - rv[i] = String_.new_a7(itm.Page_ttl().Page_db()); - } - tmp_langs.Clear(); - return rv; - } - public void Test_write_json_as_html(String raw_str, String expd) { - byte[] raw_bry = Bry_.new_a7(raw_str); - raw_bry = gplx.langs.jsons.Json_parser_tst.Replace_apos(raw_bry); - Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512(); - Wdata_wiki_mgr.Write_json_as_html(wdata_mgr.Jdoc_parser(), bfr, raw_bry); - Tfds.Eq(expd, bfr.To_str_and_rls()); - } - public static String New_json(String entity_id, String grp_key, String[] grp_vals) { - Bry_bfr bfr = Bry_bfr_.New(); - bfr.Add_str_a7("{ 'entity':'").Add_str_u8(entity_id).Add_byte(Byte_ascii.Apos).Add_byte_nl(); - bfr.Add_str_a7(", 'datatype':'commonsMedia'\n"); - bfr.Add_str_a7(", '").Add_str_u8(grp_key).Add_str_a7("':").Add_byte_nl(); - int len = grp_vals.length; - for (int i = 0; i < len; i += 2) { - bfr.Add_byte_repeat(Byte_ascii.Space, 2); - bfr.Add_byte(i == 0 ? Byte_ascii.Curly_bgn : Byte_ascii.Comma).Add_byte(Byte_ascii.Space); - bfr.Add_byte(Byte_ascii.Apos).Add_str_u8(grp_vals[i ]).Add_byte(Byte_ascii.Apos).Add_byte(Byte_ascii.Colon); - bfr.Add_byte(Byte_ascii.Apos).Add_str_u8(grp_vals[i + 1]).Add_byte(Byte_ascii.Apos).Add_byte_nl(); - } - bfr.Add_str_a7(" }").Add_byte_nl(); - bfr.Add_str_a7("}").Add_byte_nl(); - return String_.Replace(bfr.To_str_and_clear(), "'", "\""); - } -} +package gplx.xowa.xtns.wbases; + +import gplx.Bry_; +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.Byte_ascii; +import gplx.Int_; +import gplx.Io_mgr; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.Ordered_hash; +import gplx.String_; +import gplx.Tfds; +import gplx.core.primitives.Gfo_number_parser; +import gplx.core.primitives.Int_obj_ref; +import gplx.langs.jsons.Json_doc; +import gplx.xowa.Xoa_ttl; +import gplx.xowa.Xoae_app; +import gplx.xowa.Xoae_page; +import gplx.xowa.Xop_fxt; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.wikis.domains.Xow_abrv_wm_; +import gplx.xowa.wikis.domains.Xow_domain_itm; +import gplx.xowa.wikis.nss.Xow_ns_; +import gplx.xowa.xtns.wbases.claims.Wbase_claim_grp; +import gplx.xowa.xtns.wbases.claims.Wbase_claim_grp_list; +import gplx.xowa.xtns.wbases.claims.Wbase_references_grp; +import gplx.xowa.xtns.wbases.claims.enums.Wbase_claim_entity_type_; +import gplx.xowa.xtns.wbases.claims.enums.Wbase_claim_value_type_; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_base; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_entity; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_globecoordinate; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_monolingualtext; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_quantity; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_string; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_time; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_time_; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_value; +import gplx.xowa.xtns.wbases.core.Wbase_pid; +import gplx.xowa.xtns.wbases.core.Wdata_sitelink_itm; +import gplx.xowa.xtns.wbases.pfuncs.Wbase_statement_mgr_; +import gplx.xowa.xtns.wbases.pfuncs.Wdata_external_lang_links_data; +import gplx.xowa.xtns.wbases.stores.Wbase_prop_mgr_loader_; +import gplx.xowa.xtns.wbases.stores.Wbase_qid_mgr; + +public class Wdata_wiki_mgr_fxt { + private Xoae_app app; private Xowe_wiki wiki; private Wdata_doc_bldr wdoc_bldr; + private final Wdata_xwiki_link_wtr wdata_lang_wtr = new Wdata_xwiki_link_wtr(); + private final Bry_bfr tmp_time_bfr = Bry_bfr_.New(); + public Xowe_wiki Wiki() {return parser_fxt.Wiki();} + public Wdata_wiki_mgr_fxt Init() {return Init(new Xop_fxt(), true);} + public Wdata_wiki_mgr_fxt Init(Xop_fxt parser_fxt, boolean reset) { + this.parser_fxt = parser_fxt; + this.wiki = parser_fxt.Wiki(); + app = wiki.Appe(); + app.Xwiki_mgr__sitelink_mgr().Init_by_app(); + wdoc_bldr = new Wdata_doc_bldr(); + wdata_mgr = app.Wiki_mgr().Wdata_mgr(); + wdata_mgr.Prop_mgr().Loader_(Wbase_prop_mgr_loader_.New_mock()); + wdata_mgr.Clear(); + if (reset) { + Io_mgr.Instance.InitEngine_mem(); + parser_fxt.Reset(); + } + return this; + } + public Xoae_app App() {return app;} + public Wdata_wiki_mgr Wdata_mgr() {return wdata_mgr;} private Wdata_wiki_mgr wdata_mgr; + public Xop_fxt Parser_fxt() {return parser_fxt;} private Xop_fxt parser_fxt; + public Wdata_doc_bldr Wdoc_bldr(String qid) {return wdoc_bldr.Qid_(qid);} + public Json_doc Make_json(String src) {return app.Utl__json_parser().Parse_by_apos(src);} + + public Wbase_claim_base Make_claim_novalue(int pid) {return Wbase_claim_value.New_novalue(pid);} + public Wbase_claim_base Make_claim_somevalue(int pid) {return Wbase_claim_value.New_somevalue(pid);} + public Wbase_claim_base Make_claim_string(int pid, String val) {return Make_claim_string(pid, Bry_.new_u8(val));} + public Wbase_claim_base Make_claim_string(int pid, byte[] val) {return new Wbase_claim_string(pid, Wbase_claim_value_type_.Tid__value, val);} + public Wbase_claim_base Make_claim_time(int pid, String val) {return Make_claim_time(pid, val, Bry_.Empty, Bry_.Empty);} + public Wbase_claim_base Make_claim_time(int pid, String val, int precision) {return Make_claim_time(pid, val, Int_.To_bry(precision), Bry_.Empty);} + public Wbase_claim_base Make_claim_time(int pid, String val, byte[] precision, byte[] calendar) { + return new Wbase_claim_time(pid, Wbase_claim_value_type_.Tid__value, Wbase_claim_time_.To_bry(tmp_time_bfr, val), Bry_.Empty, Bry_.Empty, Bry_.Empty, precision, calendar); + } + public Wbase_claim_base Make_claim_monolingual(int pid, String lang, String text) {return new Wbase_claim_monolingualtext(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_u8(lang), Bry_.new_u8(text));} + public Wbase_claim_base Make_claim_quantity(int pid, String amount, String unit, String ubound, String lbound) {return new Wbase_claim_quantity(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_a7(amount), Bry_.new_a7(unit), Bry_.new_a7(ubound), Bry_.new_a7(lbound));} + public Wbase_claim_base Make_claim_entity_qid(int pid, int val) {return new Wbase_claim_entity(pid, Wbase_claim_value_type_.Tid__value, Wbase_claim_entity_type_.Tid__item, Int_.To_bry(val));} + public Wbase_claim_base Make_claim_entity_pid(int pid, int val) {return new Wbase_claim_entity(pid, Wbase_claim_value_type_.Tid__value, Wbase_claim_entity_type_.Tid__property, Int_.To_bry(val));} + public Wbase_claim_base Make_claim_geo(int pid, String lon, String lat) {return Make_claim_geo(pid, lon, lat, ".00001", null, "http://www.wikidata.org/entity/Q2");} + public Wbase_claim_base Make_claim_geo(int pid, String lon, String lat, String prc, String alt, String glb) { + return new Wbase_claim_globecoordinate(pid, Wbase_claim_value_type_.Tid__value, Bry_.new_a7(lat), Bry_.new_a7(lon), Bry_.new_a7(alt), Bry_.new_a7(prc), Bry_.new_a7(glb)); + } + public Wbase_claim_grp Make_qualifiers_grp(int pid, Wbase_claim_base... ary) {return new Wbase_claim_grp(Int_obj_ref.New(pid), ary);} + public Wbase_claim_grp_list Make_qualifiers(Wbase_claim_grp... ary) { + Wbase_claim_grp_list rv = new Wbase_claim_grp_list(); + int len = ary.length; + for (int i = 0; i < len; ++i) + rv.Add(ary[i]); + return rv; + } + public Wbase_references_grp[] Make_references(Wbase_references_grp... ary) { + return ary; + } + public Wbase_references_grp Make_reference_grp(String hash, int[] snaks_order, Wbase_claim_grp... snaks) { + Wbase_claim_grp_list list = new Wbase_claim_grp_list(); + for (Wbase_claim_grp itm : snaks) + list.Add(itm); + return new Wbase_references_grp(Bry_.new_u8(hash), list, snaks_order); + } + public Wbase_claim_grp Make_reference_itm(int id, Wbase_claim_base... itms) { + return new Wbase_claim_grp(Int_obj_ref.New(id), itms); + } + + public Wdata_doc_bldr Wdoc(String qid) {return wdoc_bldr.Qid_(qid);} + public Wdata_doc doc_(String qid, Wbase_claim_base... props) {return wdoc_bldr.Qid_(qid).Add_claims(props).Xto_wdoc();} + + public void Init_pids_add(String lang_key, String pid_name, int pid) {wdata_mgr.Pid_mgr.Add(Bry_.new_u8(lang_key + "|" + pid_name), pid);} + public void Init_qids_add(String lang_key, int wiki_tid, String ttl, String qid) { + Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); + wdata_mgr.Qid_mgr.Add(tmp_bfr, Bry_.new_a7(lang_key), wiki_tid, Bry_.new_a7("000"), Bry_.new_a7(ttl), Bry_.new_a7(qid)); + tmp_bfr.Mkr_rls(); + } + public void Init_lang_fallbacks(String... fallbacks) {wiki.Lang().Fallback_bry_(Bry_.new_a7(String_.Concat_with_str(",", fallbacks)));} + public void Init_xwikis_add(String... prefixes) { + int len = prefixes.length; + for (int i = 0; i < len; i++) { + String prefix = prefixes[i]; + wiki.Xwiki_mgr().Add_by_atrs(prefix, prefix + ".wikipedia.org"); + } + } + public void Init_external_links_mgr_clear() {wiki.Parser_mgr().Ctx().Page().Wdata_external_lang_links().Reset();} + public void Init_external_links_mgr_add(String... langs) { + Wdata_external_lang_links_data external_lang_links = wiki.Parser_mgr().Ctx().Page().Wdata_external_lang_links(); + external_lang_links.Enabled_(true); + int len = langs.length; + for (int i = 0; i < len; i++) { + String lang = langs[i]; + if (String_.Eq(lang, "*")) + external_lang_links.Sort_(true); + else + external_lang_links.Langs_add(Bry_.new_a7(lang)); + } + } + + public void Init__docs__add(Wdata_doc_bldr bldr) {Init__docs__add(bldr.Xto_wdoc());} + public void Init__docs__add(Wdata_doc wdoc) { + wdata_mgr.Doc_mgr.Add(wdoc.Qid(), wdoc); + + Bry_bfr tmp_bfr = Bry_bfr_.New(); + Wbase_qid_mgr qid_mgr = wdata_mgr.Qid_mgr; + Ordered_hash slinks = wdoc.Slink_list(); + int slinks_len = slinks.Len(); + for (int i = 0; i < slinks_len; i++) { + Wdata_sitelink_itm slink = (Wdata_sitelink_itm)slinks.Get_at(i); + Xow_domain_itm domain = Xow_abrv_wm_.Parse_to_domain_itm(slink.Site()); + Xoa_ttl page_ttl = wiki.Ttl_parse(slink.Name()); + qid_mgr.Add(tmp_bfr, domain.Lang_actl_key(), domain.Domain_type_id(), page_ttl.Ns().Num_bry(), page_ttl.Page_db(), wdoc.Qid()); + } + } + + public void Test_link(String ttl_str, String expd) {Test_link(Xoa_ttl.Parse(wiki, Xow_ns_.Tid__main, Bry_.new_u8(ttl_str)), expd);} + public void Test_link(Xoa_ttl ttl, String expd) { + byte[] qid_ttl = wdata_mgr.Qid_mgr.Get_qid_or_null(wiki, ttl); + Tfds.Eq(expd, String_.new_u8(qid_ttl)); + } + public void Test_parse_pid_null(String val) {Test_parse_pid(val, Wbase_pid.Id_null);} + public void Test_parse_pid(String val, int expd) {Tfds.Eq(expd, Wbase_statement_mgr_.Parse_pid(num_parser, Bry_.new_a7(val)));} private Gfo_number_parser num_parser = new Gfo_number_parser(); + public void Test_parse(String raw, String expd) { + parser_fxt.Test_parse_page_tmpl_str(raw, expd); + } + public void Test_parse_langs(String raw, String expd) { + // setup langs + Xoae_page page = wiki.Parser_mgr().Ctx().Page(); + app.Xwiki_mgr__sitelink_mgr().Parse(Bry_.new_u8(String_.Concat_lines_nl + ( "0|grp1" + , "1|en|English" + , "1|fr|French" + , "1|de|German" + , "1|pl|Polish" + ))); + wiki.Xwiki_mgr().Add_by_sitelink_mgr(); + wiki.Appe().Usere().Wiki().Xwiki_mgr().Add_by_csv(Bry_.new_a7(String_.Concat_lines_nl + ( "1|en.wikipedia.org|en.wikipedia.org" + , "1|fr.wikipedia.org|fr.wikipedia.org" + , "1|de.wikipedia.org|de.wikipedia.org" + , "1|pl.wikipedia.org|pl.wikipedia.org" + ))); + + parser_fxt.Page_ttl_("Q1_en"); + parser_fxt.Exec_parse_page_all_as_str(raw); + Bry_bfr tmp_bfr = wiki.Utl__bfr_mkr().Get_b512(); + + wdata_lang_wtr.Page_(page).Bfr_arg__add(tmp_bfr); + Tfds.Eq_str_lines(expd, tmp_bfr.To_str_and_rls()); + } + public void Test_xwiki_links(String ttl, String... expd) { + tmp_langs.Clear(); + Wdata_xwiki_link_wtr.Write_wdata_links(tmp_langs, wiki, Xoa_ttl.Parse(wiki, Bry_.new_u8(ttl)), wiki.Parser_mgr().Ctx().Page().Wdata_external_lang_links()); + Tfds.Eq_ary_str(expd, Test_xwiki_links_xto_str_ary(tmp_langs)); + } private List_adp tmp_langs = List_adp_.New(); + String[] Test_xwiki_links_xto_str_ary(List_adp list) { + int len = list.Count(); + String[] rv = new String[len]; + for (int i = 0; i < len; i++) { + Wdata_sitelink_itm itm = (Wdata_sitelink_itm)list.Get_at(i); + rv[i] = String_.new_a7(itm.Page_ttl().Page_db()); + } + tmp_langs.Clear(); + return rv; + } + public void Test_write_json_as_html(String raw_str, String expd) { + byte[] raw_bry = Bry_.new_a7(raw_str); + raw_bry = Bry_.new_u8(Json_doc.Make_str_by_apos(raw_str)); + Bry_bfr bfr = wiki.Utl__bfr_mkr().Get_b512(); + Wdata_wiki_mgr.Write_json_as_html(wdata_mgr.Jdoc_parser(), bfr, raw_bry); + Tfds.Eq(expd, bfr.To_str_and_rls()); + } + public static String New_json(String entity_id, String grp_key, String[] grp_vals) { + Bry_bfr bfr = Bry_bfr_.New(); + bfr.Add_str_a7("{ 'entity':'").Add_str_u8(entity_id).Add_byte(Byte_ascii.Apos).Add_byte_nl(); + bfr.Add_str_a7(", 'datatype':'commonsMedia'\n"); + bfr.Add_str_a7(", '").Add_str_u8(grp_key).Add_str_a7("':").Add_byte_nl(); + int len = grp_vals.length; + for (int i = 0; i < len; i += 2) { + bfr.Add_byte_repeat(Byte_ascii.Space, 2); + bfr.Add_byte(i == 0 ? Byte_ascii.Curly_bgn : Byte_ascii.Comma).Add_byte(Byte_ascii.Space); + bfr.Add_byte(Byte_ascii.Apos).Add_str_u8(grp_vals[i ]).Add_byte(Byte_ascii.Apos).Add_byte(Byte_ascii.Colon); + bfr.Add_byte(Byte_ascii.Apos).Add_str_u8(grp_vals[i + 1]).Add_byte(Byte_ascii.Apos).Add_byte_nl(); + } + bfr.Add_str_a7(" }").Add_byte_nl(); + bfr.Add_str_a7("}").Add_byte_nl(); + return String_.Replace(bfr.To_str_and_clear(), "'", "\""); + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wbase_claim_factory.java b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wbase_claim_factory.java index 6e85a2e34..eb6a9785b 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wbase_claim_factory.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wbase_claim_factory.java @@ -39,11 +39,11 @@ public class Wbase_claim_factory { public Wbase_claim_base Parse(byte[] qid, int pid, byte snak_tid, Json_nde nde, byte value_tid, Json_itm value_itm) { switch (value_tid) { case Wbase_claim_type_.Tid__string: return new Wbase_claim_string(pid, snak_tid, value_itm.Data_bry()); - case Wbase_claim_type_.Tid__entity: return Parse_datavalue_entity (qid, pid, snak_tid, Json_nde.cast(value_itm)); - case Wbase_claim_type_.Tid__time: return Parse_datavalue_time (qid, pid, snak_tid, Json_nde.cast(value_itm)); - case Wbase_claim_type_.Tid__quantity: return Parse_datavalue_quantity (qid, pid, snak_tid, Json_nde.cast(value_itm)); - case Wbase_claim_type_.Tid__globecoordinate: return Parse_datavalue_globecoordinate (qid, pid, snak_tid, Json_nde.cast(value_itm)); - case Wbase_claim_type_.Tid__monolingualtext: return Parse_datavalue_monolingualtext (qid, pid, snak_tid, Json_nde.cast(value_itm)); + case Wbase_claim_type_.Tid__entity: return Parse_datavalue_entity (qid, pid, snak_tid, Json_nde.Cast(value_itm)); + case Wbase_claim_type_.Tid__time: return Parse_datavalue_time (qid, pid, snak_tid, Json_nde.Cast(value_itm)); + case Wbase_claim_type_.Tid__quantity: return Parse_datavalue_quantity (qid, pid, snak_tid, Json_nde.Cast(value_itm)); + case Wbase_claim_type_.Tid__globecoordinate: return Parse_datavalue_globecoordinate (qid, pid, snak_tid, Json_nde.Cast(value_itm)); + case Wbase_claim_type_.Tid__monolingualtext: return Parse_datavalue_monolingualtext (qid, pid, snak_tid, Json_nde.Cast(value_itm)); default: throw Err_.new_unhandled_default(value_tid); } } @@ -53,7 +53,7 @@ public class Wbase_claim_factory { byte[] numericId = null; byte[] id = null; for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); byte tid = Wbase_claim_entity_.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; byte[] subValBry = sub.Val().Data_bry(); switch (tid) { @@ -70,7 +70,7 @@ public class Wbase_claim_factory { int len = nde.Len(); byte[] lang = null, text = null; for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); byte tid = Wbase_claim_monolingualtext_.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; byte[] sub_val_bry = sub.Val().Data_bry(); switch (tid) { @@ -85,7 +85,7 @@ public class Wbase_claim_factory { int len = nde.Len(); byte[] lat = null, lng = null, alt = null, prc = null, glb = null; for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); byte tid = Wbase_claim_globecoordinate_.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; byte[] sub_val_bry = sub.Val().Data_bry(); switch (tid) { @@ -103,7 +103,7 @@ public class Wbase_claim_factory { int len = nde.Len(); byte[] amount = null, unit = null, ubound = null, lbound = null; for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); byte tid = Wbase_claim_quantity_.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; byte[] sub_val_bry = sub.Val().Data_bry(); switch (tid) { @@ -120,7 +120,7 @@ public class Wbase_claim_factory { int len = nde.Len(); byte[] time = null, timezone = null, before = null, after = null, precision = null, calendarmodel = null; for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); byte tid = Wbase_claim_time_.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; byte[] sub_val_bry = sub.Val().Data_bry(); switch (tid) { diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_claims_parser_v2.java b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_claims_parser_v2.java index 207c57e10..e23e2237f 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_claims_parser_v2.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_claims_parser_v2.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,135 +13,158 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.wbases.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.wbases.*; -import gplx.core.primitives.*; -import gplx.langs.jsons.*; import gplx.xowa.xtns.wbases.core.*; import gplx.xowa.xtns.wbases.claims.*; import gplx.xowa.xtns.wbases.claims.enums.*; import gplx.xowa.xtns.wbases.claims.itms.*; -class Wdata_claims_parser_v2 { - private final Wbase_claim_factory factory = new Wbase_claim_factory(); - public void Make_claim_itms(byte[] qid, List_adp claim_itms_list, byte[] src, Json_kv claim_grp) { - Json_ary claim_itms_ary = Json_ary.cast_or_null(claim_grp.Val()); - int claim_itms_len = claim_itms_ary.Len(); - int pid = Parse_pid(claim_grp.Key().Data_bry()); - for (int i = 0; i < claim_itms_len; ++i) { - Json_nde claim_itm_nde = Json_nde.cast(claim_itms_ary.Get_at(i)); - Wbase_claim_base itm = Parse_claim_itm(qid, claim_itm_nde, pid); - if (itm != null) // HACK: itm can be null if value is "somevalue"; DATE:2014-09-20 - claim_itms_list.Add(itm); - } - } - private Wbase_claim_base Parse_claim_itm(byte[] qid, Json_nde nde, int pid) { - int len = nde.Len(); - byte rank_tid = Wbase_claim_rank_.Tid__unknown; - Wbase_claim_base claim_itm = null; Wbase_claim_grp_list qualifiers = null; int[] qualifiers_order = null; Wbase_references_grp[] snaks_grp = null; - for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); - byte tid = Wdata_dict_claim.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_claim.Tid__mainsnak: claim_itm = Parse_mainsnak(qid, Json_nde.cast(sub.Val()), pid); break; - case Wdata_dict_claim.Tid__rank: rank_tid = Wbase_claim_rank_.Reg.Get_tid_or(sub.Val().Data_bry(), Wbase_claim_rank_.Tid__unknown); break; - case Wdata_dict_claim.Tid__references: snaks_grp = Parse_references(qid, Json_ary.cast_or_null(sub.Val())); break; - case Wdata_dict_claim.Tid__qualifiers: qualifiers = Parse_qualifiers(qid, Json_nde.cast(sub.Val())); break; - case Wdata_dict_claim.Tid__qualifiers_order: qualifiers_order = Parse_pid_order(Json_ary.cast_or_null(sub.Val())); break; - case Wdata_dict_claim.Tid__type: break; // ignore: "statement" - case Wdata_dict_claim.Tid__id: break; // ignore: "Q2$F909BD1C-D34D-423F-9ED2-3493663321AF" - } - } - if (claim_itm != null) { - claim_itm.Rank_tid_(rank_tid); - if (qualifiers != null) claim_itm.Qualifiers_(qualifiers); - if (qualifiers_order != null) claim_itm.Qualifiers_order_(qualifiers_order); - if (snaks_grp != null) claim_itm.References_(snaks_grp); - } - return claim_itm; - } - public Wbase_references_grp[] Parse_references(byte[] qid, Json_ary owner) { - int len = owner.Len(); - Wbase_references_grp[] rv = new Wbase_references_grp[len]; - for (int i = 0; i < len; ++i) { - Json_nde grp_nde = Json_nde.cast(owner.Get_at(i)); - rv[i] = Parse_references_grp(qid, grp_nde); - } - return rv; - } - private Wbase_references_grp Parse_references_grp(byte[] qid, Json_nde owner) { - byte[] hash = null; - Wbase_claim_grp_list snaks = null; - int[] snaks_order = null; - int len = owner.Len(); - for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(owner.Get_at(i)); - byte tid = Wdata_dict_reference.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_reference.Tid__hash: hash = sub.Val_as_bry(); break; - case Wdata_dict_reference.Tid__snaks: snaks = Parse_qualifiers(qid, Json_nde.cast(sub.Val())); break; - case Wdata_dict_reference.Tid__snaks_order: snaks_order = Parse_pid_order(Json_ary.cast_or_null(sub.Val())); break; - } - } - return new Wbase_references_grp(hash, snaks, snaks_order); - } - public Wbase_claim_grp_list Parse_qualifiers(byte[] qid, Json_nde qualifiers_nde) { - Wbase_claim_grp_list rv = new Wbase_claim_grp_list(); - if (qualifiers_nde == null) return rv; // NOTE:sometimes references can have 0 snaks; return back an empty Wbase_claim_grp_list, not null; PAGE:Птичкин,_Евгений_Николаевич; DATE:2015-02-16 - int len = qualifiers_nde.Len(); - for (int i = 0; i < len; ++i) { - Json_kv qualifier_kv = Json_kv.cast(qualifiers_nde.Get_at(i)); - int pid = Parse_pid(qualifier_kv.Key().Data_bry()); - Wbase_claim_grp claims_grp = Parse_props_grp(qid, pid, Json_ary.cast_or_null(qualifier_kv.Val())); - rv.Add(claims_grp); - } - return rv; - } - public int[] Parse_pid_order(Json_ary ary) { - int len = ary.Len(); - int[] rv = new int[len]; - for (int i = 0; i < len; ++i) { - Json_itm pid_itm = ary.Get_at(i); - rv[i] = Parse_pid(pid_itm.Data_bry()); - } - return rv; - } - private Wbase_claim_grp Parse_props_grp(byte[] qid, int pid, Json_ary props_ary) { - List_adp list = List_adp_.New(); - int len = props_ary.Len(); - for (int i = 0; i < len; ++i) { - Json_nde qualifier_nde = Json_nde.cast(props_ary.Get_at(i)); - Wbase_claim_base qualifier_itm = Parse_mainsnak(qid, qualifier_nde, pid); - list.Add(qualifier_itm); - } - return new Wbase_claim_grp(Int_obj_ref.New(pid), (Wbase_claim_base[])list.To_ary_and_clear(Wbase_claim_base.class)); - } - public Wbase_claim_base Parse_mainsnak(byte[] qid, Json_nde nde, int pid) { - int len = nde.Len(); - byte snak_tid = Byte_.Max_value_127; - for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); - byte tid = Wdata_dict_mainsnak.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_mainsnak.Tid__snaktype: snak_tid = Wbase_claim_value_type_.Reg.Get_tid_or_fail(sub.Val().Data_bry()); break; - case Wdata_dict_mainsnak.Tid__datavalue: return Parse_datavalue(qid, pid, snak_tid, Json_nde.cast(sub.Val())); - case Wdata_dict_mainsnak.Tid__datatype: break; // ignore: has values like "wikibase-property"; EX: www.wikidata.org/wiki/Property:P397; DATE:2015-06-12 - case Wdata_dict_mainsnak.Tid__property: break; // ignore: pid already available above - case Wdata_dict_mainsnak.Tid__hash: break; // ignore: "84487fc3f93b4f74ab1cc5a47d78f596f0b49390" - } - } - return new Wbase_claim_value(pid, Wbase_claim_type_.Tid__unknown, snak_tid); // NOTE: mainsnak can be null, especially for qualifiers; PAGE:Q2!P576; DATE:2014-09-20 - } - public Wbase_claim_base Parse_datavalue(byte[] qid, int pid, byte snak_tid, Json_nde nde) { - int len = nde.Len(); - Json_itm value_itm = null; byte value_tid = Wbase_claim_type_.Tid__unknown; - for (int i = 0; i < len; ++i) { - Json_kv sub = Json_kv.cast(nde.Get_at(i)); - byte tid = Wdata_dict_datavalue.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_datavalue.Tid__type: value_tid = Wbase_claim_type_.Get_tid_or_unknown(sub.Val().Data_bry()); break; - case Wdata_dict_datavalue.Tid__value: value_itm = sub.Val(); break; - case Wdata_dict_datavalue.Tid__error: break; // ignore: "Can only construct GlobeCoordinateValue with a String globe parameter" - } - } - return factory.Parse(qid, pid, snak_tid, nde, value_tid, value_itm); - } - private static int Parse_pid(byte[] pid_bry) { - int rv = Bry_.To_int_or(pid_bry, 1, pid_bry.length, -1); if (rv == -1) throw Err_.new_wo_type("invalid pid", "pid", String_.new_u8(pid_bry)); - return rv; - } -} +package gplx.xowa.xtns.wbases.parsers; + +import gplx.Bry_; +import gplx.Byte_; +import gplx.Err_; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.String_; +import gplx.core.primitives.Int_obj_ref; +import gplx.langs.jsons.Json_ary; +import gplx.langs.jsons.Json_itm; +import gplx.langs.jsons.Json_kv; +import gplx.langs.jsons.Json_nde; +import gplx.xowa.xtns.wbases.claims.Wbase_claim_grp; +import gplx.xowa.xtns.wbases.claims.Wbase_claim_grp_list; +import gplx.xowa.xtns.wbases.claims.Wbase_references_grp; +import gplx.xowa.xtns.wbases.claims.enums.Wbase_claim_rank_; +import gplx.xowa.xtns.wbases.claims.enums.Wbase_claim_type_; +import gplx.xowa.xtns.wbases.claims.enums.Wbase_claim_value_type_; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_base; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_value; +import gplx.xowa.xtns.wbases.core.Wdata_dict_claim; +import gplx.xowa.xtns.wbases.core.Wdata_dict_datavalue; +import gplx.xowa.xtns.wbases.core.Wdata_dict_mainsnak; +import gplx.xowa.xtns.wbases.core.Wdata_dict_reference; + +class Wdata_claims_parser_v2 { + private final Wbase_claim_factory factory = new Wbase_claim_factory(); + public void Make_claim_itms(byte[] qid, List_adp claim_itms_list, byte[] src, Json_kv claim_grp) { + Json_ary claim_itms_ary = Json_ary.cast_or_null(claim_grp.Val()); + int claim_itms_len = claim_itms_ary.Len(); + int pid = Parse_pid(claim_grp.Key().Data_bry()); + for (int i = 0; i < claim_itms_len; ++i) { + Json_nde claim_itm_nde = Json_nde.Cast(claim_itms_ary.Get_at(i)); + Wbase_claim_base itm = Parse_claim_itm(qid, claim_itm_nde, pid); + if (itm != null) // HACK: itm can be null if value is "somevalue"; DATE:2014-09-20 + claim_itms_list.Add(itm); + } + } + private Wbase_claim_base Parse_claim_itm(byte[] qid, Json_nde nde, int pid) { + int len = nde.Len(); + byte rank_tid = Wbase_claim_rank_.Tid__unknown; + Wbase_claim_base claim_itm = null; Wbase_claim_grp_list qualifiers = null; int[] qualifiers_order = null; Wbase_references_grp[] snaks_grp = null; + for (int i = 0; i < len; ++i) { + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); + byte tid = Wdata_dict_claim.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_claim.Tid__mainsnak: claim_itm = Parse_mainsnak(qid, Json_nde.Cast(sub.Val()), pid); break; + case Wdata_dict_claim.Tid__rank: rank_tid = Wbase_claim_rank_.Reg.Get_tid_or(sub.Val().Data_bry(), Wbase_claim_rank_.Tid__unknown); break; + case Wdata_dict_claim.Tid__references: snaks_grp = Parse_references(qid, Json_ary.cast_or_null(sub.Val())); break; + case Wdata_dict_claim.Tid__qualifiers: qualifiers = Parse_qualifiers(qid, Json_nde.Cast(sub.Val())); break; + case Wdata_dict_claim.Tid__qualifiers_order: qualifiers_order = Parse_pid_order(Json_ary.cast_or_null(sub.Val())); break; + case Wdata_dict_claim.Tid__type: break; // ignore: "statement" + case Wdata_dict_claim.Tid__id: break; // ignore: "Q2$F909BD1C-D34D-423F-9ED2-3493663321AF" + } + } + if (claim_itm != null) { + claim_itm.Rank_tid_(rank_tid); + if (qualifiers != null) claim_itm.Qualifiers_(qualifiers); + if (qualifiers_order != null) claim_itm.Qualifiers_order_(qualifiers_order); + if (snaks_grp != null) claim_itm.References_(snaks_grp); + } + return claim_itm; + } + public Wbase_references_grp[] Parse_references(byte[] qid, Json_ary owner) { + int len = owner.Len(); + Wbase_references_grp[] rv = new Wbase_references_grp[len]; + for (int i = 0; i < len; ++i) { + Json_nde grp_nde = Json_nde.Cast(owner.Get_at(i)); + rv[i] = Parse_references_grp(qid, grp_nde); + } + return rv; + } + private Wbase_references_grp Parse_references_grp(byte[] qid, Json_nde owner) { + byte[] hash = null; + Wbase_claim_grp_list snaks = null; + int[] snaks_order = null; + int len = owner.Len(); + for (int i = 0; i < len; ++i) { + Json_kv sub = Json_kv.Cast(owner.Get_at(i)); + byte tid = Wdata_dict_reference.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_reference.Tid__hash: hash = sub.Val_as_bry(); break; + case Wdata_dict_reference.Tid__snaks: snaks = Parse_qualifiers(qid, Json_nde.Cast(sub.Val())); break; + case Wdata_dict_reference.Tid__snaks_order: snaks_order = Parse_pid_order(Json_ary.cast_or_null(sub.Val())); break; + } + } + return new Wbase_references_grp(hash, snaks, snaks_order); + } + public Wbase_claim_grp_list Parse_qualifiers(byte[] qid, Json_nde qualifiers_nde) { + Wbase_claim_grp_list rv = new Wbase_claim_grp_list(); + if (qualifiers_nde == null) return rv; // NOTE:sometimes references can have 0 snaks; return back an empty Wbase_claim_grp_list, not null; PAGE:Птичкин,_Евгений_Николаевич; DATE:2015-02-16 + int len = qualifiers_nde.Len(); + for (int i = 0; i < len; ++i) { + Json_kv qualifier_kv = Json_kv.Cast(qualifiers_nde.Get_at(i)); + int pid = Parse_pid(qualifier_kv.Key().Data_bry()); + Wbase_claim_grp claims_grp = Parse_props_grp(qid, pid, Json_ary.cast_or_null(qualifier_kv.Val())); + rv.Add(claims_grp); + } + return rv; + } + public int[] Parse_pid_order(Json_ary ary) { + int len = ary.Len(); + int[] rv = new int[len]; + for (int i = 0; i < len; ++i) { + Json_itm pid_itm = ary.Get_at(i); + rv[i] = Parse_pid(pid_itm.Data_bry()); + } + return rv; + } + private Wbase_claim_grp Parse_props_grp(byte[] qid, int pid, Json_ary props_ary) { + List_adp list = List_adp_.New(); + int len = props_ary.Len(); + for (int i = 0; i < len; ++i) { + Json_nde qualifier_nde = Json_nde.Cast(props_ary.Get_at(i)); + Wbase_claim_base qualifier_itm = Parse_mainsnak(qid, qualifier_nde, pid); + list.Add(qualifier_itm); + } + return new Wbase_claim_grp(Int_obj_ref.New(pid), (Wbase_claim_base[])list.To_ary_and_clear(Wbase_claim_base.class)); + } + public Wbase_claim_base Parse_mainsnak(byte[] qid, Json_nde nde, int pid) { + int len = nde.Len(); + byte snak_tid = Byte_.Max_value_127; + for (int i = 0; i < len; ++i) { + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); + byte tid = Wdata_dict_mainsnak.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_mainsnak.Tid__snaktype: snak_tid = Wbase_claim_value_type_.Reg.Get_tid_or_fail(sub.Val().Data_bry()); break; + case Wdata_dict_mainsnak.Tid__datavalue: return Parse_datavalue(qid, pid, snak_tid, Json_nde.Cast(sub.Val())); + case Wdata_dict_mainsnak.Tid__datatype: break; // ignore: has values like "wikibase-property"; EX: www.wikidata.org/wiki/Property:P397; DATE:2015-06-12 + case Wdata_dict_mainsnak.Tid__property: break; // ignore: pid already available above + case Wdata_dict_mainsnak.Tid__hash: break; // ignore: "84487fc3f93b4f74ab1cc5a47d78f596f0b49390" + } + } + return new Wbase_claim_value(pid, Wbase_claim_type_.Tid__unknown, snak_tid); // NOTE: mainsnak can be null, especially for qualifiers; PAGE:Q2!P576; DATE:2014-09-20 + } + public Wbase_claim_base Parse_datavalue(byte[] qid, int pid, byte snak_tid, Json_nde nde) { + int len = nde.Len(); + Json_itm value_itm = null; byte value_tid = Wbase_claim_type_.Tid__unknown; + for (int i = 0; i < len; ++i) { + Json_kv sub = Json_kv.Cast(nde.Get_at(i)); + byte tid = Wdata_dict_datavalue.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_datavalue.Tid__type: value_tid = Wbase_claim_type_.Get_tid_or_unknown(sub.Val().Data_bry()); break; + case Wdata_dict_datavalue.Tid__value: value_itm = sub.Val(); break; + case Wdata_dict_datavalue.Tid__error: break; // ignore: "Can only construct GlobeCoordinateValue with a String globe parameter" + } + } + return factory.Parse(qid, pid, snak_tid, nde, value_tid, value_itm); + } + private static int Parse_pid(byte[] pid_bry) { + int rv = Bry_.To_int_or(pid_bry, 1, pid_bry.length, -1); if (rv == -1) throw Err_.new_wo_type("invalid pid", "pid", String_.new_u8(pid_bry)); + return rv; + } +} diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_fxt_base.java b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_fxt_base.java index 7942dac84..ebd0148ed 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_fxt_base.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_fxt_base.java @@ -52,8 +52,8 @@ import gplx.xowa.xtns.wbases.core.Wdata_sitelink_itm; abstract class Wdata_doc_parser_fxt_base { protected Wdata_doc_parser wdoc_parser; - private final Json_parser json_parser = new Json_parser(); - private final Bry_bfr tmp_time_bfr = Bry_bfr_.New(); + private final Json_parser json_parser = new Json_parser(); + private final Bry_bfr tmp_time_bfr = Bry_bfr_.New(); public void Init() { if (wdoc_parser == null) wdoc_parser = Make_parser(); } @@ -100,13 +100,13 @@ abstract class Wdata_doc_parser_fxt_base { } public void Test_qualifiers(String raw, Wbase_claim_base... expd_itms) { Json_doc jdoc = json_parser.Parse_by_apos(raw); - Json_nde qualifiers_nde = Json_nde.cast(Json_kv.cast(jdoc.Root_nde().Get_at(0)).Val()); + Json_nde qualifiers_nde = Json_nde.Cast(Json_kv.Cast(jdoc.Root_nde().Get_at(0)).Val()); Wbase_claim_grp_list actl = wdoc_parser.Parse_qualifiers(Q1_bry, qualifiers_nde); Tfds.Eq_ary_str(expd_itms, To_ary(actl)); } public void Test_references(String raw, int[] expd_order, Wbase_claim_base... expd_itms) { Json_doc jdoc = json_parser.Parse_by_apos(raw); - Json_ary owner = Json_ary.cast_or_null(Json_kv.cast(jdoc.Root_nde().Get_at(0)).Val()); + Json_ary owner = Json_ary.cast_or_null(Json_kv.Cast(jdoc.Root_nde().Get_at(0)).Val()); Wbase_references_grp[] actl = wdoc_parser.Parse_references(Q1_bry, owner); Wbase_references_grp actl_grp = actl[0]; Tfds.Eq_ary(expd_order, actl_grp.Snaks_order()); @@ -114,7 +114,7 @@ abstract class Wdata_doc_parser_fxt_base { } public void Test_pid_order(String raw, int... expd) { Json_doc jdoc = json_parser.Parse_by_apos(raw); - Json_ary nde = Json_ary.cast_or_null(Json_kv.cast(jdoc.Root_nde().Get_at(0)).Val()); + Json_ary nde = Json_ary.cast_or_null(Json_kv.Cast(jdoc.Root_nde().Get_at(0)).Val()); int[] actl = wdoc_parser.Parse_pid_order(Q1_bry, nde); Tfds.Eq_ary(expd, actl); } @@ -131,7 +131,7 @@ abstract class Wdata_doc_parser_fxt_base { } return (Wbase_claim_base[])rv.To_ary_and_clear(Wbase_claim_base.class); } - private static final byte[] Q1_bry = Bry_.new_a7("Q1"); + private static final byte[] Q1_bry = Bry_.new_a7("Q1"); } class Wdata_doc_parser_v2_fxt extends Wdata_doc_parser_fxt_base { @Override public Wdata_doc_parser Make_parser() {return new Wdata_doc_parser_v2();} diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v1.java b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v1.java index ae1384077..ccde06df1 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v1.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v1.java @@ -1,19 +1,19 @@ -/* -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.xtns.wbases.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.wbases.*; +/* +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.xtns.wbases.parsers; import gplx.*; import gplx.core.primitives.*; import gplx.langs.jsons.*; import gplx.xowa.xtns.wbases.core.*; import gplx.xowa.xtns.wbases.claims.*; import gplx.xowa.xtns.wbases.claims.enums.*; import gplx.xowa.xtns.wbases.claims.itms.*; public class Wdata_doc_parser_v1 implements Wdata_doc_parser { @@ -36,18 +36,18 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { } public Ordered_hash Parse_sitelinks(byte[] qid, Json_doc doc) { try { - Json_nde list_nde = Json_nde.cast(doc.Get_grp(Bry_links)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(Bry_links)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; Ordered_hash rv = Ordered_hash_.New_bry(); int list_len = list_nde.Len(); for (int i = 0; i < list_len; ++i) { - Json_kv wiki_kv = Json_kv.cast(list_nde.Get_at(i)); + Json_kv wiki_kv = Json_kv.Cast(list_nde.Get_at(i)); byte[] site_bry = wiki_kv.Key().Data_bry(); byte[] title_bry = null; byte[][] badges_bry_ary = null; if (wiki_kv.Val().Tid() == Json_itm_.Tid__nde) { // v1.2: "enwiki":{name:"Earth", badges:[]} - Json_nde val_nde = Json_nde.cast(wiki_kv.Val()); - Json_kv name_kv = Json_kv.cast(val_nde.Get_at(0)); + Json_nde val_nde = Json_nde.Cast(wiki_kv.Val()); + Json_kv name_kv = Json_kv.Cast(val_nde.Get_at(0)); title_bry = name_kv.Val().Data_bry(); - Json_kv badges_kv = Json_kv.cast(val_nde.Get_at(1)); + Json_kv badges_kv = Json_kv.Cast(val_nde.Get_at(1)); if (badges_kv != null) {// TEST:some tests do not define a badges nde; ignore if null; DATE:2014-09-19 Json_ary badges_ary = Json_ary.cast_or_null(badges_kv.Val()); badges_bry_ary = badges_ary.Xto_bry_ary(); @@ -65,11 +65,11 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { public Ordered_hash Parse_langvals(byte[] qid, Json_doc doc, boolean label_or_description) { try { byte[] langval_key = label_or_description ? Bry_label : Bry_description; - Json_nde list_nde = Json_nde.cast(doc.Get_grp(langval_key)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(langval_key)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; Ordered_hash rv = Ordered_hash_.New_bry(); int list_len = list_nde.Len(); for (int i = 0; i < list_len; ++i) { - Json_kv data_kv = Json_kv.cast(list_nde.Get_at(i)); + Json_kv data_kv = Json_kv.Cast(list_nde.Get_at(i)); byte[] lang_bry = data_kv.Key().Data_bry(); Wdata_langtext_itm itm = new Wdata_langtext_itm(lang_bry, data_kv.Val().Data_bry()); rv.Add(lang_bry, itm); @@ -79,11 +79,11 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { } public Ordered_hash Parse_aliases(byte[] qid, Json_doc doc) { try { - Json_nde list_nde = Json_nde.cast(doc.Get_grp(Bry_aliases)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(Bry_aliases)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; Ordered_hash rv = Ordered_hash_.New_bry(); int list_len = list_nde.Len(); for (int i = 0; i < list_len; ++i) { - Json_kv data_kv = Json_kv.cast(list_nde.Get_at(i)); + Json_kv data_kv = Json_kv.Cast(list_nde.Get_at(i)); byte[] lang_bry = data_kv.Key().Data_bry(); byte[][] vals_bry_ary = null; Json_itm data_val = data_kv.Val(); @@ -93,11 +93,11 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { vals_bry_ary = vals_ary.Xto_bry_ary(); break; case Json_itm_.Tid__nde: // EX: 'en':{'0:en_val_1','1:en_val_2'}; PAGE:wd.q:621080 DATE:2014-09-21 - Json_nde vals_nde = Json_nde.cast(data_val); + Json_nde vals_nde = Json_nde.Cast(data_val); int vals_len = vals_nde.Len(); vals_bry_ary = new byte[vals_len][]; for (int j = 0; j < vals_len; ++j) { - Json_kv vals_sub_kv = Json_kv.cast(vals_nde.Get_at(j)); + Json_kv vals_sub_kv = Json_kv.Cast(vals_nde.Get_at(j)); vals_bry_ary[j] = vals_sub_kv.Val().Data_bry(); } break; @@ -116,7 +116,7 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { byte[] src = doc.Src(); int len = list_nde.Len(); for (int i = 0; i < len; i++) { - Json_nde claim_nde = Json_nde.cast(list_nde.Get_at(i)); + Json_nde claim_nde = Json_nde.Cast(list_nde.Get_at(i)); Wbase_claim_base claim_itm = Make_claim_itm(src, claim_nde); temp_list.Add(claim_itm); } @@ -150,10 +150,10 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { int len = prop_nde.Len(); // should have 5 (m, q, g, rank, refs), but don't enforce (can rely on keys) Wbase_claim_base rv = null; for (int i = 0; i < len; i++) { - Json_kv kv = Json_kv.cast(prop_nde.Get_at(i)); + Json_kv kv = Json_kv.Cast(prop_nde.Get_at(i)); Json_itm kv_key = kv.Key(); - Byte_obj_val bv = (Byte_obj_val)Prop_key_hash.Get_by_mid(src, kv_key.Src_bgn(), kv_key.Src_end()); - if (bv == null) {Warn("invalid prop node: ~{0}", String_.new_u8(src, kv_key.Src_bgn(), kv_key.Src_end())); return null;} + Byte_obj_val bv = (Byte_obj_val)Prop_key_hash.Get_by_bry(kv_key.Data_bry()); + if (bv == null) {Warn("invalid prop node: ~{0}", String_.new_u8(kv_key.Data_bry())); return null;} switch (bv.Val()) { case Prop_tid_m: rv = New_prop_by_m(src, Json_ary.cast_or_null(kv.Val())); @@ -176,13 +176,13 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { } private Wbase_claim_base New_prop_by_m(byte[] src, Json_ary ary) { byte snak_tid = Wbase_claim_value_type_.Reg.Get_tid_or_fail(ary.Get_at(0).Data_bry()); - int pid = Json_itm_int.cast(ary.Get_at(1)).Data_as_int(); + int pid = Json_itm_int.Cast(ary.Get_at(1)).Data_as_int(); switch (snak_tid) { case Wbase_claim_value_type_.Tid__novalue : return Wbase_claim_value.New_novalue(pid); case Wbase_claim_value_type_.Tid__somevalue : return Wbase_claim_value.New_somevalue(pid); } Json_itm val_tid_itm = ary.Get_at(2); - byte val_tid = Wbase_claim_type_.Get_tid_or_unknown(src, val_tid_itm.Src_bgn(), val_tid_itm.Src_end()); + byte val_tid = Wbase_claim_type_.Get_tid_or_unknown(val_tid_itm.Data_bry()); return Make_itm(pid, snak_tid, val_tid, ary); } private Wbase_claim_base Make_itm(int pid, byte snak_tid, byte val_tid, Json_ary ary) { @@ -190,31 +190,31 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { case Wbase_claim_type_.Tid__string: return new Wbase_claim_string(pid, snak_tid, ary.Get_at(3).Data_bry()); case Wbase_claim_type_.Tid__entity: { - Json_nde sub_nde = Json_nde.cast(ary.Get_at(3)); - Json_kv entity_kv = Json_kv.cast(sub_nde.Get_at(1)); + Json_nde sub_nde = Json_nde.Cast(ary.Get_at(3)); + Json_kv entity_kv = Json_kv.Cast(sub_nde.Get_at(1)); return new Wbase_claim_entity(pid, snak_tid, Wbase_claim_entity_type_.Tid__item, entity_kv.Val().Data_bry()); } case Wbase_claim_type_.Tid__time: { - Json_nde sub_nde = Json_nde.cast(ary.Get_at(3)); + Json_nde sub_nde = Json_nde.Cast(ary.Get_at(3)); return new Wbase_claim_time(pid, snak_tid, Get_val(sub_nde, 0), Get_val(sub_nde, 1), Get_val(sub_nde, 2), Get_val(sub_nde, 3), Get_val(sub_nde, 4), Get_val(sub_nde, 5)); } case Wbase_claim_type_.Tid__globecoordinate: case Wbase_claim_type_.Tid__bad: { - Json_nde sub_nde = Json_nde.cast(ary.Get_at(3)); + Json_nde sub_nde = Json_nde.Cast(ary.Get_at(3)); return new Wbase_claim_globecoordinate(pid, snak_tid, Get_val(sub_nde, 0), Get_val(sub_nde, 1), Get_val(sub_nde, 2), Get_val(sub_nde, 3), Get_val(sub_nde, 4)); } case Wbase_claim_type_.Tid__quantity: { - Json_nde sub_nde = Json_nde.cast(ary.Get_at(3)); + Json_nde sub_nde = Json_nde.Cast(ary.Get_at(3)); return new Wbase_claim_quantity(pid, snak_tid, Get_val(sub_nde, 0), Get_val(sub_nde, 1), Get_val(sub_nde, 2), Get_val(sub_nde, 3)); } case Wbase_claim_type_.Tid__monolingualtext: { - Json_nde sub_nde = Json_nde.cast(ary.Get_at(3)); + Json_nde sub_nde = Json_nde.Cast(ary.Get_at(3)); return new Wbase_claim_monolingualtext(pid, snak_tid, Get_val(sub_nde, 1), Get_val(sub_nde, 0)); } default: {throw Err_.new_unhandled(val_tid);} } } private static byte[] Get_val(Json_nde sub_nde, int i) { - Json_kv kv = Json_kv.cast(sub_nde.Get_at(i)); + Json_kv kv = Json_kv.Cast(sub_nde.Get_at(i)); return kv.Val().Data_bry(); } private void Warn(String fmt, Object... args) {usr_dlg.Warn_many("", "", fmt, args);} @@ -227,11 +227,11 @@ public class Wdata_doc_parser_v1 implements Wdata_doc_parser { .Add_bry_byte(Wdata_dict_claim_v1.Bry_rank , Prop_tid_rank) .Add_bry_byte(Wdata_dict_claim_v1.Bry_refs , Prop_tid_refs); Ordered_hash Bld_hash(Json_doc doc, byte[] key) { - Json_nde nde = Json_nde.cast(doc.Get_grp(key)); if (nde == null) return Empty_ordered_hash_bry; + Json_nde nde = Json_nde.Cast(doc.Get_grp(key)); if (nde == null) return Empty_ordered_hash_bry; Ordered_hash rv = Ordered_hash_.New_bry(); int len = nde.Len(); for (int i = 0; i < len; i++) { - Json_kv kv = Json_kv.cast(nde.Get_at(i)); + Json_kv kv = Json_kv.Cast(nde.Get_at(i)); byte[] kv_key = kv.Key().Data_bry(); rv.Add(kv_key, kv); } diff --git a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v2.java b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v2.java index c48c34285..c5a21dc55 100644 --- a/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v2.java +++ b/400_xowa/src/gplx/xowa/xtns/wbases/parsers/Wdata_doc_parser_v2.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,133 +13,154 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.xtns.wbases.parsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.wbases.*; -import gplx.langs.jsons.*; import gplx.core.btries.*; -import gplx.xowa.xtns.wbases.core.*; import gplx.xowa.xtns.wbases.claims.*; import gplx.xowa.xtns.wbases.claims.enums.*; import gplx.xowa.xtns.wbases.claims.itms.*; -public class Wdata_doc_parser_v2 implements Wdata_doc_parser { - private Wdata_claims_parser_v2 claims_parser = new Wdata_claims_parser_v2(); - public byte[] Parse_qid(Json_doc doc) { - try { - Json_itm itm = doc.Find_nde(Bry_id); - return Bry_.Lcase__1st(itm.Data_bry()); // standardize on "q" instead of "Q" for compatibility with v1 - } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse qid", "src", String_.new_u8(doc.Src()));} - } - public Ordered_hash Parse_sitelinks(byte[] qid, Json_doc doc) { - try { - Json_nde list_nde = Json_nde.cast(doc.Get_grp(Bry_sitelinks)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; - Ordered_hash rv = Ordered_hash_.New_bry(); - int list_len = list_nde.Len(); - for (int i = 0; i < list_len; ++i) { - Json_kv data_kv = Json_kv.cast(list_nde.Get_at(i)); - Json_nde data_nde = Json_nde.cast(data_kv.Val()); - int data_nde_len = data_nde.Len(); - Json_kv site_kv = null, name_kv = null; Json_ary badges_ary = null; - for (int j = 0; j < data_nde_len; ++j) { - Json_kv sub = Json_kv.cast(data_nde.Get_at(j)); - byte tid = Wdata_dict_sitelink.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_sitelink.Tid__site: site_kv = Json_kv.cast(sub); break; - case Wdata_dict_sitelink.Tid__title: name_kv = Json_kv.cast(sub); break; - case Wdata_dict_sitelink.Tid__badges: badges_ary = Json_ary.cast_or_null(Json_kv.cast(sub).Val()); break; - } - } - byte[] site_bry = site_kv.Val().Data_bry(); - Wdata_sitelink_itm itm = new Wdata_sitelink_itm(site_bry, name_kv.Val().Data_bry(), badges_ary.Xto_bry_ary()); - rv.Add(site_bry, itm); - } - return rv; - } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse sitelinks", "qid", String_.new_u8(qid));} - } - public Ordered_hash Parse_langvals(byte[] qid, Json_doc doc, boolean label_or_description) { - try { - byte[] langval_key = label_or_description ? Bry_labels : Bry_descriptions; - Json_nde list_nde = Json_nde.cast(doc.Get_grp(langval_key)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; - Ordered_hash rv = Ordered_hash_.New_bry(); - int list_len = list_nde.Len(); - for (int i = 0; i < list_len; ++i) { - Json_kv data_kv = Json_kv.cast(list_nde.Get_at(i)); - Json_nde data_nde = Json_nde.cast(data_kv.Val()); - Json_kv text_kv = null; - int data_nde_len = data_nde.Len(); - for (int j = 0; j < data_nde_len; ++j) { - Json_kv sub = Json_kv.cast(data_nde.Get_at(j)); - byte tid = Wdata_dict_langtext.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_langtext.Tid__language: break; - case Wdata_dict_langtext.Tid__value: text_kv = Json_kv.cast(sub); break; - } - } - byte[] lang_bry = data_kv.Key().Data_bry(); - Wdata_langtext_itm itm = new Wdata_langtext_itm(lang_bry, text_kv.Val().Data_bry()); - rv.Add(lang_bry, itm); - } - return rv; - } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse langvals", "qid", String_.new_u8(qid), "langval_tid", label_or_description);} - } - public Ordered_hash Parse_aliases(byte[] qid, Json_doc doc) { - try { - Json_nde list_nde = Json_nde.cast(doc.Get_grp(Bry_aliases)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; - Ordered_hash rv = Ordered_hash_.New_bry(); - int list_len = list_nde.Len(); - for (int i = 0; i < list_len; ++i) { - Json_kv data_kv = Json_kv.cast(list_nde.Get_at(i)); - Json_ary vals_ary = Json_ary.cast_or_null(data_kv.Val()); - int vals_len = vals_ary.Len(); - byte[][] vals = new byte[vals_len][]; - for (int j = 0; j < vals_len; ++j) { - Json_nde lang_nde = Json_nde.cast(vals_ary.Get_at(j)); - int k_len = lang_nde.Len(); - for (int k = 0; k < k_len; ++k) { - Json_kv sub = Json_kv.cast(lang_nde.Get_at(k)); - byte tid = Wdata_dict_langtext.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; - switch (tid) { - case Wdata_dict_langtext.Tid__language: break; - case Wdata_dict_langtext.Tid__value: vals[j] = sub.Val().Data_bry(); break; - } - } - } - byte[] lang_bry = data_kv.Key().Data_bry(); - Wdata_alias_itm itm = new Wdata_alias_itm(lang_bry, vals); - rv.Add(lang_bry, itm); - } - return rv; - } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse sitelinks", "qid", String_.new_u8(qid));} - } - public Ordered_hash Parse_claims(byte[] qid, Json_doc doc) { - synchronized (this) {// TS; DATE:2016-07-06 - try { - Json_nde list_nde = Json_nde.cast(doc.Get_grp(Bry_claims)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_generic; - List_adp temp_list = List_adp_.New(); - byte[] src = doc.Src(); - int len = list_nde.Len(); - for (int i = 0; i < len; i++) { - Json_kv claim_nde = Json_kv.cast(list_nde.Get_at(i)); - claims_parser.Make_claim_itms(qid, temp_list, src, claim_nde); - } - return Wdata_doc_parser_v1.Claims_list_to_hash(temp_list); - } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse claims", "qid", String_.new_u8(doc.Src()));} - } - } - public Wbase_claim_base Parse_claims_data(byte[] qid, int pid, byte snak_tid, Json_nde nde) {return claims_parser.Parse_datavalue(qid, pid, snak_tid, nde);} - public Wbase_claim_grp_list Parse_qualifiers(byte[] qid, Json_nde nde) {return claims_parser.Parse_qualifiers(qid, nde);} - public Wbase_references_grp[] Parse_references(byte[] qid, Json_ary owner) {return claims_parser.Parse_references(qid, owner);} - public int[] Parse_pid_order(byte[] qid, Json_ary ary) {return claims_parser.Parse_pid_order(ary);} - public static final String - Str_id = "id" - , Str_sitelinks = "sitelinks" - , Str_labels = "labels" - , Str_descriptions = "descriptions" - , Str_aliases = "aliases" - , Str_claims = "claims" - , Str_type = "type" - ; - public static final byte[] - Bry_id = Bry_.new_a7(Str_id) - , Bry_sitelinks = Bry_.new_a7(Str_sitelinks) - , Bry_labels = Bry_.new_a7(Str_labels) - , Bry_descriptions = Bry_.new_a7(Str_descriptions) - , Bry_aliases = Bry_.new_a7(Str_aliases) - , Bry_claims = Bry_.new_a7(Str_claims) - , Bry_type = Bry_.new_a7(Str_type) - ; -} +package gplx.xowa.xtns.wbases.parsers; + +import gplx.Bry_; +import gplx.Byte_; +import gplx.Err_; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.Ordered_hash; +import gplx.Ordered_hash_; +import gplx.String_; +import gplx.langs.jsons.Json_ary; +import gplx.langs.jsons.Json_doc; +import gplx.langs.jsons.Json_itm; +import gplx.langs.jsons.Json_kv; +import gplx.langs.jsons.Json_nde; +import gplx.xowa.xtns.wbases.claims.Wbase_claim_grp_list; +import gplx.xowa.xtns.wbases.claims.Wbase_references_grp; +import gplx.xowa.xtns.wbases.claims.itms.Wbase_claim_base; +import gplx.xowa.xtns.wbases.core.Wdata_alias_itm; +import gplx.xowa.xtns.wbases.core.Wdata_dict_langtext; +import gplx.xowa.xtns.wbases.core.Wdata_dict_sitelink; +import gplx.xowa.xtns.wbases.core.Wdata_langtext_itm; +import gplx.xowa.xtns.wbases.core.Wdata_sitelink_itm; + +public class Wdata_doc_parser_v2 implements Wdata_doc_parser { + private Wdata_claims_parser_v2 claims_parser = new Wdata_claims_parser_v2(); + public byte[] Parse_qid(Json_doc doc) { + try { + Json_itm itm = doc.Find_nde(Bry_id); + return Bry_.Lcase__1st(itm.Data_bry()); // standardize on "q" instead of "Q" for compatibility with v1 + } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse qid", "src", String_.new_u8(doc.Src()));} + } + public Ordered_hash Parse_sitelinks(byte[] qid, Json_doc doc) { + try { + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(Bry_sitelinks)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; + Ordered_hash rv = Ordered_hash_.New_bry(); + int list_len = list_nde.Len(); + for (int i = 0; i < list_len; ++i) { + Json_kv data_kv = Json_kv.Cast(list_nde.Get_at(i)); + Json_nde data_nde = Json_nde.Cast(data_kv.Val()); + int data_nde_len = data_nde.Len(); + Json_kv site_kv = null, name_kv = null; Json_ary badges_ary = null; + for (int j = 0; j < data_nde_len; ++j) { + Json_kv sub = Json_kv.Cast(data_nde.Get_at(j)); + byte tid = Wdata_dict_sitelink.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_sitelink.Tid__site: site_kv = Json_kv.Cast(sub); break; + case Wdata_dict_sitelink.Tid__title: name_kv = Json_kv.Cast(sub); break; + case Wdata_dict_sitelink.Tid__badges: badges_ary = Json_ary.cast_or_null(Json_kv.Cast(sub).Val()); break; + } + } + byte[] site_bry = site_kv.Val().Data_bry(); + Wdata_sitelink_itm itm = new Wdata_sitelink_itm(site_bry, name_kv.Val().Data_bry(), badges_ary.Xto_bry_ary()); + rv.Add(site_bry, itm); + } + return rv; + } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse sitelinks", "qid", String_.new_u8(qid));} + } + public Ordered_hash Parse_langvals(byte[] qid, Json_doc doc, boolean label_or_description) { + try { + byte[] langval_key = label_or_description ? Bry_labels : Bry_descriptions; + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(langval_key)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; + Ordered_hash rv = Ordered_hash_.New_bry(); + int list_len = list_nde.Len(); + for (int i = 0; i < list_len; ++i) { + Json_kv data_kv = Json_kv.Cast(list_nde.Get_at(i)); + Json_nde data_nde = Json_nde.Cast(data_kv.Val()); + Json_kv text_kv = null; + int data_nde_len = data_nde.Len(); + for (int j = 0; j < data_nde_len; ++j) { + Json_kv sub = Json_kv.Cast(data_nde.Get_at(j)); + byte tid = Wdata_dict_langtext.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_langtext.Tid__language: break; + case Wdata_dict_langtext.Tid__value: text_kv = Json_kv.Cast(sub); break; + } + } + byte[] lang_bry = data_kv.Key().Data_bry(); + Wdata_langtext_itm itm = new Wdata_langtext_itm(lang_bry, text_kv.Val().Data_bry()); + rv.Add(lang_bry, itm); + } + return rv; + } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse langvals", "qid", String_.new_u8(qid), "langval_tid", label_or_description);} + } + public Ordered_hash Parse_aliases(byte[] qid, Json_doc doc) { + try { + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(Bry_aliases)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_bry; + Ordered_hash rv = Ordered_hash_.New_bry(); + int list_len = list_nde.Len(); + for (int i = 0; i < list_len; ++i) { + Json_kv data_kv = Json_kv.Cast(list_nde.Get_at(i)); + Json_ary vals_ary = Json_ary.cast_or_null(data_kv.Val()); + int vals_len = vals_ary.Len(); + byte[][] vals = new byte[vals_len][]; + for (int j = 0; j < vals_len; ++j) { + Json_nde lang_nde = Json_nde.Cast(vals_ary.Get_at(j)); + int k_len = lang_nde.Len(); + for (int k = 0; k < k_len; ++k) { + Json_kv sub = Json_kv.Cast(lang_nde.Get_at(k)); + byte tid = Wdata_dict_langtext.Reg.Get_tid_or_max_and_log(qid, sub.Key().Data_bry()); if (tid == Byte_.Max_value_127) continue; + switch (tid) { + case Wdata_dict_langtext.Tid__language: break; + case Wdata_dict_langtext.Tid__value: vals[j] = sub.Val().Data_bry(); break; + } + } + } + byte[] lang_bry = data_kv.Key().Data_bry(); + Wdata_alias_itm itm = new Wdata_alias_itm(lang_bry, vals); + rv.Add(lang_bry, itm); + } + return rv; + } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse sitelinks", "qid", String_.new_u8(qid));} + } + public Ordered_hash Parse_claims(byte[] qid, Json_doc doc) { + synchronized (this) {// TS; DATE:2016-07-06 + try { + Json_nde list_nde = Json_nde.Cast(doc.Get_grp(Bry_claims)); if (list_nde == null) return Wdata_doc_parser_v1.Empty_ordered_hash_generic; + List_adp temp_list = List_adp_.New(); + byte[] src = doc.Src(); + int len = list_nde.Len(); + for (int i = 0; i < len; i++) { + Json_kv claim_nde = Json_kv.Cast(list_nde.Get_at(i)); + claims_parser.Make_claim_itms(qid, temp_list, src, claim_nde); + } + return Wdata_doc_parser_v1.Claims_list_to_hash(temp_list); + } catch (Exception e) {throw Err_.new_exc(e, "xo", "failed to parse claims", "qid", String_.new_u8(doc.Src()));} + } + } + public Wbase_claim_base Parse_claims_data(byte[] qid, int pid, byte snak_tid, Json_nde nde) {return claims_parser.Parse_datavalue(qid, pid, snak_tid, nde);} + public Wbase_claim_grp_list Parse_qualifiers(byte[] qid, Json_nde nde) {return claims_parser.Parse_qualifiers(qid, nde);} + public Wbase_references_grp[] Parse_references(byte[] qid, Json_ary owner) {return claims_parser.Parse_references(qid, owner);} + public int[] Parse_pid_order(byte[] qid, Json_ary ary) {return claims_parser.Parse_pid_order(ary);} + public static final String + Str_id = "id" + , Str_sitelinks = "sitelinks" + , Str_labels = "labels" + , Str_descriptions = "descriptions" + , Str_aliases = "aliases" + , Str_claims = "claims" + , Str_type = "type" + ; + public static final byte[] + Bry_id = Bry_.new_a7(Str_id) + , Bry_sitelinks = Bry_.new_a7(Str_sitelinks) + , Bry_labels = Bry_.new_a7(Str_labels) + , Bry_descriptions = Bry_.new_a7(Str_descriptions) + , Bry_aliases = Bry_.new_a7(Str_aliases) + , Bry_claims = Bry_.new_a7(Str_claims) + , Bry_type = Bry_.new_a7(Str_type) + ; +} diff --git a/400_xowa/tst/gplx/langs/jsons/JsonDocBldrTest.java b/400_xowa/tst/gplx/langs/jsons/JsonDocBldrTest.java new file mode 100644 index 000000000..b57db6029 --- /dev/null +++ b/400_xowa/tst/gplx/langs/jsons/JsonDocBldrTest.java @@ -0,0 +1,47 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +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.langs.jsons; + +import gplx.Bry_bfr; +import gplx.Bry_bfr_; +import gplx.core.tests.Gftest; +import org.junit.Test; + +public class JsonDocBldrTest { + private JsonDocBldrTestUtil util = new JsonDocBldrTestUtil(); + @Test public void Basic() { + JsonDocBldr bldr = JsonDocBldr.NewRootNde(); + bldr.NdeBgn("nde") + .KvBool("bool", true) + .KvInt("int", 123) + .KvStr("str", "abc") + .NdeEnd(); + util.Test(bldr + , "{ 'nde':" + , " { 'bool':true" + , " , 'int':123" + , " , 'str':'abc'" + , " }" + , "}"); + } +} +class JsonDocBldrTestUtil { + public void Test(JsonDocBldr bldr, String... ary) { + Bry_bfr bfr = Bry_bfr_.New(); + bldr.ToDoc().Root_grp().Print_as_json(bfr, 0); + Gftest.Eq__ary__lines(Json_doc.Make_str_by_apos(ary), bfr.To_str_and_clear()); + } +} \ No newline at end of file diff --git a/400_xowa/tst/gplx/langs/mustaches/JsonMustacheNdeTest.java b/400_xowa/tst/gplx/langs/mustaches/JsonMustacheNdeTest.java new file mode 100644 index 000000000..83a7425c8 --- /dev/null +++ b/400_xowa/tst/gplx/langs/mustaches/JsonMustacheNdeTest.java @@ -0,0 +1,71 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +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.langs.mustaches; + +import gplx.Bry_; +import gplx.Bry_bfr_; +import gplx.core.tests.Gftest; +import gplx.langs.jsons.Json_doc; +import gplx.langs.jsons.Json_parser; +import org.junit.Test; + +public class JsonMustacheNdeTest { + private JsonMustacheNdeTestUtil util = new JsonMustacheNdeTestUtil(); + + @Test public void Str() { + util.TestWrite("[{{key}}]", "{'key':'abc'}", "[abc]"); + } + @Test public void Bool() { + String mustache = "[{{#key}}y{{/key}}]"; + util.TestWrite(mustache, "{'key':true}", "[y]"); + util.TestWrite(mustache, "{'key':false}", "[]"); + } + @Test public void Ary() { + String mustache = "[{{#group}}{{key}} {{/group}}]"; + util.TestWrite(mustache, "{'group':[{'key':'a'}, {'key':'b'}, {'key':'c'}]}", "[a b c ]"); + } + @Test public void SectionPropWithDot() { + String mustache = "[{{#key}}{{.}}{{/key}}]"; + util.TestWrite(mustache, "{'key':'test'}", "[test]"); + util.TestWrite(mustache, "{'key1':'test'}", "[]"); + } + @Test public void SectionPropWithoutDot() { + String mustache = "[{{#prop}}{{propx}}{{/prop}}]"; + util.TestWrite(mustache, "{'prop':'test'}", "[]"); + util.TestWrite(mustache, "{'propx':'test'}", "[]"); + } + @Test public void Dot() {// NOTE: online demo gives `([object Object])`; https://mustache.github.io/#demo + String mustache = "({{.}})"; + util.TestWrite(mustache, "{'key':'test'}", "()"); + } +} +class JsonMustacheNdeTestUtil { + public void TestWrite(String mustache, String json, String expd) { + // parse JSON to mustache itm + Json_doc jdoc = Json_parser.ParseToJdoc(Json_doc.Make_str_by_apos(json)); + JsonMustacheNde nde = new JsonMustacheNde(jdoc.Root_nde()); + + // parse template + Mustache_tkn_itm actl_itm = new Mustache_tkn_parser().Parse(Bry_.new_u8(mustache)); + + // render + Mustache_bfr bfr = new Mustache_bfr(Bry_bfr_.New()); + actl_itm.Render(bfr, new Mustache_render_ctx().Init(nde)); + + // test + Gftest.Eq__ary__lines(expd, bfr.To_str_and_clear()); + } +} \ No newline at end of file diff --git a/res/bin/any/xowa/cfg/app/xowa.gfs b/res/bin/any/xowa/cfg/app/xowa.gfs index 66ce738aa..3f490fdc1 100644 --- a/res/bin/any/xowa/cfg/app/xowa.gfs +++ b/res/bin/any/xowa/cfg/app/xowa.gfs @@ -140,10 +140,14 @@ app.wikis.get('~{wiki_key}') { app.wikis.get('~{wiki_key}').html.portal { div_personal = <:[" -
    -

    ~{<>msgs.get('personaltools');<>}

    +
    + "]:> ; div_ns = @@ -178,7 +182,7 @@ app.wikis.get('~{wiki_key}').html.portal { msgs.get_html_accesskey_and_title('search');<>} value="~{search_text}" autocomplete="off"> msgs.get_html_accesskey_and_title('search');<>} id="mw-searchButton" class="searchButton mw-fallbackSearchButton" /> - + @@ -195,8 +199,10 @@ app.wikis.get('~{wiki_key}').html.portal { ; div_home = <:[" - + "]:> ; div_sync = <:[" -
    -

    ~{<>app.user.msgs.get('xowa-portal-sync');<>}

    +
    + "]:> ; div_wikis = <:[" -
    -

    ~{toggle_btn}

    -
    +
    + "]:> ; } @@ -254,12 +264,12 @@ app.wikis.get('~{wiki_key}').html.article { @@ -267,7 +277,7 @@ app.wikis.get('~{wiki_key}').html.article {
    ~{page_indicators}~{page_pgbnr} - ~{page_heading} + ~{page_heading}
    From XOWA: the free, open-source, offline wiki application
    @@ -321,7 +331,7 @@ app.wikis.get('~{wiki_key}').html.article { ~{html_css_night_tag}~{xowa_head}