mirror of https://github.com/gnosygnu/xowa
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
12 KiB
271 lines
12 KiB
/*
|
|
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.core.brys.fmtrs; import gplx.*;
|
|
import gplx.core.brys.*;
|
|
import gplx.core.primitives.*; import gplx.core.strings.*;
|
|
import gplx.objects.strings.AsciiByte;
|
|
public class Bry_fmtr {
|
|
public byte[] Fmt() {return fmt;} private byte[] fmt = Bry_.Empty;
|
|
public boolean Fmt_null() {return fmt.length == 0;}
|
|
public Bry_fmtr_eval_mgr Eval_mgr() {return eval_mgr;} public Bry_fmtr Eval_mgr_(Bry_fmtr_eval_mgr v) {eval_mgr = v; return this;} Bry_fmtr_eval_mgr eval_mgr = Bry_fmtr_eval_mgr_gfs.Instance;
|
|
public Bry_fmtr Fmt_(byte[] v) {fmt = v; dirty = true; return this;} public Bry_fmtr Fmt_(String v) {return Fmt_(Bry_.new_u8(v));}
|
|
public Bry_fmtr Keys_(String... ary) {
|
|
if (keys == null) keys = Hash_adp_.New();
|
|
else keys.Clear();
|
|
int ary_len = ary.length;
|
|
for (int i = 0; i < ary_len; i++)
|
|
keys.Add(Bry_obj_ref.New(Bry_.new_u8(ary[i])), new Int_obj_val(i));
|
|
dirty = true;
|
|
return this;
|
|
} Hash_adp keys = null;
|
|
public void Bld_bfr(Bry_bfr bfr, byte[]... args) {
|
|
if (dirty) Compile();
|
|
int args_len = args.length;
|
|
for (int i = 0; i < itms_len; i++) {
|
|
Bry_fmtr_itm itm = itms[i];
|
|
if (itm.Arg) {
|
|
int arg_idx = itm.ArgIdx;
|
|
if (arg_idx < args_len)
|
|
bfr.Add(args[arg_idx]);
|
|
else
|
|
bfr.Add(missing_bgn).Add_int_variable(arg_idx + missing_adj).Add(missing_end);
|
|
}
|
|
else
|
|
bfr.Add(itm.Dat);
|
|
}
|
|
}
|
|
public void Bld_bfr_none(Bry_bfr bfr) {
|
|
if (dirty) Compile();
|
|
for (int i = 0; i < itms_len; i++) {
|
|
Bry_fmtr_itm itm = itms[i];
|
|
if (itm.Arg)
|
|
bfr.Add_byte(char_escape).Add_byte(char_arg_bgn).Add_int_variable(itm.ArgIdx).Add_byte(char_arg_end);
|
|
else
|
|
bfr.Add(itm.Dat);
|
|
}
|
|
}
|
|
public void Bld_bfr(Bry_bfr bfr, Bfr_arg... args) {
|
|
if (dirty) Compile();
|
|
for (int i = 0; i < itms_len; i++) {
|
|
Bry_fmtr_itm itm = itms[i];
|
|
if (itm.Arg)
|
|
args[itm.ArgIdx].Bfr_arg__add(bfr);
|
|
else
|
|
bfr.Add(itm.Dat);
|
|
}
|
|
}
|
|
public void Bld_bfr_one(Bry_bfr bfr, Object val) {
|
|
Bld_bfr_one_ary[0] = val;
|
|
Bld_bfr_ary(bfr, Bld_bfr_one_ary);
|
|
} Object[] Bld_bfr_one_ary = new Object[1];
|
|
public void Bld_bfr_many(Bry_bfr bfr, Object... args) {Bld_bfr_ary(bfr, args);}
|
|
public void Bld_bfr_ary(Bry_bfr bfr, Object[] args) {
|
|
if (dirty) Compile();
|
|
int args_len = args.length;
|
|
for (int i = 0; i < itms_len; i++) {
|
|
Bry_fmtr_itm itm = itms[i];
|
|
if (itm.Arg) {
|
|
int arg_idx = itm.ArgIdx;
|
|
if (arg_idx > -1 && arg_idx < args_len)
|
|
bfr.Add_obj(args[itm.ArgIdx]);
|
|
else
|
|
bfr.Add_byte(char_escape).Add_byte(char_arg_bgn).Add_int_variable(arg_idx).Add_byte(char_arg_end);
|
|
}
|
|
else
|
|
bfr.Add(itm.Dat);
|
|
}
|
|
}
|
|
public byte[] Bld_bry_none(Bry_bfr bfr) {Bld_bfr_ary(bfr, Object_.Ary_empty); return bfr.To_bry_and_clear();}
|
|
public byte[] Bld_bry_many(Bry_bfr bfr, Object... args) {
|
|
Bld_bfr_ary(bfr, args);
|
|
return bfr.To_bry_and_clear();
|
|
}
|
|
public String Bld_str_many(Bry_bfr bfr, String fmt, Object... args) {
|
|
this.Fmt_(fmt).Bld_bfr_many(bfr, args);
|
|
return bfr.To_str_and_clear();
|
|
}
|
|
public String Bld_str_many(String... args) {
|
|
if (dirty) Compile();
|
|
String_bldr rv = String_bldr_.new_();
|
|
int args_len = args.length;
|
|
for (int i = 0; i < itms_len; i++) {
|
|
Bry_fmtr_itm itm = itms[i];
|
|
if (itm.Arg) {
|
|
int arg_idx = itm.ArgIdx;
|
|
if (arg_idx < args_len)
|
|
rv.Add(args[arg_idx]);
|
|
else
|
|
rv.Add(missing_bgn).Add(arg_idx + missing_adj).Add(missing_end);
|
|
}
|
|
else
|
|
rv.Add(itm.DatStr());
|
|
}
|
|
return rv.To_str();
|
|
} private Bry_fmtr_itm[] itms; int itms_len;
|
|
public byte[] Missing_bgn() {return missing_bgn;} public Bry_fmtr Missing_bgn_(byte[] v) {missing_bgn = v; return this;} private byte[] missing_bgn = missing_bgn_static; static byte[] missing_bgn_static = Bry_.new_u8("~{"), missing_end_static = Bry_.new_u8("}");
|
|
public byte[] Missing_end() {return missing_end;} public Bry_fmtr Missing_end_(byte[] v) {missing_end = v; return this;} private byte[] missing_end = missing_end_static;
|
|
public int Missing_adj() {return missing_adj;} public Bry_fmtr Missing_adj_(int v) {missing_adj = v; return this;} int missing_adj;
|
|
public boolean Fail_when_invalid_escapes() {return fail_when_invalid_escapes;} public Bry_fmtr Fail_when_invalid_escapes_(boolean v) {fail_when_invalid_escapes = v; return this;} private boolean fail_when_invalid_escapes = true;
|
|
public Bry_fmtr Compile() {
|
|
synchronized (this) { // THREAD: DATE:2015-04-29
|
|
Bry_bfr lkp_bfr = Bry_bfr_.New_w_size(16);
|
|
int fmt_len = fmt.length; int fmt_end = fmt_len - 1; int fmt_pos = 0;
|
|
byte[] trg_bry = new byte[fmt_len]; int trg_pos = 0;
|
|
boolean lkp_is_active = false, lkp_is_numeric = true;
|
|
byte nxt_byte, tmp_byte;
|
|
boolean dirty_disable = true;
|
|
List_adp list = List_adp_.New();
|
|
fmt_args_exist = false;
|
|
while (true) {
|
|
if (fmt_pos > fmt_end) break;
|
|
byte cur_byte = fmt[fmt_pos];
|
|
if (lkp_is_active) {
|
|
if (cur_byte == char_arg_end) {
|
|
if (lkp_is_numeric)
|
|
list.Add(Bry_fmtr_itm.arg_(lkp_bfr.To_int(0) - baseInt));
|
|
else {
|
|
byte[] key_fmt = lkp_bfr.To_bry();
|
|
Object idx_ref = keys.GetByOrNull(Bry_obj_ref.New(key_fmt));
|
|
if (idx_ref == null) {
|
|
int lkp_bfr_len = lkp_bfr.Len();
|
|
byte[] lkp_bry = lkp_bfr.Bfr();
|
|
trg_bry[trg_pos++] = char_escape;
|
|
trg_bry[trg_pos++] = char_arg_bgn;
|
|
for (int i = 0; i < lkp_bfr_len; i++)
|
|
trg_bry[trg_pos++] = lkp_bry[i];
|
|
trg_bry[trg_pos++] = char_arg_end;
|
|
}
|
|
else {
|
|
list.Add(Bry_fmtr_itm.arg_(((Int_obj_val)idx_ref).Val() - baseInt));
|
|
}
|
|
}
|
|
lkp_is_active = false;
|
|
lkp_bfr.Clear();
|
|
fmt_args_exist = true;
|
|
}
|
|
else {
|
|
lkp_bfr.Add_byte(cur_byte);
|
|
switch (cur_byte) {
|
|
case AsciiByte.Num0: case AsciiByte.Num1: case AsciiByte.Num2: case AsciiByte.Num3: case AsciiByte.Num4:
|
|
case AsciiByte.Num5: case AsciiByte.Num6: case AsciiByte.Num7: case AsciiByte.Num8: case AsciiByte.Num9:
|
|
break;
|
|
default:
|
|
lkp_is_numeric = false;
|
|
break;
|
|
}
|
|
}
|
|
fmt_pos += 1;
|
|
}
|
|
else if (cur_byte == char_escape) {
|
|
if (fmt_pos == fmt_end) {
|
|
if (fail_when_invalid_escapes)
|
|
throw Err_.new_wo_type("escape char encountered but no more chars left");
|
|
else {
|
|
trg_bry[trg_pos] = cur_byte;
|
|
break;
|
|
}
|
|
}
|
|
nxt_byte = fmt[fmt_pos + 1];
|
|
if (nxt_byte == char_arg_bgn) {
|
|
if (trg_pos > 0) {list.Add(Bry_fmtr_itm.dat_(trg_bry, trg_pos)); trg_pos = 0;} // something pending; add it to list
|
|
int eval_lhs_bgn = fmt_pos + 2;
|
|
if (eval_lhs_bgn < fmt_len && fmt[eval_lhs_bgn] == char_eval_bgn) { // eval found
|
|
dirty_disable = false; // eval allows args to retrigger compiles; this is probably not used, but just in case, do not disable dirty; TEST: Tfds.Eq("012~{<>3<>}4", fmtr.Bld_str_many("1"));
|
|
fmt_pos = Compile_eval_cmd(fmt, fmt_len, eval_lhs_bgn, list);
|
|
continue;
|
|
}
|
|
else {
|
|
lkp_is_active = true;
|
|
lkp_is_numeric = true;
|
|
}
|
|
}
|
|
else { // ~{0}; ~~ -> ~; ~n -> newLine; ~t -> tab
|
|
if (nxt_byte == char_escape) tmp_byte = char_escape;
|
|
else if (nxt_byte == char_escape_nl) tmp_byte = AsciiByte.Nl;
|
|
else if (nxt_byte == char_escape_tab) tmp_byte = AsciiByte.Tab;
|
|
else {
|
|
if (fail_when_invalid_escapes) throw Err_.new_wo_type("unknown escape code", "code", Char_.By_int(nxt_byte), "fmt_pos", fmt_pos + 1);
|
|
else
|
|
tmp_byte = cur_byte;
|
|
}
|
|
trg_bry[trg_pos++] = tmp_byte;
|
|
}
|
|
fmt_pos += 2;
|
|
}
|
|
else {
|
|
trg_bry[trg_pos++] = cur_byte;
|
|
fmt_pos += 1;
|
|
}
|
|
}
|
|
if (lkp_is_active) throw Err_.new_wo_type("idx mode not closed");
|
|
if (trg_pos > 0) {list.Add(Bry_fmtr_itm.dat_(trg_bry, trg_pos)); trg_pos = 0;}
|
|
itms = (Bry_fmtr_itm[])list.ToAry(Bry_fmtr_itm.class);
|
|
itms_len = itms.length;
|
|
if (dirty_disable)
|
|
dirty = false; // ISSUE#:575; DATE:2019-09-16
|
|
return this;
|
|
}
|
|
}
|
|
int Compile_eval_cmd(byte[] fmt, int fmt_len, int eval_lhs_bgn, List_adp list) {
|
|
int eval_lhs_end = Bry_find_.Find_fwd(fmt, char_eval_end, eval_lhs_bgn + AsciiByte.Len1, fmt_len); if (eval_lhs_end == Bry_find_.Not_found) throw Err_.new_wo_type("eval_lhs_end_invalid: could not find eval_lhs_end", "snip", String_.new_u8(fmt, eval_lhs_bgn, fmt_len));
|
|
byte[] eval_dlm = Bry_.Mid(fmt, eval_lhs_bgn , eval_lhs_end + AsciiByte.Len1);
|
|
int eval_rhs_bgn = Bry_find_.Find_fwd(fmt, eval_dlm , eval_lhs_end + AsciiByte.Len1, fmt_len); if (eval_rhs_bgn == Bry_find_.Not_found) throw Err_.new_wo_type("eval_rhs_bgn_invalid: could not find eval_rhs_bgn", "snip", String_.new_u8(fmt, eval_lhs_end, fmt_len));
|
|
byte[] eval_cmd = Bry_.Mid(fmt, eval_lhs_end + AsciiByte.Len1, eval_rhs_bgn);
|
|
byte[] eval_rslt = eval_mgr.Eval(eval_cmd);
|
|
int eval_rhs_end = eval_rhs_bgn + AsciiByte.Len1 + eval_dlm.length;
|
|
if (eval_rslt == null) eval_rslt = Bry_.Mid(fmt, eval_lhs_bgn - 2, eval_rhs_end); // not found; return original argument
|
|
list.Add(Bry_fmtr_itm.dat_bry_(eval_rslt));
|
|
return eval_rhs_end;
|
|
}
|
|
static final String GRP_KEY = "gplx.Bry_fmtr";
|
|
public boolean Fmt_args_exist() {return fmt_args_exist;} private boolean fmt_args_exist;
|
|
boolean dirty = true;
|
|
int baseInt = 0;
|
|
public static final byte char_escape = AsciiByte.Tilde, char_arg_bgn = AsciiByte.CurlyBgn, char_arg_end = AsciiByte.CurlyEnd, char_escape_nl = AsciiByte.Ltr_n, char_escape_tab = AsciiByte.Ltr_t, char_eval_bgn = AsciiByte.Lt, char_eval_end = AsciiByte.Gt;
|
|
public static final Bry_fmtr Null = new Bry_fmtr().Fmt_("");
|
|
public static Bry_fmtr New__tmp() {return new Bry_fmtr().Fmt_("").Keys_();}
|
|
public static Bry_fmtr new_(String fmt, String... keys) {return new Bry_fmtr().Fmt_(fmt).Keys_(keys);} // NOTE: keys may seem redundant, but are needed to align ordinals with proc; EX: fmt may be "~{A} ~{B}" or "~{B} ~{A}"; call will always be Bld(a, b); passing in "A", "B" guarantees A is 0 and B is 1;
|
|
public static Bry_fmtr new_(byte[] fmt, String... keys) {return new Bry_fmtr().Fmt_(fmt).Keys_(keys);} // NOTE: keys may seem redundant, but are needed to align ordinals with proc; EX: fmt may be "~{A} ~{B}" or "~{B} ~{A}"; call will always be Bld(a, b); passing in "A", "B" guarantees A is 0 and B is 1;
|
|
public static Bry_fmtr new_() {return new Bry_fmtr();}
|
|
public static Bry_fmtr keys_(String... keys) {return new Bry_fmtr().Keys_(keys);}
|
|
public static Bry_fmtr new_bry_(byte[] fmt, String... keys) {return new Bry_fmtr().Fmt_(fmt).Keys_(keys);}
|
|
public static String New_fmt_str(String key, Object[] args) {
|
|
tmp_bfr.Clear();
|
|
tmp_bfr.Add_str_u8(key);
|
|
tmp_bfr.Add_byte(AsciiByte.Colon);
|
|
int args_len = args.length;
|
|
for (int i = 0; i < args_len; i++) { // add " 0='~{0}'"
|
|
tmp_bfr.Add_byte(AsciiByte.Space);
|
|
tmp_bfr.Add_int_variable(i);
|
|
tmp_bfr.Add_byte(AsciiByte.Eq);
|
|
tmp_bfr.Add_byte(AsciiByte.Apos);
|
|
tmp_bfr.Add_byte(AsciiByte.Tilde);
|
|
tmp_bfr.Add_byte(AsciiByte.CurlyBgn);
|
|
tmp_bfr.Add_int_variable(i);
|
|
tmp_bfr.Add_byte(AsciiByte.CurlyEnd);
|
|
tmp_bfr.Add_byte(AsciiByte.Apos);
|
|
}
|
|
return tmp_bfr.To_str_and_clear();
|
|
} static Bry_bfr tmp_bfr = Bry_bfr_.Reset(255);
|
|
public void Bld_bfr_many_and_set_fmt(Object... args) {
|
|
Bry_bfr bfr = Bry_bfr_.New();
|
|
this.Bld_bfr_many(bfr, args);
|
|
byte[] bry = bfr.To_bry_and_clear();
|
|
this.Fmt_(bry).Compile();
|
|
}
|
|
public static String Escape_tilde(String v) {return String_.Replace(v, "~", "~~");}
|
|
}
|