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

Skin: Change to mustache-backed Skin.Vector [#797]

This commit is contained in:
gnosygnu
2020-10-18 10:52:54 -04:00
parent 5c3d6a173b
commit 19a315b8cd
62 changed files with 12602 additions and 10620 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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}} <b>{{name}}</b>{{/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;}
}

View File

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

View File

@@ -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}}", "&lt;");
}
@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}}", "&lt;");
}
@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);
}
}

View File

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

View File

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

View File

@@ -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}} -> &lt;a&gt;
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}} -> &lt;a&gt;
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}}} -> <a>
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}} -> &lt;a&gt;
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}} -> &lt;a&gt;
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}}} -> <a>
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);}
}

View File

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