1
0
mirror of https://github.com/gnosygnu/xowa.git synced 2026-03-02 03:49:30 +00:00
This commit is contained in:
gnosygnu
2015-07-12 21:10:02 -04:00
commit 794b5a232f
3099 changed files with 238212 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
public interface Scrib_engine {
boolean Dbg_print(); void Dbg_print_(boolean v);
Scrib_server Server(); void Server_(Scrib_server v);
Scrib_lua_proc LoadString(String name, String text);
KeyVal[] CallFunction(int id, KeyVal[] args);
void RegisterLibrary(KeyVal[] functions_ary);
KeyVal[] ExecuteModule(int mod_id);
void CleanupChunks(KeyVal[] ids);
}

View File

@@ -0,0 +1,34 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
public class Scrib_engine_type {
public static String Xto_str(byte v) {
switch (v) {
case Type_lua: return "lua";
case Type_luaj: return "luaj";
default: throw Exc_.new_unimplemented();
}
}
public static byte Xto_byte(String s) {
if (String_.Eq(s, "lua")) return Type_lua;
else if (String_.Eq(s, "luaj")) return Type_luaj;
else throw Exc_.new_unimplemented();
}
public static final byte Type_lua = 0, Type_luaj = 1;
public static KeyVal[] Options__list = KeyVal_.Ary(KeyVal_.new_("luaj"), KeyVal_.new_("lua"));
}

View File

@@ -0,0 +1,28 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*;
public interface Scrib_server {
void Init(String... process_args);
int Server_timeout(); Scrib_server Server_timeout_(int v);
int Server_timeout_polling(); Scrib_server Server_timeout_polling_(int v);
int Server_timeout_busy_wait(); Scrib_server Server_timeout_busy_wait_(int v);
byte[] Server_comm(byte[] cmd, Object[] cmd_objs);
void Server_send(byte[] cmd, Object[] cmd_objs);
byte[] Server_recv();
void Term();
}

View File

@@ -0,0 +1,122 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.luaj; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.luaj.vm2.*; import org.luaj.vm2.lib.*; import org.luaj.vm2.lib.jse.*;
import gplx.xowa.xtns.scribunto.engines.process.*;
public class Luaj_engine implements Scrib_engine {
private Luaj_server server;
private Scrib_proc_mgr proc_mgr;
private Scrib_core core;
public Luaj_engine(Xoae_app app, Scrib_core core, boolean debug_enabled) {
this.core = core;
server = new Luaj_server(core, debug_enabled);
proc_mgr = core.Proc_mgr();
Luaj_server_func_recv._.Engine_(this);
Luaj_server_func_dbg._.Core_(core);
}
public Scrib_server Server() {return server;} public void Server_(Scrib_server v) {server = (Luaj_server)v;}
public boolean Dbg_print() {return dbg_print;} public void Dbg_print_(boolean v) {dbg_print = v;} private boolean dbg_print;
public Scrib_lua_proc LoadString(String name, String text) {
LuaTable msg = LuaValue.tableOf();
msg.set("op", Val_loadString);
msg.set("text", LuaValue.valueOf(text));
msg.set("chunkName", LuaValue.valueOf(name));
LuaTable rsp = server.Dispatch(msg);
LuaTable values_tbl = Luaj_value_.Get_val_as_lua_table(rsp, "values");
LuaInteger proc_id = (LuaInteger)values_tbl.rawget(1);
return new Scrib_lua_proc(name, proc_id.v);
}
public void RegisterLibrary(KeyVal[] functions) {
LuaTable msg = LuaValue.tableOf();
msg.set("op", Val_registerLibrary);
msg.set("name", "mw_interface");
msg.set("functions", Luaj_value_.X_obj_to_val(server, functions));
server.Dispatch(msg);
}
public KeyVal[] CallFunction(int id, KeyVal[] args) {
int args_len = args.length;
LuaTable msg = LuaValue.tableOf();
msg.set("op", Val_callFunction);
msg.set("id", LuaValue.valueOf(id));
msg.set("nargs", LuaValue.valueOf(args_len));
msg.set("args", Luaj_value_.X_obj_to_val(server, args));
return this.Dispatch_as_kv_ary(msg);
}
public KeyVal[] ExecuteModule(int mod_id) {
return this.CallFunction(core.Lib_mw().Mod().Fncs_get_id("executeModule"), Scrib_kv_utl_.base1_obj_(new Scrib_lua_proc("", mod_id)));
}
public void CleanupChunks(KeyVal[] ids) {
LuaTable msg = LuaValue.tableOf();
msg.set("op", "cleanupChunks");
msg.set("ids", Luaj_value_.X_obj_to_val(server, ids));
this.Dispatch_as_kv_ary(msg);
}
public KeyVal[] Dispatch_as_kv_ary(LuaTable msg) {
while (true) {
LuaTable rsp = server.Dispatch(msg);
String op = Luaj_value_.Get_val_as_str(rsp, "op");
if (String_.Eq(op, "return"))
return Luaj_value_.Get_val_as_kv_ary(server, rsp, "values");
else if (String_.Eq(op, "call"))
msg = Server_recv_call(rsp);
else if (String_.Eq(op, "error")) {
String err = Luaj_value_.Get_val_as_str(rsp, "value");
core.Handle_error(err, "");
return KeyVal_.Ary_empty;
}
else
return KeyVal_.Ary_empty;
}
}
public LuaTable Server_recv_call(LuaTable rsp) {
String proc_id = Luaj_value_.Get_val_as_str(rsp, "id");
KeyVal[] args = Luaj_value_.Get_val_as_kv_ary(server, rsp, "args");
Scrib_proc proc = proc_mgr.Get_by_key(proc_id); if (proc == null) throw Scrib_xtn_mgr.err_("could not find proc with id of {0}", proc_id);
Scrib_proc_args proc_args = new Scrib_proc_args(args);
Scrib_proc_rslt proc_rslt = new Scrib_proc_rslt();
proc.Proc_exec(proc_args, proc_rslt);
String fail_msg = proc_rslt.Fail_msg();
if (fail_msg == null) {
KeyVal[] cbk_rslts = proc_rslt.Ary();
return ReturnMessage(cbk_rslts);
}
else {
return ReturnFail(fail_msg);
}
}
private LuaTable ReturnMessage(KeyVal[] values) {
LuaTable msg = LuaValue.tableOf();
msg.set("op", Val_returnMessage);
msg.set("nvalues", LuaValue.valueOf(values.length));
msg.set("values", Luaj_value_.X_obj_to_val(server, values));
return msg;
}
private LuaTable ReturnFail(String fail_msg) {
LuaTable msg = LuaValue.tableOf();
msg.set("op", Val_error);
msg.set("value", LuaValue.valueOf(fail_msg));
return msg;
}
private static final LuaValue
Val_loadString = LuaValue.valueOf("loadString")
, Val_registerLibrary = LuaValue.valueOf("registerLibrary")
, Val_callFunction = LuaValue.valueOf("call")
, Val_returnMessage = LuaValue.valueOf("return")
, Val_error = LuaValue.valueOf("error")
;
}

