/* XOWA: the XOWA Offline Wiki Application Copyright (C) 2012 gnosygnu@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package gplx.langs.jsons; import gplx.*; import gplx.langs.*; import gplx.core.primitives.*; public class Json_wtr { private final Bry_bfr bfr = Bry_bfr_.New_w_size(255); private final Int_ary idx_stack = new Int_ary(4); private int idx = 0; public Bry_bfr Bfr() {return bfr;} public void Indent_(int v) {this.indent = v;} private int indent; public byte Opt_quote_byte() {return opt_quote_byte;} public Json_wtr Opt_quote_byte_(byte v) {opt_quote_byte = v; return this;} private byte opt_quote_byte = Byte_ascii.Quote; public boolean Opt_ws() {return opt_ws;} public Json_wtr Opt_ws_(boolean v) {opt_ws = v; return this;} private boolean opt_ws = true; public boolean Opt_backslash_2x() {return opt_backslash_2x;} public Json_wtr Opt_backslash_2x_(boolean v) {opt_backslash_2x = v; return this;} private boolean opt_backslash_2x = false; public byte[] To_bry_and_clear() {return bfr.To_bry_and_clear();} public String To_str_and_clear() {return bfr.To_str_and_clear();} public Json_wtr () {this.Clear();} public Json_wtr Clear() { indent = -1; idx_stack.Clear(); idx = 0; return this; } public Json_wtr Doc_nde_bgn() {return Write_grp_bgn(Sym_nde_bgn);} public Json_wtr Doc_nde_end() {Write_grp_end(Bool_.Y, Sym_nde_end); return Write_nl();} public Json_wtr Doc_ary_bgn() {return Write_grp_bgn(Sym_ary_bgn);} public Json_wtr Doc_ary_end() {Write_grp_end(Bool_.N, Sym_ary_end); return Write_nl();} public Json_wtr Nde_bgn_ary() {return Nde_bgn(Bry_.Empty);} public Json_wtr Nde_bgn(String key) {return Nde_bgn(Bry_.new_u8(key));} public Json_wtr Nde_bgn(byte[] key) { Write_indent_itm(); if (key == Bry_.Empty) { if (opt_ws) bfr.Del_by_1(); // remove trailing space from Write_indent_itm ++idx; } else Write_key(key); Write_nl(); return Write_grp_bgn(Sym_nde_bgn); } public Json_wtr Nde_end() { Write_grp_end(Bool_.Y, Sym_nde_end); return Write_nl(); } public Json_wtr Ary_bgn_ary() {return Ary_bgn(String_.Empty);} public Json_wtr Ary_bgn(String nde) { Write_indent_itm(); if (nde == String_.Empty) { if (opt_ws) bfr.Del_by_1(); // remove trailing space from Write_indent_itm ++idx; } else Write_key(Bry_.new_u8(nde)); return Ary_bgn_keyless(); } private Json_wtr Ary_bgn_keyless() { Write_nl(); return Write_grp_bgn(Sym_ary_bgn); } public Json_wtr Ary_itm_str(String itm) {return Ary_itm_by_type_tid(Type_adp_.Tid__str, itm);} public Json_wtr Ary_itm_bry(byte[] itm) {return Ary_itm_by_type_tid(Type_adp_.Tid__bry, itm);} public Json_wtr Ary_itm_obj(Object itm) {return Ary_itm_by_type_tid(Type_adp_.To_tid_obj(itm), itm);} public Json_wtr Ary_itm_by_type_tid(int itm_type_tid, Object itm) { Write_indent_itm(); Write_val_obj(Bool_.Y, itm_type_tid, itm); Write_nl(); ++idx; return this; } public Json_wtr Ary_end() { Write_grp_end(Bool_.N, Sym_ary_end); return Write_nl(); } public Json_wtr Kv_bool_as_mw(String key, boolean val) { if (val) Kv_bry(key, Bry_.Empty); // if true, write 'key:""'; if false, write nothing return this; } public Json_wtr Kv_bool(String key, boolean val) {return Kv_bool(Bry_.new_u8(key), val);} public Json_wtr Kv_bool(byte[] key, boolean val) {return Kv_raw(key, val ? Bool_.True_bry : Bool_.False_bry);} public Json_wtr Kv_int(String key, int val) {return Kv_raw(Bry_.new_u8(key), Int_.To_bry(val));} public Json_wtr Kv_long(String key, long val) {return Kv_raw(Bry_.new_u8(key), Bry_.new_a7(Long_.To_str(val)));} public Json_wtr Kv_float(String key, float val) {return Kv_raw(Bry_.new_u8(key), Bry_.new_a7(Float_.To_str(val)));} public Json_wtr Kv_double(String key, double val) {return Kv_raw(Bry_.new_u8(key), Bry_.new_a7(Double_.To_str(val)));} private Json_wtr Kv_raw(byte[] key, byte[] val) { Write_indent_itm(); Write_key(key); bfr.Add(val); Write_nl(); return this; } public Json_wtr Kv_str(String key, String val) {return Kv_bry(Bry_.new_u8(key), val == null ? null : Bry_.new_u8(val));} public Json_wtr Kv_str(byte[] key, String val) {return Kv_bry(key, Bry_.new_u8(val));} public Json_wtr Kv_bry(String key, byte[] val) {return Kv_bry(Bry_.new_u8(key), val);} public Json_wtr Kv_bry(byte[] key, byte[] val) { Write_indent_itm(); Write_key(key); Write_str(val); Write_nl(); return this; } public Object Get_x(Json_itm itm) { switch (itm.Tid()) { case Json_itm_.Tid__ary: case Json_itm_.Tid__nde: return itm; default: case Json_itm_.Tid__kv: throw Err_.new_unsupported(); case Json_itm_.Tid__bool: case Json_itm_.Tid__int: case Json_itm_.Tid__decimal: case Json_itm_.Tid__str: return itm.Data(); } } public void Kv_itm_x(byte[] key, Json_itm itm) { Object val = Get_x(itm); int val_tid = Type_adp_.To_tid_obj(val); Kv_obj(key, val, val_tid); } public Json_wtr Kv_obj(byte[] key, Object val, int val_tid) { Write_indent_itm(); Write_key(key); Write_val_obj(Bool_.N, val_tid, val); Write_nl(); return this; } private Json_wtr Write_grp_bgn(byte[] grp_sym) {return Write_grp_bgn(grp_sym, Bool_.Y);} private Json_wtr Write_grp_bgn(byte[] grp_sym, boolean write_indent) { idx_stack.Add(idx); idx = 0; ++indent; if (write_indent) Write_indent(); bfr.Add(grp_sym); return this; } private Json_wtr Write_grp_end(boolean grp_is_nde, byte[] grp_sym) { if ((grp_is_nde && idx == 0) || (!grp_is_nde && idx == 0)) Write_nl(); Write_indent(); --indent; bfr.Add(grp_sym); this.idx = idx_stack.Pop_or(0); return this; } private Json_wtr Write_key(byte[] bry) { Write_str(bry); // "key" bfr.Add_byte_colon(); // ":" ++idx; return this; } private void Write_val_obj(boolean called_by_ary, int type_tid, Object obj) { switch (type_tid) { case Type_adp_.Tid__null: bfr.Add(Object_.Bry__null); break; case Type_adp_.Tid__bool: bfr.Add_bool(Bool_.Cast(obj)); break; case Type_adp_.Tid__byte: bfr.Add_byte(Byte_.cast(obj)); break; case Type_adp_.Tid__int: bfr.Add_int_variable(Int_.cast(obj)); break; case Type_adp_.Tid__long: bfr.Add_long_variable(Long_.cast(obj)); break; case Type_adp_.Tid__float: bfr.Add_float(Float_.cast(obj)); break; case Type_adp_.Tid__double: bfr.Add_double(Double_.cast(obj)); break; case Type_adp_.Tid__str: Write_str(Bry_.new_u8((String)obj)); break; case Type_adp_.Tid__bry: Write_str((byte[])obj); break; case Type_adp_.Tid__char: case Type_adp_.Tid__date: case Type_adp_.Tid__decimal: Write_str(Bry_.new_u8(Object_.Xto_str_strict_or_empty(obj))); break; case Type_adp_.Tid__obj: int grp_type = Grp_type__get(obj); if (grp_type < Grp_type__json_ary) Write_val_obj__nde(called_by_ary, grp_type, obj); else Write_val_itm__ary(called_by_ary, grp_type, obj); break; default: throw Err_.new_unhandled(type_tid); } } private void Handle_nde_as_ary_itm_0() { if (idx == 0) { // if nde, and first item, then put on new line bfr.Del_by_1(); if (opt_ws) { bfr.Add_byte_nl(); ++indent; Write_indent(); --indent; } } } private void Write_val_obj__nde(boolean called_by_ary, int grp_type, Object obj) { if (grp_type == Grp_type__json_nde) { if (idx == 0) { // if nde, and first item, then put on new line if (!called_by_ary) { bfr.Del_by_1(); if (opt_ws) { bfr.Add_byte_nl(); ++indent; Write_indent(); --indent; } } } bfr.Add_byte_nl(); Write_grp_bgn(Sym_nde_bgn, Bool_.Y); Json_nde sub_nde = (Json_nde)obj; int sub_nde_len = sub_nde.Len(); for (int i = 0; i < sub_nde_len; ++i) { Json_kv sub_kv = sub_nde.Get_at_as_kv(i); Kv_itm_x(sub_kv.Key_as_bry(), sub_kv.Val()); } } else { Handle_nde_as_ary_itm_0(); Write_grp_bgn(Sym_nde_bgn, Bool_.N); Keyval[] kvy = (Keyval[])obj; int kvy_len = kvy.length; for (int i = 0; i < kvy_len; ++i) { Keyval kv = kvy[i]; Object kv_val = kv.Val(); Kv_obj(Bry_.new_u8(kv.Key()), kv_val, Type_adp_.To_tid_obj(kv_val)); } } Write_grp_end(Bool_.Y, Sym_nde_end); } private void Write_val_itm__ary(boolean called_by_ary, int grp_type, Object obj) { Ary_bgn_keyless(); if (grp_type == Grp_type__json_ary) { Json_ary sub_ary = (Json_ary)(obj); int len = sub_ary.Len(); for (int i = 0; i < len; ++i) { Json_itm sub_itm = sub_ary.Get_at(i); Ary_itm_obj(Get_x(sub_itm)); } } else { Object ary = Array_.cast(obj); int len = Array_.Len(ary); for (int i = 0; i < len; ++i) { Object itm = Array_.Get_at(ary, i); Ary_itm_obj(itm); } } Write_grp_end(Bool_.N, Sym_ary_end); } private void Write_str(byte[] bry) { if (bry == null) {bfr.Add(Object_.Bry__null); return;} int len = bry.length; int backslash_count = opt_backslash_2x ? 3 : 1; // NOTE: 3 handles backslashes usurped by javascript; EX: '{"val":"\\\\"}' --javascript--> '{"val":"\\"}' --json--> '{"val":"\"}' bfr.Add_byte(opt_quote_byte); for (int i = 0; i < len; ++i) { byte b = bry[i]; switch (b) { case Byte_ascii.Backslash: bfr.Add_byte_repeat(Byte_ascii.Backslash, backslash_count).Add_byte(b); break; // "\" -> "\\"; needed else js will usurp \ as escape; EX: "\&" -> "&"; DATE:2014-06-24 case Byte_ascii.Quote: bfr.Add_byte_repeat(Byte_ascii.Backslash, backslash_count).Add_byte(b); break; case Byte_ascii.Apos: // // "'" -> "'''"; needed else xocfg fails; DATE:2016-12-07 if (opt_backslash_2x) bfr.Add_byte_repeat(Byte_ascii.Backslash, 1).Add_byte(b); else bfr.Add_byte(b); break; case Byte_ascii.Nl: bfr.Add_byte_repeat(Byte_ascii.Backslash, 2).Add_byte(Byte_ascii.Ltr_n); break; // "\n" -> "\\n" case Byte_ascii.Cr: break;// skip default: bfr.Add_byte(b); break; } } bfr.Add_byte(opt_quote_byte); } private void Write_indent_itm() { if (idx == 0) { if (opt_ws) bfr.Add_byte_space(); } else { Write_indent(); bfr.Add(Sym_itm_spr); if (opt_ws) bfr.Add_byte_space(); } } private void Write_indent() { if (opt_ws && indent > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent * 2); } private Json_wtr Write_nl() { if (opt_ws) bfr.Add_byte_nl(); return this; } private static final byte[] Sym_nde_bgn = Bry_.new_a7("{") , Sym_nde_end = Bry_.new_a7("}") , Sym_ary_bgn = Bry_.new_a7("[") , Sym_ary_end = Bry_.new_a7("]") , Sym_itm_spr = Bry_.new_a7(",") ; private static final int Grp_type__json_nde = 1, Grp_type__kv_ary = 2, Grp_type__json_ary = 3, Grp_type__obj_ary = 4; private static int Grp_type__get(Object obj) { Class type = obj.getClass(); if (Type_adp_.Eq(type, Keyval[].class)) return Grp_type__kv_ary; else if (Type_adp_.Is_array(type)) return Grp_type__obj_ary; else if (Type_adp_.Eq(type, Json_nde.class)) return Grp_type__json_nde; else if (Type_adp_.Eq(type, Json_ary.class)) return Grp_type__json_ary; else throw Err_.new_unhandled(type); } }