View File

@@ -0,0 +1,83 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.luaj; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.luaj.vm2.*; import org.luaj.vm2.lib.*; import org.luaj.vm2.lib.jse.*;
import gplx.xowa.xtns.scribunto.engines.process.*;
public class Luaj_server implements Scrib_server {
private LuaTable server;
public Luaj_server(Scrib_core core, boolean debug_enabled) {
}
public static Globals Globals_singleton;
public void Init(String... init_args) {
Globals_singleton = JsePlatform.standardGlobals();
Globals_singleton.load(new DebugLib());
Globals_singleton.load(new MWClient());
Globals_singleton.set("dbg", Luaj_server_func_dbg._);
String root_str = init_args[2];
if (Op_sys.Cur().Tid_is_wnt())
root_str = String_.Replace(root_str, Op_sys.Wnt.Fsys_dir_spr_str(), Op_sys.Lnx.Fsys_dir_spr_str());
LuaValue main_fil_val = LuaValue.valueOf(root_str + "engines/Luaj/mw_main.lua");
LuaValue package_val = Globals_singleton.get("package");
package_val.rawset("path", LuaValue.valueOf(root_str + "engines/Luaj/?.lua;" + root_str + "engines/LuaCommon/lualib/?.lua"));
server = (LuaTable)Globals_singleton.get("dofile").call(main_fil_val);
}
public LuaTable Dispatch(LuaTable msg) {
return (LuaTable)server.method(Val_server_recv, msg);
}
public int Get_id_by_closure(LuaValue closure) {
LuaValue xchunks = server.get(Val_xchunks);
LuaValue closure_id = xchunks.get(closure);
int rv = -1;
if (closure_id == LuaValue.NIL) // new closure; add it to chunks table via addChunk (which will return new id)
rv = ((LuaInteger)server.method("addChunk", closure)).v;
else
rv = ((LuaInteger)closure_id).v;
return rv;
}
public LuaValue Get_closure_by_id(int id) {
LuaValue chunks = server.get(Val_chunks);
return chunks.get(LuaValue.valueOf(id));
}
public int Server_timeout() {return server_timeout;} public Scrib_server Server_timeout_(int v) {server_timeout = v; return this;} private int server_timeout;
public int Server_timeout_polling() {return server_timeout_polling;} public Scrib_server Server_timeout_polling_(int v) {server_timeout_polling = v; return this;} private int server_timeout_polling;
public int Server_timeout_busy_wait() {return server_timeout_busy_wait;} public Scrib_server Server_timeout_busy_wait_(int v) {server_timeout_busy_wait = v; return this;} private int server_timeout_busy_wait;
public byte[] Server_comm(byte[] cmd, Object[] cmd_objs) {return Bry_.Empty;}
public void Server_send(byte[] cmd, Object[] cmd_objs) {}
public byte[] Server_recv() {return Bry_.Empty;}
public void Term() {}
private static final LuaValue
Val_server_recv = LuaValue.valueOf("server_recv")
, Val_xchunks = LuaValue.valueOf("xchunks")
, Val_chunks = LuaValue.valueOf("chunks")
;
class MWClient extends OneArgFunction {
/** The implementation of the ZeroArgFunction interface.
* This will be called once when the library is loaded via require().
* @param arg LuaString containing the name used in the call to require().
* @return Value that will be returned in the require() call. In this case,
* it is the library itself.
*/
public LuaValue call(LuaValue libname) {
LuaValue library = tableOf();
library.set("client_recv", Luaj_server_func_recv._);
LuaValue env = gplx.xowa.xtns.scribunto.engines.luaj.Luaj_server.Globals_singleton;
env.set( "MWClient", library );
return library;
}
}
}

View File

@@ -0,0 +1,40 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.luaj; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaThread;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.VarArgFunction;
public class Luaj_server_func_dbg extends VarArgFunction {
private Scrib_core core;
public void Core_(Scrib_core v) {this.core = v;}
public Varargs invoke(Varargs args) {
int len = args.narg();
Bry_bfr bfr = Bry_bfr.new_();
bfr.Add_str("<span class='xowa_dbg' style='color:red'>");
for (int i = 1; i <= len; ++i) {
String s = args.arg(i).toString();
bfr.Add_str(gplx.html.Html_utl.Escape_html_as_str(s) + "&nbsp;");
}
bfr.Add_str("</span><br/>");
core.Page().Html_data().Xtn_scribunto_dbg_(bfr.Xto_bry_and_clear());
return NONE;
}
public static Luaj_server_func_dbg _ = new Luaj_server_func_dbg();
}

View File

@@ -0,0 +1,32 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.luaj; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.OneArgFunction;
public class Luaj_server_func_recv extends OneArgFunction {
private Luaj_engine engine;
public void Engine_(Luaj_engine v) {this.engine = v;}
public LuaValue call(LuaValue tbl_val) {
LuaTable tbl = (LuaTable)tbl_val;
String op = Luaj_value_.Get_val_as_str(tbl, "op");
if (!String_.Eq(op, "call")) throw Exc_.new_("luaj_recvr only processes op calls");
return engine.Server_recv_call(tbl);
}
public static Luaj_server_func_recv _ = new Luaj_server_func_recv();
}

View File

@@ -0,0 +1,117 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.luaj; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.luaj.vm2.*;
class Luaj_value_ {
public static String Get_val_as_str(LuaTable owner, String key) {
return ((LuaString)owner.get(key)).tojstring();
}
public static LuaTable Get_val_as_lua_table(LuaTable owner, String key) {
return (LuaTable)owner.get(key);
}
public static KeyVal[] Get_val_as_kv_ary(Luaj_server server, LuaTable owner, String key) {
LuaTable table = (LuaTable)owner.get(key);
return Luaj_value_.X_tbl_to_kv_ary(server, table);
}
public static KeyVal[] X_tbl_to_kv_ary(Luaj_server server, LuaTable tbl) {
List_adp temp = List_adp_.new_();
LuaValue cur = LuaValue.NIL;
int len = 0;
while (true) { // iterate over pairs in tbl; no direct way to get kvs
Varargs itm = tbl.next(cur);
if (itm == LuaValue.NIL) break; // no more pairs; stop
LuaValue itm_val = itm.arg(2); // val is itm 2
Object itm_val_obj = X_val_to_obj(server, itm_val);
LuaValue itm_key = itm.arg(1);
KeyVal kv = null;
if (itm_val.type() == LuaValue.TFUNCTION) { // function is converted to Scrib_lua_proc
String func_key = itm_key.tojstring();
int func_id = Int_.cast_(itm_val_obj);
Scrib_lua_proc lua_func = new Scrib_lua_proc(func_key, func_id);
if (itm_key.type() == LuaValue.TSTRING) // most functions are named
kv = KeyVal_.new_(func_key, lua_func);
else // some are not; particularly "anonymous" functions in Module for gsub_function; these will have a kv of int,int; note that key must be int; if string, lua will not be able to match it back to int later
kv = KeyVal_.int_(((LuaInteger)itm_key).v, lua_func);
}
else {
switch (itm_key.type()) {
case LuaValue.TNUMBER:
kv = KeyVal_.int_(((LuaNumber)itm_key).toint(), itm_val_obj);
break;
case LuaValue.TSTRING:
kv = KeyVal_.new_(((LuaString)itm_key).tojstring(), itm_val_obj);
break;
default:
throw Exc_.new_unhandled(itm_key.type());
}
}
temp.Add(kv);
cur = itm_key;
++len;
}
if (len == 0) return KeyVal_.Ary_empty;
return (KeyVal[])temp.To_ary(KeyVal.class);
}
private static Object X_val_to_obj(Luaj_server server, LuaValue v) {
switch (v.type()) {
case LuaValue.TNIL: return null;
case LuaValue.TBOOLEAN: return ((LuaBoolean)v).toboolean();
case LuaValue.TSTRING: return ((LuaString)v).tojstring();
case LuaValue.TNUMBER:
LuaNumber v_num = (LuaNumber)v;
if (v_num.isint())
return v_num.toint();
else
return v_num.todouble();
case LuaValue.TTABLE: return X_tbl_to_kv_ary(server, (LuaTable)v);
case LuaValue.TFUNCTION: return server.Get_id_by_closure(v);
default: throw Exc_.new_unhandled(v.type());
}
}
public static LuaValue X_obj_to_val(Luaj_server server, Object o) {
if (o == null) return LuaValue.NIL;
Class<?> c = ClassAdp_.ClassOf_obj(o);
if (Object_.Eq(c, Bool_.Cls_ref_type)) return LuaValue.valueOf((Boolean)o);
else if (Object_.Eq(c, Int_.Cls_ref_type)) return LuaValue.valueOf((Integer)o);
else if (Object_.Eq(c, Double_.Cls_ref_type)) return LuaValue.valueOf((Double)o);
else if (Object_.Eq(c, String_.Cls_ref_type)) return LuaValue.valueOf((String)o);
else if (Object_.Eq(c, byte[].class)) return LuaValue.valueOf(String_.new_u8((byte[])o));
else if (Object_.Eq(c, KeyVal.class)) return X_kv_ary_to_tbl(server, (KeyVal)o);
else if (Object_.Eq(c, KeyVal[].class)) return X_kv_ary_to_tbl(server, (KeyVal[])o);
else if (Object_.Eq(c, Scrib_lua_proc.class)) return server.Get_closure_by_id(((Scrib_lua_proc)o).Id());
else return LuaValue.NIL;
}
private static LuaTable X_kv_ary_to_tbl(Luaj_server server, KeyVal... ary) {
LuaTable rv = LuaValue.tableOf();
int len = ary.length;
for (int i = 0; i < len; i++) {
KeyVal itm = ary[i];
LuaValue itm_val = X_obj_to_val(server, itm.Val());
switch (itm.Key_tid()) {
case KeyVal_.Key_tid_int:
rv.set(Int_.cast_(itm.Key_as_obj()), itm_val);
break;
case KeyVal_.Key_tid_str:
case KeyVal_.Key_tid_obj:
rv.set(itm.Key(), itm_val);
break;
}
}
return rv;
}
}

View File

@@ -0,0 +1,108 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import gplx.texts.*;
import gplx.xowa.xtns.scribunto.libs.*;
public class Process_engine implements Scrib_engine {
private Scrib_core core; private Xoae_app app; private Scrib_xtn_mgr scrib_opts;
private Process_recv_msg rsp = new Process_recv_msg(); private Process_send_wtr msg_encoder;
private Scrib_proc_mgr proc_mgr;
public Process_engine(Xoae_app app, Scrib_core core) {
this.app = app; this.core = core; this.proc_mgr = core.Proc_mgr();
msg_encoder = new Process_send_wtr(app.Usr_dlg());
server = new Process_server();
scrib_opts = (Scrib_xtn_mgr)app.Xtn_mgr().Get_or_fail(Scrib_xtn_mgr.XTN_KEY);
}
public boolean Dbg_print() {return dbg_print;} public void Dbg_print_(boolean v) {dbg_print = v;} private boolean dbg_print;
public Scrib_server Server() {return server;} public void Server_(Scrib_server v) {server = v;} Scrib_server server;
public Scrib_lua_proc LoadString(String name, String text) {
KeyVal[] rslt = this.Dispatch("op", "loadString", "text", text, "chunkName", name);
return new Scrib_lua_proc(name, Int_.cast_(rslt[0].Val()));
}
public KeyVal[] CallFunction(int id, KeyVal[] args) {
return this.Dispatch("op", "call", "id", id, "nargs", args.length, "args", args);
}
public void RegisterLibrary(KeyVal[] functions_ary) {
this.Dispatch("op", "registerLibrary", "name", Scrib_core.Key_mw_interface, "functions", functions_ary);
}
public void CleanupChunks(KeyVal[] ids) {
this.Dispatch("op", "cleanupChunks", "ids", ids);
}
public KeyVal[] ExecuteModule(int mod_id) {
return this.CallFunction(core.Lib_mw().Mod().Fncs_get_id("executeModule"), Scrib_kv_utl_.base1_obj_(new Scrib_lua_proc("", mod_id)));
}
private KeyVal[] Dispatch(Object... ary) {
Bry_bfr bfr = app.Utl__bfr_mkr().Get_k004().Clear();
while (true) {
Dispatch_bld_send(bfr, ary);
boolean log_enabled = scrib_opts.Lua_log_enabled();
if (log_enabled) app.Usr_dlg().Log_direct("sent:" + bfr.Xto_str() + "\n");
byte[] rsp_bry = server.Server_comm(bfr.Xto_bry_and_clear(), ary);
if (log_enabled) app.Usr_dlg().Log_direct("rcvd:" + String_.new_u8(rsp_bry) + "\n\n");
String op = rsp.Extract(rsp_bry);
if (String_.Eq(op, "return")) {
bfr.Mkr_rls();
return rsp.Values();
}
else if (String_.Eq(op, "error")) {
core.Handle_error(rsp.Rslt_ary()[0].Val_to_str_or_empty(), "");
return KeyVal_.Ary_empty;
}
else if (String_.Eq(op, "call")) {
String id = rsp.Call_id();
KeyVal[] args = rsp.Call_args();
Scrib_proc proc = proc_mgr.Get_by_key(id); if (proc == null) throw Scrib_xtn_mgr.err_("could not find proc with id of {0}", id);
Scrib_proc_args proc_args = new Scrib_proc_args(args);
Scrib_proc_rslt proc_rslt = new Scrib_proc_rslt();
proc.Proc_exec(proc_args, proc_rslt);
String fail_msg = proc_rslt.Fail_msg();
if (fail_msg == null) {
KeyVal[] cbk_rslts = proc_rslt.Ary();
ary = Object_.Ary("op", "return", "nvalues", cbk_rslts.length, "values", cbk_rslts);
}
else {
ary = Object_.Ary("op", "error", "value", fail_msg);
}
}
else {
bfr.Mkr_rls();
// app.Usr_dlg().Warn_many("", "", "invalid dispatch: op=~{0} page=~{1}", op, String_.new_u8(core.Ctx().Cur_page().Page_ttl().Page_db()));
return KeyVal_.Ary_empty;
}
}
} private static final byte[] Dispatch_hdr = Bry_.new_a7("0000000000000000"); // itm_len + itm_chk in 8-len HexDec
private void Dispatch_bld_send(Bry_bfr bfr, Object[] ary) {
int len = ary.length; if (len % 2 != 0) throw Exc_.new_("arguments must be factor of 2", "len", len);
bfr.Add(Dispatch_hdr);
bfr.Add_byte(Byte_ascii.Curly_bgn);
for (int i = 0; i < len; i++) {
Object itm = ary[i];
if (i % 2 == 0) {
if (i != 0) bfr.Add_byte(Byte_ascii.Comma);
msg_encoder.Encode_key(bfr, itm);
}
else
msg_encoder.Encode_obj(bfr, itm);
}
bfr.Add_byte(Byte_ascii.Curly_end);
int msg_len = bfr.Len() - 16; // 16 for Dispatch_hdr_len
int chk_len = (msg_len * 2) -1; // defined by Scribunto
HexDecUtl.Write(bfr.Bfr(), 0, 8, msg_len);
HexDecUtl.Write(bfr.Bfr(), 9, 16, chk_len);
}
}

View File

@@ -0,0 +1,59 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import gplx.core.primitives.*; import gplx.php.*;
public class Process_recv_msg {
private Php_srl_parser parser = new Php_srl_parser();
public Process_recv_msg() {
arg_keys.Add("op" , Byte_obj_val.new_(Arg_op));
arg_keys.Add("values" , Byte_obj_val.new_(Arg_values));
arg_keys.Add("id" , Byte_obj_val.new_(Arg_id));
arg_keys.Add("args" , Byte_obj_val.new_(Arg_args));
} private Hash_adp arg_keys = Hash_adp_.new_(); private static final byte Arg_op = 0, Arg_values = 1, Arg_id = 2, Arg_args = 3;
public String Op() {return op;} private String op;
public String Call_id() {return call_id;} private String call_id;
public KeyVal[] Rslt_ary() {return rslt_ary;} private KeyVal[] rslt_ary;
public KeyVal[] Values() {return values;} private KeyVal[] values;
public KeyVal[] Call_args() {return call_args;} private KeyVal[] call_args;
public String Extract(byte[] rsp) {
try {
op = call_id = null;
rslt_ary = values = call_args = null;
KeyVal[] root_ary = parser.Parse_as_kvs(rsp);
rslt_ary = (KeyVal[])root_ary[0].Val();
int len = rslt_ary.length;
for (int i = 0; i < len; i++) {
KeyVal kv = rslt_ary[i];
String kv_key = kv.Key();
Byte_obj_val bv = (Byte_obj_val)arg_keys.Get_by(kv_key);
if (bv != null) {
switch (bv.Val()) {
case Arg_op: op = kv.Val_to_str_or_empty(); break;
case Arg_values: values = (KeyVal[])kv.Val(); break;
case Arg_id: call_id = kv.Val_to_str_or_empty(); break;
case Arg_args: call_args = (KeyVal[])kv.Val(); break;
}
}
}
return op;
}
catch (Exception e) {
throw Scrib_xtn_mgr.err_(e, "failed to extract data: {0} {1}", Err_.Message_gplx_brief(e), String_.new_u8(rsp));
}
}
}

View File

@@ -0,0 +1,98 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
public class Process_send_wtr {
public Process_send_wtr(Gfo_usr_dlg usr_dlg) {this.usr_dlg = usr_dlg;} private Gfo_usr_dlg usr_dlg;
public String Encode(Object o) {
Bry_bfr tmp_bfr = Bry_bfr.reset_(Io_mgr.Len_kb);
Encode_obj(tmp_bfr, o);
return tmp_bfr.Xto_str_and_clear();
}
public void Encode_bool(Bry_bfr bfr, boolean v) {bfr.Add(v ? CONST_bool_true : CONST_bool_false);}
public void Encode_int(Bry_bfr bfr, int v) {bfr.Add_int_variable(v);}
public boolean Encode_double(Bry_bfr bfr, double v) {
if (Double_.IsNaN(v)) {usr_dlg.Warn_many(GRP_KEY, "fail_encode_double", "cannot convert non-finite number"); return false;}
bfr.Add_double(v);
return true;
}
public boolean Encode_str(Bry_bfr bfr, String v) {return Encode_str(bfr, Bry_.new_u8(v));}
public boolean Encode_str(Bry_bfr bfr, byte[] bry) {
int len = bry.length;
bfr.Add_byte(Byte_ascii.Quote);
for (int i = 0; i < len; i++) {
byte b = bry[i];
switch (b) {
case Byte_ascii.Quote: bfr.Add_byte(Byte_ascii.Backslash).Add_byte(b); break;
case Byte_ascii.Nl: bfr.Add_byte(Byte_ascii.Backslash).Add_byte(Byte_ascii.Ltr_n); break;
case Byte_ascii.Cr: bfr.Add_byte(Byte_ascii.Backslash).Add_byte(Byte_ascii.Ltr_r); break;
case Byte_ascii.Nil: bfr.Add(CONST_escape_000); break;
case Byte_ascii.Backslash: bfr.Add_byte(Byte_ascii.Backslash).Add_byte(Byte_ascii.Backslash); break;
default: bfr.Add_byte(b); break;
}
}
bfr.Add_byte(Byte_ascii.Quote);
return true;
}
public boolean Encode_prc(Bry_bfr bfr, Scrib_lua_proc prc) {
bfr.Add(Prc_bgn);
bfr.Add_int_variable(prc.Id());
bfr.Add_byte(Byte_ascii.Brack_end);
return true;
} static final byte[] Prc_bgn = Bry_.new_a7("chunks[");
private boolean Encode_ary(Bry_bfr bfr, KeyVal[] ary) {
int len = ary.length;
bfr.Add_byte(Byte_ascii.Curly_bgn);
for (int i = 0; i < len; i++) {
if (i != 0) bfr.Add_byte(Byte_ascii.Comma);
KeyVal itm = ary[i];
Encode_key(bfr, itm.Key_as_obj());
Encode_obj(bfr, itm.Val());
}
bfr.Add_byte(Byte_ascii.Curly_end);
return true;
}
private boolean Encode_kv(Bry_bfr bfr, KeyVal kv) {
bfr.Add_byte(Byte_ascii.Curly_bgn);
Encode_key(bfr, kv.Key_as_obj());
Encode_obj(bfr, kv.Val());
bfr.Add_byte(Byte_ascii.Curly_end);
return true;
}
public void Encode_key(Bry_bfr bfr, Object key) {
bfr.Add_byte(Byte_ascii.Brack_bgn);
Encode_obj(bfr, key);
bfr.Add_byte(Byte_ascii.Brack_end);
bfr.Add_byte(Byte_ascii.Eq);
}
public boolean Encode_obj(Bry_bfr bfr, Object o) {
if (o == null) {bfr.Add(CONST_nil); return true;}
Class<?> c = ClassAdp_.ClassOf_obj(o);
if (Object_.Eq(c, Bool_.Cls_ref_type)) Encode_bool(bfr, Bool_.cast_(o));
else if (Object_.Eq(c, Int_.Cls_ref_type)) Encode_int(bfr, Int_.cast_(o));
else if (Object_.Eq(c, Double_.Cls_ref_type)) {if (!Encode_double(bfr, Double_.cast_(o))) return false;}
else if (Object_.Eq(c, String.class)) {if (!Encode_str(bfr, (String)o)) return false;}
else if (Object_.Eq(c, byte[].class)) {if (!Encode_str(bfr, (byte[])o)) return false;} // NOTE: not in Scribunto; added here for PERF of not re-creating a String Object
else if (Object_.Eq(c, Scrib_lua_proc.class)) {if (!Encode_prc(bfr, (Scrib_lua_proc)o)) return false;}
else if (Object_.Eq(c, KeyVal.class)) {if (!Encode_kv(bfr, (KeyVal)o)) return false;}
else if (Object_.Eq(c, KeyVal[].class)) {if (!Encode_ary(bfr, (KeyVal[])o)) return false;}
else {throw Scrib_xtn_mgr.err_("Object cannot be serialized: {0}", ClassAdp_.NameOf_obj(o));}
return true;
}
private static final byte[] CONST_nil = Bry_.new_a7("nil"), CONST_bool_true = Bry_.new_a7("true"), CONST_bool_false = Bry_.new_a7("false"), CONST_escape_000 = Bry_.new_a7("\\000");
private static final String GRP_KEY = "xowa-scribunto-lua-srl";
}

View File

@@ -0,0 +1,42 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.junit.*;
public class Process_send_wtr_tst {
@Before public void init() {fxt.Clear();} Scrib_lua_srl_fxt fxt = new Scrib_lua_srl_fxt();
@Test public void Encode_str_basic() {fxt.Test_encode("ab" , "\"ab\"");}
@Test public void Encode_str_quote() {fxt.Test_encode("a\"b" , "\"a\\\"b\"");}
@Test public void Encode_str_nl() {fxt.Test_encode("a\nb" , "\"a\\nb\"");}
@Test public void Encode_str_cr() {fxt.Test_encode("a\rb" , "\"a\\rb\"");}
@Test public void Encode_str_nil() {fxt.Test_encode("a\0b" , "\"a\\000b\"");}
@Test public void Encode_ary_empty() {fxt.Test_encode(fxt.ary_() , "{}");}
@Test public void Encode_ary_one() {fxt.Test_encode(fxt.ary_(fxt.itm_("key", 123)), "{[\"key\"]=123}");}
@Test public void Encode_ary_many() {fxt.Test_encode(fxt.ary_(fxt.itm_("k1", 123), fxt.itm_("k2", 234)), "{[\"k1\"]=123,[\"k2\"]=234}");}
@Test public void Encode_ary_nest() {fxt.Test_encode(fxt.ary_(fxt.itm_("k1", fxt.ary_(fxt.itm_("k2", 234)))), "{[\"k1\"]={[\"k2\"]=234}}");}
}
class Scrib_lua_srl_fxt {
public void Clear() {
if (srl == null) {
usr_dlg = Gfo_usr_dlg_.Test();
srl = new Process_send_wtr(usr_dlg);
}
} Process_send_wtr srl; Gfo_usr_dlg usr_dlg;
public KeyVal[] ary_(KeyVal... ary) {return ary;}
public KeyVal itm_(String key, Object val) {return KeyVal_.new_(key, val);}
public void Test_encode(Object o, String expd) {Tfds.Eq(expd, srl.Encode(o));}
}

View File

@@ -0,0 +1,127 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import gplx.core.threads.*;
import gplx.texts.HexDecUtl;
import gplx.xowa.xtns.scribunto.*;
import gplx.core.threads.Thread_adp_;
import gplx.ios.*;
import java.io.*;
public class Process_server implements Scrib_server {
private Process process;
private OutputStream stream_write; private Process_server_gobbler_recv stream_read; private byte[] bry_header = new byte[16], bry_body = new byte[Io_mgr.Len_kb * 4], bry_error = new byte[Io_mgr.Len_kb * 4];
private Process_stream_rdr process_rdr;
private Process_server_gobbler_error error_reader;
public int Server_timeout() {return server_timeout;} public Scrib_server Server_timeout_(int v) {server_timeout = v; return this;} int server_timeout = 8000;
public int Server_timeout_polling() {return server_timeout_polling;} public Scrib_server Server_timeout_polling_(int v) {server_timeout_polling = v; return this;} int server_timeout_polling = 1;
public int Server_timeout_busy_wait() {return server_timeout_busy_wait;} public Scrib_server Server_timeout_busy_wait_(int v) {server_timeout_busy_wait = v; return this;} int server_timeout_busy_wait = 250;
public void Init(String... process_args) {
process_rdr = new Process_stream_rdr(bry_header, bry_body);
ProcessBuilder pb = new ProcessBuilder(process_args);
pb.redirectErrorStream(false);
try {process = pb.start();}
catch (Exception e) {throw Exc_.new_exc(e, "core", "process init failed", "args", String_.AryXtoStr(process_args));}
stream_write = process.getOutputStream();
error_reader = new Process_server_gobbler_error(process.getErrorStream(), bry_error);
error_reader.Start();
}
public byte[] Server_comm(byte[] cmd, Object[] cmd_objs) {
Server_send(cmd, cmd_objs);
return Server_recv();
}
public void Server_send(byte[] cmd, Object[] cmd_objs) {
if (process == null) throw Exc_.new_("process not started");
cmd_last = cmd;
// stream_read.Data_reset();
stream_read = new Process_server_gobbler_recv(process.getInputStream(), process_rdr).Start();
try {
stream_write.write(cmd);
stream_write.flush();
}
catch (Exception e) {throw Exc_.new_exc(e, "core", "failed to write to output");}
} private byte[] cmd_last;
public byte[] Server_recv() {
long time_bgn = System.currentTimeMillis();
long time_woke = time_bgn;
while (true) {
byte[] rv = stream_read.Data();
if (rv != null) return rv;
long time_now = System.currentTimeMillis();
if (time_now > time_woke + server_timeout_busy_wait) {
if (time_now > time_bgn + server_timeout) throw Scrib_xtn_mgr.err_("lua_timeout: timeout={0} cmd={1}", server_timeout, String_.new_u8(cmd_last));
Thread_adp_.Sleep(server_timeout_polling);
time_woke = System.currentTimeMillis();
}
}
}
public void Term() {
if (error_reader != null) error_reader.Term(); // NOTE: need to interrupt close, else process stays open
if (stream_read != null) stream_read.Term();
if (process != null) process.destroy();
}
}
class Process_server_gobbler_error extends Thread {
private byte[] bfr;
private InputStream stream;
public Process_server_gobbler_error(InputStream stream, byte[] bfr) {
super("process.lua.error");
this.stream = stream;
this.bfr = bfr;
}
public Process_server_gobbler_error Start() {this.start(); return this;}
public void run() {
try {
while (true) { // loop b/c one gobbler is reused for multiple calls
stream.read(bfr);
if (terminating) break;
// ThreadAdp_.Sleep(100); commented out; DATE:2014-03-20
}
}
catch (Exception e) {
if (terminating)
return;
else
throw Exc_.new_exc(e, "core", "failed to write to output");
}
}
public void Term() {
terminating = true;
this.interrupt();
} boolean terminating = false;
}
class Process_server_gobbler_recv extends Thread {
private Process_stream_rdr process_rdr;
private InputStream stream;
public Process_server_gobbler_recv(InputStream stream, Process_stream_rdr process_rdr) {
super("process.lua.read");
this.stream = stream; this.process_rdr = process_rdr;
}
public void Data_reset() {data = null;}
public byte[] Data() {return data;} private byte[] data;
public Process_server_gobbler_recv Start() {
process_rdr.Rdr().UnderRdr_(stream);
this.start();
return this;
}
public void run() {
data = process_rdr.Read();
}
public void Term() {
this.interrupt();
}
}

View File

@@ -0,0 +1,94 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
public class Process_server_mock implements Scrib_server {
private List_adp rsps = List_adp_.new_(); private int rsps_idx = 0;
public void Init(String... process_args) {}
public int Server_timeout() {return server_timeout;} public Scrib_server Server_timeout_(int v) {server_timeout = v; return this;} private int server_timeout = 8000;
public int Server_timeout_polling() {return server_timeout_polling;} public Scrib_server Server_timeout_polling_(int v) {server_timeout_polling = v; return this;} private int server_timeout_polling = 1;
public int Server_timeout_busy_wait() {return server_timeout_busy_wait;} public Scrib_server Server_timeout_busy_wait_(int v) {server_timeout_busy_wait = v; return this;} private int server_timeout_busy_wait = 250;
public byte[] Server_comm(byte[] cmd, Object[] cmd_objs) {
Server_send(cmd, cmd_objs);
return Server_recv();
}
public void Server_send(byte[] cmd, Object[] cmd_objs) {
this.cmd_objs = cmd_objs;
log_rcvd.Add(String_.new_u8(cmd));
} Object[] cmd_objs;
public byte[] Server_recv() {
Process_server_mock_rcvd rcvd = (Process_server_mock_rcvd)rsps.Get_at(rsps_idx++);
String rv = rcvd.Bld(cmd_objs);
log_sent.Add(rv);
return Bry_.new_u8(rv);
}
public void Term() {}
public void Clear() {rsps.Clear(); rsps_idx = 0; log_rcvd.Clear(); log_sent.Clear();}
public boolean Print_key() {return print_key;} public Process_server_mock Print_key_(boolean v) {print_key = v; return this;} private boolean print_key;
public void Prep_add(String v) {rsps.Add(new Process_server_mock_rcvd_str(v));}
public void Prep_add_dynamic_val() {rsps.Add(new Process_server_mock_rcvd_val(print_key));}
public List_adp Log_rcvd() {return log_rcvd;} private List_adp log_rcvd = List_adp_.new_();
public List_adp Log_sent() {return log_sent;} private List_adp log_sent = List_adp_.new_();
}
interface Process_server_mock_rcvd {
String Bld(Object[] cmd_obs);
}
class Process_server_mock_rcvd_str implements Process_server_mock_rcvd {
public Process_server_mock_rcvd_str(String rcvd) {this.rcvd = rcvd;} private String rcvd;
public String Bld(Object[] cmd_obs) {return rcvd;}
}
class Process_server_mock_rcvd_val implements Process_server_mock_rcvd {
public Process_server_mock_rcvd_val(boolean print_key) {this.print_key = print_key;} private boolean print_key;
public String Bld(Object[] cmd_objs) {
Bry_bfr tmp_bfr = Bry_bfr.new_();
Bld_recursive(tmp_bfr, 0, (KeyVal[])cmd_objs[5]);
byte[] values_str = tmp_bfr.Xto_bry_and_clear();
tmp_bfr.Add(Bry_rv_bgn).Add_int_variable(values_str.length).Add(Bry_rv_mid).Add(values_str).Add(Bry_rv_end);
return tmp_bfr.Xto_str_and_clear();
}
private void Bld_recursive(Bry_bfr bfr, int depth, KeyVal[] ary) {
int len = ary.length;
for (int i = 0; i < len; i++) {
if (i != 0) bfr.Add_byte(Byte_ascii.Semic);
KeyVal kv = ary[i];
Object kv_val = kv.Val();
if (kv_val == null) {
bfr.Add(gplx.json.Json_itm_.Const_null);
continue;
}
Class<?> kv_val_type = kv_val.getClass();
boolean kv_val_is_array = ClassAdp_.Eq(kv_val_type, KeyVal[].class);
if (print_key && !kv_val_is_array)
bfr.Add_str(kv.Key()).Add_byte(Byte_ascii.Colon);
if (ClassAdp_.Eq(kv_val_type, Bool_.Cls_ref_type))
bfr.Add(Bool_.cast_(kv_val) ? gplx.json.Json_itm_.Const_true : gplx.json.Json_itm_.Const_false);
else if (kv_val_is_array) {
KeyVal[] sub = (KeyVal[])kv_val;
if (sub.length == 0) {bfr.Add_byte(Byte_ascii.Curly_bgn).Add_byte(Byte_ascii.Curly_end);}
else {
bfr.Add_byte_nl();
bfr.Add_byte_repeat(Byte_ascii.Space, (depth + 1) * 2);
Bld_recursive(bfr, depth + 1, (KeyVal[])kv_val);
}
}
else
bfr.Add_str(kv.Val_to_str_or_empty());
}
}
private static final byte[] Bry_rv_bgn = Bry_.new_a7("a:3:{s:2:\"op\";s:6:\"return\";s:7:\"nvalues\";i:1;s:6:\"values\";a:1:{i:1;s:"), Bry_rv_mid = Bry_.new_a7(":\""), Bry_rv_end = Bry_.new_a7("\";}}");
}

View File

@@ -0,0 +1,88 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import gplx.ios.*; import gplx.texts.*;
public class Process_stream_rdr {
public Process_stream_rdr(byte[] bry_header, byte[] bry_body) {this.bry_header = bry_header; this.bry_body = bry_body;} private byte[] bry_header, bry_body;
public IoStream_stream_rdr Rdr() {return rdr;} IoStream_stream_rdr rdr = new IoStream_stream_rdr();
public byte[] Read() {
int bytes_read = rdr.Read(bry_header, 0, 16);
if (bytes_read < 16) {
if (bytes_read == -1) return null; // stream closed; should only occur when shutting down
else throw Exc_.new_("failed to read header");
}
int body_len = HexDecUtl.parse_or_(bry_header, 0,8, -1); if (body_len == -1) throw Exc_.new_("failed to read body_len");
int chk_len= HexDecUtl.parse_or_(bry_header, 9, 16, -1); if (chk_len == -1 || chk_len != (body_len * 2) - 1) throw Exc_.new_("failed to read chk_len");
byte[] trg_bry = (body_len > bry_body.length) ? new byte[body_len] : bry_body;
return Read_body(trg_bry, body_len, rdr);
}
public byte[] Read_body(byte[] trg_bry, int src_len, IoStream rdr) {
int src_len_orig = src_len;
int src_pos = 0, trg_bgn = 0;
boolean escaped = false, escaped_once = false;
int adj = 0;
while (src_pos < src_len_orig) {
int read_len = rdr.Read(trg_bry, trg_bgn, src_len_orig - src_pos); // NOTE: 1st requests 16k, but only get back 2k; 2nd request 14k and only gets back 2k; etc.
for (int i = 0; i < read_len; i++) {
int trg_idx = trg_bgn + i;
byte b = trg_bry[trg_idx];
if (escaped) { // escaped mode; convert cur byte to appropriate byte
switch (b) {
case Byte_ascii.Ltr_n: b = Byte_ascii.Nl; break;
case Byte_ascii.Ltr_r: b = Byte_ascii.Cr; break;
case Byte_ascii.Backslash: b = Byte_ascii.Backslash; break;
default: throw Exc_.new_unhandled(b);
}
}
else { // regular mode
if (b == Byte_ascii.Backslash) {
escaped = true; // flip flag
escaped_once = true;
--src_len; // add 1 to src_len; EX: "a\\nb" has 4 chars in stream, but src_len is reported as 3
++adj; // add 1 to adj
continue; // skip section below; cur byte is backslash, and is discarded (next byte is important)
}
}
if (adj > 0 // check if there is adj; if no adj, don't bother overwriting; if adj, then shift all characters backwards; SEE:NOTE_1
|| escaped_once) // NOTE: when trg_idx = 0, adj is always 0 but escaped can be true; occurs when straddling reads; EX: "a\nb\" -> 97
trg_bry[trg_idx - adj] = b;
escaped = false;
}
src_pos += read_len;
trg_bgn += read_len;
}
return Bry_.Mid(trg_bry, 0, src_len);
}
}
/*
NOTE_1:
EX: "a\nb\nc"
. has src_len of 5: MWServer.lua counts chars exactly
. has src_str of "a\\nb\\nc" (len of 7 characters: a, backslash, n, b, backslash, n, c); MWServer.lua converts \n -> \\n (or in ASCII, 10 -> 92 110)
. so, process is as follows
.. start :097 092 110 098 092 110 099 a \\ n b \\ n c)
.. pass_0: 097 a
.. pass_1: 097 092 \ escape mode entered; adj = 1
.. pass_2a: 097 092 110 n n reached
.. pass_2b: 097 092 010 n converted to \n
.. pass_2c: 097 010 010 \n shifted down 1 to overwrite backslash
.. pass_3: 097 010 098 b b shifted down 1 to overwrite \n
.. pass_4: 097 010 098 092 \ escape mode entered; adj = 2
.. pass_5: 097 010 098 010 n same as pass 2a-2c, except shift is 2
.. pass_6: 097 010 098 010 099 c
*/

View File

@@ -0,0 +1,79 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.xtns.scribunto.engines.process; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*;
import org.junit.*;
import gplx.ios.*;
public class Process_stream_rdr_tst {
@Before public void init() {fxt.Clear();} Scrib_lua_srl_fxt fxt = new Scrib_lua_srl_fxt();
@Test public void Body_basic() {
Process_stream_rdr_fxt fxt2 = new Process_stream_rdr_fxt().Init();
fxt2.Init_src_str_("abcd").Test_read_body(); // read_1
fxt2.Init_src_str_("abcde").Test_read_body(); // read_2
fxt2.Init_src_str_("abcdefghijklm").Test_read_body(); // read_3
fxt2.Init_src_str_w_nl_("a\\nb").Test_read_body(); // nl; read_1
fxt2.Init_src_str_w_nl_("\\n\\\\ab").Test_read_body(); // nl; read_2
fxt2.Init_src_str_w_nl_("\\n\\\\a").Test_read_body(); // nl; backslash
fxt2.Init_src_str_w_nl_("a\\nb\\nc").Test_read_body(); // nl; straddling reads
}
}
class Process_stream_rdr_fxt {
public Process_stream_rdr_fxt Init() {
if (process == null) {
bry_header = new byte[16];
bry_body = Bry_.Empty;
rdr = new IoStream_mock().Read_limit_(5);
process = new Process_stream_rdr(new byte[16], new byte[16]);
}
return this;
} byte[] bry_header, bry_body; Process_stream_rdr process; IoStream_mock rdr;
public Process_stream_rdr_fxt Init_src_str_(String v) {this.src_bry = Bry_.new_a7(v); src_len = src_bry.length; expd_str = v; return this;} private byte[] src_bry;
public Process_stream_rdr_fxt Init_src_len_(int v) {this.src_len = v; return this;} private int src_len;
public Process_stream_rdr_fxt Expd_str_(String v) {this.expd_str = v; return this;} private String expd_str;
public Process_stream_rdr_fxt Init_src_str_w_nl_(String v) {
this.Init_src_str_(v);
int len = src_bry.length;
for (int i = 0; i < len; i++) {
byte b = src_bry[i];
switch (b) {
case Byte_ascii.Backslash:
++i;
b = src_bry[i];
switch (b) {
case Byte_ascii.Backslash: bfr.Add_byte(Byte_ascii.Backslash); 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;
default: throw Exc_.new_unhandled(b);
}
break;
default:
bfr.Add_byte(b);
break;
}
}
expd_str = bfr.Xto_str_and_clear();
return this;
} Bry_bfr bfr = Bry_bfr.reset_(128);
public void Test_read_body() {
rdr.Data_bry_(src_bry);
byte[] bry_body = new byte[src_len];
rdr.Reset();
byte[] rv = process.Read_body(bry_body, src_len, rdr);
Tfds.Eq(expd_str, String_.new_a7(rv));
Tfds.Eq(src_bry.length, rdr.Data_bry_pos());
}
}