diff --git a/100_core/.classpath b/100_core/.classpath new file mode 100644 index 000000000..0240594b5 --- /dev/null +++ b/100_core/.classpath @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/100_core/.project b/100_core/.project new file mode 100644 index 000000000..71ba1b5d0 --- /dev/null +++ b/100_core/.project @@ -0,0 +1,17 @@ + + + 100_core + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/100_core/lib/commons-compress-1.5.jar b/100_core/lib/commons-compress-1.5.jar new file mode 100644 index 000000000..0239414e8 Binary files /dev/null and b/100_core/lib/commons-compress-1.5.jar differ diff --git a/100_core/src/gplx/String_bldr.java b/100_core/src/gplx/String_bldr.java new file mode 100644 index 000000000..83bb7c2b0 --- /dev/null +++ b/100_core/src/gplx/String_bldr.java @@ -0,0 +1,115 @@ +/* +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; +public interface String_bldr { + boolean Has_none(); + boolean Has_some(); + String_bldr Add_many(String... array); + String_bldr Add_fmt(String format, Object... args); + String_bldr Add_fmt_line(String format, Object... args); + String_bldr Add_kv(String hdr, String val); + String_bldr Add_kv_obj(String k, Object v); + String_bldr Add_char_pipe(); + String_bldr Add_char_nl(); + String_bldr Add_char_crlf(); + String_bldr Add_str_w_crlf(String v); + String_bldr Add_spr_unless_first(String s, String spr, int i); + String_bldr Clear(); + String XtoStrAndClear(); + String XtoStr(); + int Count(); + String_bldr Add(byte[] v); + String_bldr Add(String s); + String_bldr Add(char c); + String_bldr Add(int i); + String_bldr Add_obj(Object o); + String_bldr Add_mid(char[] ary, int bgn, int count); + String_bldr Add_at(int idx, String s); + String_bldr Del(int bgn, int len); +} +abstract class String_bldr_base implements String_bldr { + public boolean Has_none() {return this.Count() == 0;} + public boolean Has_some() {return this.Count() > 0;} + public String_bldr Add_many(String... array) {for (String s : array) Add(s); return this;} + public String_bldr Add_fmt(String format, Object... args) {Add(String_.Format(format, args)); return this;} + public String_bldr Add_fmt_line(String format, Object... args) {Add_str_w_crlf(String_.Format(format, args)); return this;} + public String_bldr Add_kv_obj(String k, Object v) { + if (this.Count() != 0) this.Add(" "); + this.Add_fmt("{0}={1}", k, Object_.XtoStr_OrNullStr(v)); + return this; + } + public String_bldr Add_char_pipe() {return Add("|");} + public String_bldr Add_char_nl() {Add(Op_sys.Lnx.Nl_str()); return this;} + public String_bldr Add_char_crlf() {Add(Op_sys.Wnt.Nl_str()); return this;} + public String_bldr Add_str_w_crlf(String line) {Add(line); Add(String_.CrLf); return this;} + public String_bldr Add_spr_unless_first(String s, String spr, int i) { + if (i != 0) Add(spr); + Add(s); + return this; + } + public String_bldr Add_kv(String hdr, String val) { + if (String_.Len_eq_0(val)) return this; + if (this.Count() != 0) this.Add(' '); + this.Add(hdr); + this.Add(val); + return this; + } + public String_bldr Clear() {Del(0, Count()); return this;} + public String XtoStrAndClear() { + String rv = XtoStr(); + Clear(); + return rv; + } + @Override public String toString() {return XtoStr();} + public abstract String XtoStr(); + public abstract int Count(); + public abstract String_bldr Add_at(int idx, String s); + public abstract String_bldr Add(byte[] v); + public abstract String_bldr Add(String s); + public abstract String_bldr Add(char c); + public abstract String_bldr Add(int i); + public abstract String_bldr Add_mid(char[] ary, int bgn, int count); + public abstract String_bldr Add_obj(Object o); + public abstract String_bldr Del(int bgn, int len); +} +class String_bldr_thread_single extends String_bldr_base { + private java.lang.StringBuilder sb = new java.lang.StringBuilder(); + @Override public String XtoStr() {return sb.toString();} + @Override public int Count() {return sb.length();} + @Override public String_bldr Add_at(int idx, String s) {sb.insert(idx, s); return this;} + @Override public String_bldr Add(byte[] v) {sb.append(String_.new_utf8_(v)); return this;} + @Override public String_bldr Add(String s) {sb.append(s); return this;} + @Override public String_bldr Add(char c) {sb.append(c); return this;} + @Override public String_bldr Add(int i) {sb.append(i); return this;} + @Override public String_bldr Add_mid(char[] ary, int bgn, int count) {sb.append(ary, bgn, count); return this;} + @Override public String_bldr Add_obj(Object o) {sb.append(o); return this;} + @Override public String_bldr Del(int bgn, int len) {sb.delete(bgn, len); return this;} +} +class String_bldr_thread_multiple extends String_bldr_base { + private java.lang.StringBuffer sb = new java.lang.StringBuffer(); + @Override public String XtoStr() {return sb.toString();} + @Override public int Count() {return sb.length();} + @Override public String_bldr Add_at(int idx, String s) {sb.insert(idx, s); return this;} + @Override public String_bldr Add(byte[] v) {sb.append(String_.new_utf8_(v)); return this;} + @Override public String_bldr Add(String s) {sb.append(s); return this;} + @Override public String_bldr Add(char c) {sb.append(c); return this;} + @Override public String_bldr Add(int i) {sb.append(i); return this;} + @Override public String_bldr Add_mid(char[] ary, int bgn, int count) {sb.append(ary, bgn, count); return this;} + @Override public String_bldr Add_obj(Object o) {sb.append(o); return this;} + @Override public String_bldr Del(int bgn, int len) {sb.delete(bgn, len); return this;} +} diff --git a/100_core/src/gplx/String_bldr_.java b/100_core/src/gplx/String_bldr_.java new file mode 100644 index 000000000..81f7aa3ce --- /dev/null +++ b/100_core/src/gplx/String_bldr_.java @@ -0,0 +1,22 @@ +/* +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; +public class String_bldr_ { + public static String_bldr new_() {return new String_bldr_thread_single();} + public static String_bldr new_thread() {return new String_bldr_thread_multiple();} +} diff --git a/100_core/src_000_err/gplx/Err.java b/100_core/src_000_err/gplx/Err.java new file mode 100644 index 000000000..fec628f30 --- /dev/null +++ b/100_core/src_000_err/gplx/Err.java @@ -0,0 +1,63 @@ +/* +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; +public class Err extends RuntimeException { + @Override public String getMessage() {return Message_gplx();} + public String Key() {return key;} public Err Key_(String v) {key = v; return this;} private String key = ""; + public String Hdr() {return hdr;} public Err Hdr_(String v) {hdr = v; return this;} private String hdr = ""; + public ListAdp Args() {return args;} ListAdp args = ListAdp_.new_(); + public Err Add(String k, Object o) {args.Add(KeyVal_.new_(k, o)); return this;} + @gplx.Internal protected ErrProcData Proc() {return proc;} ErrProcData proc = ErrProcData.Null; + public OrderedHash CallStack() {return callStack;} OrderedHash callStack = OrderedHash_.new_(); + public int CallLevel() {return callLevel;} public Err CallLevel_(int val) {callLevel = val; return this;} public Err CallLevel_1_() {return CallLevel_(1);} int callLevel; + public Err Inner() {return inner;} Err inner; + @gplx.Internal protected static Err hdr_(String hdr) { + Err rv = new Err(); + rv.hdr = hdr; + return rv; + } @gplx.Internal protected Err() {} + @gplx.Internal protected static Err exc_(Exception thrown, String hdr) { + Err rv = hdr_(hdr); + rv.inner = convert_(thrown); + for (int i = 0; i < rv.inner.callStack.Count(); i++) { + ErrProcData itm = (ErrProcData)rv.inner.callStack.FetchAt(i); + rv.callStack.Add(itm.Raw(), itm); + } + return rv; + } + @gplx.Internal protected static Err convert_(Exception thrown) { + Err rv = Err_.as_(thrown); + if (rv == null) + rv = Err_.new_key_(ClassAdp_.NameOf_obj(thrown), Err_.Message_lang(thrown)); + CallStack_fill(rv, Err_.StackTrace_lang(rv)); + return rv; + } + static void CallStack_fill(Err err, String stackTrace) { + ErrProcData[] ary = ErrProcData.parse_ary_(stackTrace); if (Array_.Len(ary) == 0) return; // no callStack; shouldn't happen, but don't throw error + err.proc = ary[0]; + for (ErrProcData itm : ary) { + String key = itm.Raw(); + if (err.callStack.Has(key)) continue; + err.callStack.Add(key, itm); + } + } + String Message_gplx() { + try {return Err_.Message_gplx(this);} + catch (Exception exc) {Err_.Noop(exc); return super.getMessage();} + } +} diff --git a/100_core/src_000_err/gplx/ErrMsgWtr.java b/100_core/src_000_err/gplx/ErrMsgWtr.java new file mode 100644 index 000000000..9f56ce113 --- /dev/null +++ b/100_core/src_000_err/gplx/ErrMsgWtr.java @@ -0,0 +1,111 @@ +/* +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; +public class ErrMsgWtr { + public String Message_gplx(Exception thrown) { + Err err = Err.convert_(thrown); // convert thrown to Err to make rest of class easier + Err[] innerAry = InnerAsAry(err); + String_bldr sb = String_bldr_.new_(); + WriteError(innerAry, sb, 0); + WriteInner(innerAry, sb); + WriteStack(innerAry, sb); + return sb.XtoStr(); + } + public String Message_gplx_brief(Exception thrown) { + Err err = Err.convert_(thrown); // convert thrown to Err to make rest of proc easier + String_bldr sb = String_bldr_.new_(); + sb.Add(err.Hdr()); + if (err.Args().Count() > 0) sb.Add(" --"); + for (Object kvo : err.Args()) { + KeyVal kv = (KeyVal)kvo; + String key = kv.Key(), val = kv.Val_to_str_or_empty(); + sb.Add_fmt(" {0}='{1}'", key, val); + } + sb.Add_fmt(" [{0}]", err.Key()); + return sb.XtoStr(); + } + void WriteInner(Err[] errAry, String_bldr sb) { + int len = Array_.Len(errAry); if (len <= 1) return; // no inners; return; + for (int i = 1; i < len; i++) + WriteError(errAry, sb, i); + } + void WriteError(Err[] errAry, String_bldr sb, int i) { + Err err = errAry[i]; + String msg = err.Hdr(); + String typ = String_.Eq(err.Key(), "") ? "" : String_.Concat(" <", err.Key(), ">"); + boolean onlyOne = errAry.length == 1; + String idxStr = onlyOne ? "" : Int_.XtoStr(i); + sb.Add(idxStr).Add("\t").Add(msg).Add(typ).Add_char_crlf(); // ex: " @count must be > 0 " + WriteKeyValAry(sb, err.Args()); + sb.Add("\t").Add(err.Proc().SignatureRaw()).Add_char_crlf(); +// WriteKeyValAry(sb, err.ProcArgs()); + } + void WriteKeyValAry(String_bldr sb, ListAdp ary) { + // calc keyMax for valIndentLen + int keyMax = 0; + for (Object o : ary) { + KeyVal kv = (KeyVal)o; + int keyLen = String_.Len(kv.Key()); + if (keyLen > keyMax) keyMax = keyLen + 1; // +1 to guarantee one space between key and val + } + if (keyMax < 8)keyMax = 8; // separate by at least 8 chars + for (Object o : ary) { + KeyVal kv = (KeyVal)o; + String key = kv.Key(); int keyLen = String_.Len(key); + String valIndent = String_.Repeat(" ", keyMax - keyLen); + sb.Add("\t\t@").Add(key).Add(valIndent).Add(kv.Val_to_str_or_empty()).Add_char_crlf(); + } + } + void WriteStack(Err[] errAry, String_bldr sb) { + if (Env_.Mode_testing()) return; // only write stack when not testing + int len = Array_.Len(errAry); if (len == 0) return; // shouldn't happen, but don't want to throw err + Err first = errAry[0]; + boolean onlyOne = len == 1; + sb.Add_str_w_crlf(String_.Repeat("-", 80)); + ListAdp tmp = ListAdp_.new_(); + OrderedHash callStack = first.CallStack(); int callStackCount = callStack.Count(); + for (int i = 0; i < callStackCount ; i++) { + ErrProcData proc = (ErrProcData)callStack.FetchAt(i); + // get procIndex + int idx = -1; + for (int j = 0; j < len; j++) { + ErrProcData comp = errAry[j].Proc(); + if (String_.Eq(proc.Raw(), comp.Raw())) {idx = j; break;} + } + String idxStr = onlyOne ? "" : Int_.XtoStr(idx); + String hdr = idx == -1 ? "\t" : idxStr + "\t"; + String ideAddressSpr = String_.CrLf + "\t\t"; + String ideAddress = String_.Eq(proc.IdeAddress(), "") ? "" : ideAddressSpr + proc.IdeAddress(); // NOTE: ideAddress will be blank in compiled mode + String msg = String_.Concat(hdr, proc.SignatureRaw(), ideAddress); + tmp.Add(msg); + } + tmp.Reverse(); + for (Object o : tmp) + sb.Add_str_w_crlf((String)o); + } + static Err[] InnerAsAry(Err err) { + ListAdp errAry = ListAdp_.new_(); + Err cur = Err_.as_(err); + while (cur != null) { + errAry.Add(cur); + cur = cur.Inner(); + } + return (Err[])errAry.XtoAry(Err.class); + } + public static final ErrMsgWtr _ = new ErrMsgWtr(); ErrMsgWtr() {} +} diff --git a/100_core/src_000_err/gplx/ErrProcData.java b/100_core/src_000_err/gplx/ErrProcData.java new file mode 100644 index 000000000..255b19736 --- /dev/null +++ b/100_core/src_000_err/gplx/ErrProcData.java @@ -0,0 +1,67 @@ +/* +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; +class ErrProcData { + public String Raw() {return raw;} public ErrProcData Raw_(String val) {raw = val; return this;} private String raw = String_.Empty; + public String SignatureRaw() {return signatureRaw;} public ErrProcData SignatureRaw_(String val) {signatureRaw = val; return this;} private String signatureRaw = String_.Empty; + public String SourceFileRaw() {return sourceFileRaw;} public ErrProcData SourceFileRaw_(String val) {sourceFileRaw = val; return this;} private String sourceFileRaw = String_.Empty; + public int SourceLine() {return sourceLine;} public ErrProcData SourceLine_(int val) {sourceLine = val; return this;} int sourceLine; + public String IdeAddress() {return ideAddress;} public ErrProcData IdeAddress_(String val) {ideAddress = val; return this;} private String ideAddress = String_.Empty; + + public static ErrProcData[] parse_ary_(String stackTrace) { + /* + .java + ' at gplx.Err.new_(Err.java:92) + at gplx.Err.exc_(Err.java:43) + at gplx.Err_.err_(Err_.java:4) + at gplx._tst.Err__tst.RdrLoad(Err__tst.java:77) + at gplx._tst.Err__tst.MgrInit(Err__tst.java:76)' + .cs + ' at gplx._tst.Err__tst.RdrLoad() in c:\000\200_dev\100.gplx\100.framework\100.core\gplx\tst\gplx\err__tst.cs:line 77 + at gplx._tst.Err__tst.MgrInit(String s) in c:\000\200_dev\100.gplx\100.framework\100.core\gplx\tst\gplx\err__tst.cs:line 76' + */ + if (stackTrace == null) return new ErrProcData[0]; + String[] lines = String_.SplitLines_any(stackTrace); + ListAdp list = ListAdp_.new_(); + int len = Array_.Len(lines); + for (int i = 0; i < len; i++) { + ErrProcData md = ErrProcData.parse_(lines[i]); + if (md.SourceLine() == 0) break; // ASSUME: java code; not interested + if (String_.HasAtBgn(md.signatureRaw, "gplx.Err_") || String_.HasAtBgn(md.signatureRaw, "gplx.Err.")) continue; // java includes entire stackTrace from point of creation; only care about point of throw + list.Add(md); + } + return (ErrProcData[])list.XtoAry(ErrProcData.class); + } + public static ErrProcData parse_(String raw) { + ErrProcData rv = new ErrProcData().Raw_(raw); + // ex:'gplx.Err.new_(Err.java:92)' + int sigEnd = String_.FindFwd(raw, "("); if (sigEnd == String_.Find_none) return rv; + rv.signatureRaw = String_.Mid(raw, 0, sigEnd); + int filBgn = sigEnd + 1; // 1="(" + int filEnd = String_.FindFwd(raw, ":", filBgn); if (filEnd == String_.Find_none) return rv; + rv.sourceFileRaw = String_.Mid(raw, filBgn, filEnd); + int linBgn = filEnd + 1; // 1=":" + int linEnd = String_.FindFwd(raw, ")", linBgn); if (linEnd == String_.Find_none) return rv; + String linRaw = String_.Mid(raw, linBgn, linEnd); + rv.sourceLine = Int_.parse_(linRaw); + rv.ideAddress = String_.Concat("(", rv.sourceFileRaw, ":", Int_.XtoStr(rv.sourceLine), ")"); + return rv; + } + public static ErrProcData new_() {return new ErrProcData();} ErrProcData() {} + public static final ErrProcData Null = new ErrProcData(); +} diff --git a/100_core/src_000_err/gplx/ErrProcData_tst.java b/100_core/src_000_err/gplx/ErrProcData_tst.java new file mode 100644 index 000000000..39c9ccda1 --- /dev/null +++ b/100_core/src_000_err/gplx/ErrProcData_tst.java @@ -0,0 +1,48 @@ +/* +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; +import org.junit.*; +public class ErrProcData_tst { + @Test public void parse_() { + tst_parse_("gplx._tst.Err__tst.RdrLoad(MethodData_tst.java:1)" + , ErrProcData.new_() + .SignatureRaw_("gplx._tst.Err__tst.RdrLoad") + .SourceFileRaw_("MethodData_tst.java") + .SourceLine_(1) + .IdeAddress_("(MethodData_tst.java:1)") + ); + } + @Test public void parse_ary_() { + String stackTrace = ""; + try {ThrowException();} catch (Exception exc) {stackTrace = Err_.StackTrace_lang(exc);} + ErrProcData[] ary = ErrProcData.parse_ary_(stackTrace); + Tfds.Eq(2, Array_.Len(ary)); + Tfds.Eq("gplx.ErrProcData_tst.ThrowException", ary[0].SignatureRaw()); + Tfds.Eq("gplx.ErrProcData_tst.parse_ary_", ary[1].SignatureRaw()); + } + Exception ThrowException() { + throw new RuntimeException("msg"); + } + void tst_parse_(String raw, ErrProcData expd) { + ErrProcData actl = ErrProcData.parse_(raw); + Tfds.Eq(expd.SignatureRaw(), actl.SignatureRaw()); + Tfds.Eq(expd.SourceFileRaw(), actl.SourceFileRaw()); + Tfds.Eq(expd.SourceLine(), actl.SourceLine()); + Tfds.Eq(expd.IdeAddress(), actl.IdeAddress()); + } +} diff --git a/100_core/src_000_err/gplx/Err_.java b/100_core/src_000_err/gplx/Err_.java new file mode 100644 index 000000000..b6eb05394 --- /dev/null +++ b/100_core/src_000_err/gplx/Err_.java @@ -0,0 +1,84 @@ +/* +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; +public class Err_ { //_20110415 + public static Err as_(Object obj) {return obj instanceof Err ? (Err)obj : null;} + public static Err new_(String fmt, Object... args) {return Err.hdr_(String_.Format(fmt, args));} + public static Err new_fmt_(String fmt, Object... args){return Err.hdr_(String_.Format(fmt, args));} + public static Err new_(String hdr) {return Err.hdr_(hdr);} + public static Err new_key_(String key, String hdr) {return Err.hdr_(hdr).Key_(key);} + public static Err err_key_(Exception exc, String key, String hdr) {return Err.exc_(exc, hdr).Key_(key);} + public static Err err_(Exception exc, String hdr) {return Err.exc_(exc, hdr);} + public static Err err_(Exception e, String fmt, Object... args) {return Err.exc_(e, String_.Format(fmt, args));} + public static Err cast_(Exception ignore, Class t, Object o) { + String o_str = ""; + try {o_str = Object_.XtoStr_OrNullStr(o);} + catch (Exception e) {Err_.Noop(e); o_str = "";} + return cast_manual_msg_(ignore, t, o_str); + } + public static Err cast_manual_msg_(Exception ignore, Class t, String s) { + String msg = String_.Format("cast failed; type={0} obj={1}", ClassAdp_.NameOf_type(t), s); + return new_(msg); + } + public static void FailIfNotFound(int v, String m) {if (v == String_.Find_none) throw find_failed_(m);} + public static Err find_failed_(String find) {return Err.hdr_("find failed").Add("find", find);} + + public static Err null_(String obj) {return Err.hdr_("null obj").Add("obj", obj);} + public static Err not_implemented_() {return not_implemented_msg_("method not implemented");} + public static Err not_implemented_msg_(String hdr) {return Err.hdr_(hdr);} + public static Err type_mismatch_exc_(Exception e, Class t, Object o) {return type_mismatch_(t, o);} // NOTE: e passed to "soak" up variable for IDE + public static Err type_mismatch_(Class t, Object o) { + return Err.hdr_("type mismatch") + .Add("expdType", ClassAdp_.FullNameOf_type(t)) + .Add("actlType", ClassAdp_.NameOf_obj(o)) + .Add("actlObj", Object_.XtoStr_OrNullStr(o)) + ; + } + public static Err missing_idx_(int idx, int len) {return Err.hdr_("index is out of bounds").Add("idx", idx).Add("len", len);} + public static Err missing_key_(String key) {return Err.hdr_("key not found").Add("key", key);} + public static Err unhandled(Object val) {return Err.hdr_("val is not in switch/if").Add("val", val);} + public static Err invalid_op_(String hdr) {return Err.hdr_(hdr);} + public static Err parse_type_(Class c, String raw) {return parse_(ClassAdp_.FullNameOf_type(c), raw);} + public static Err parse_type_exc_(Exception e, Class c, String raw) {return parse_(ClassAdp_.FullNameOf_type(c), raw).Add("e", Err_.Message_lang(e));} + public static Err parse_msg_(String type, String raw, String cause) {return parse_(type, raw).Add("cause", cause);} + public static Err parse_(String typeName, String raw) {return Err.hdr_("parse failed").Add("type", typeName).Add("raw", raw);} + + public static final String op_canceled__const = "gplx.op_canceled"; + public static Err op_canceled_usr_() {return Err_.new_key_(op_canceled__const, "canceled by usr");} + public static Err rethrow_(Exception exc) {return Err_.new_key_("rethrow", Err_.Message_lang(exc));} // NOTE: needed because throw exc does not work in java (exc must be RuntimeException, not Exception) + + public static void Noop(Exception e) {} + public static String Message_lang(Exception e) {return e.getClass() + " " + e.getMessage();} + public static String Message_gplx(Exception e) {return ErrMsgWtr._.Message_gplx(e);} + public static String Message_gplx_brief(Exception e) {return ErrMsgWtr._.Message_gplx_brief(e);} + @gplx.Internal protected static String StackTrace_lang(Exception e) { + String_bldr sb = String_bldr_.new_(); + StackTraceElement[] stackTraceAry = e.getStackTrace(); + int len = stackTraceAry.length; + for (int i = 0; i < len; i++) { + if (i != 0) sb.Add_char_crlf(); + sb.Add(stackTraceAry[i].toString()); + } + return sb.XtoStr(); + } + public static boolean MatchKey(Exception exc, String key) { + Err err = Err_.as_(exc); if (err == null) return false; + String msg = err.Key(); + return String_.Has(msg, key); + } +} diff --git a/100_core/src_000_err/gplx/Err_arg.java b/100_core/src_000_err/gplx/Err_arg.java new file mode 100644 index 000000000..fee3dd571 --- /dev/null +++ b/100_core/src_000_err/gplx/Err_arg.java @@ -0,0 +1,53 @@ +/* +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; +public class Err_arg extends Err { + public String ArgName() {return argName;} private String argName; + public Object ArgValue() {return argValue;} Object argValue; + public static Err_arg null_(String argName) { + Err_arg rv = new Err_arg(); + rv.Key_("gplx.arg").Hdr_("@" + rv.argName + " cannot be null"); + rv.argName = argName; + return rv; + } + public static Err_arg cannotBe_(String msg, String argName, Object argValue) { + Err_arg rv = new Err_arg(); + rv.Key_("gplx.arg"); + rv.Hdr_("val cannot be " + msg); + rv.Add("key", argName); + rv.Add("val", argValue); + return rv; + } + public static Err_arg notFound_key_(String argName, Object argValue) { + Err_arg rv = new Err_arg(); + rv.Key_("gplx.arg").Hdr_("arg not found").Add(argName, argValue); + rv.argName = argName; + rv.argValue = argValue; + return rv; + } + public static Err_arg outOfBounds_(String argName, int i, int count) { + Err_arg rv = new Err_arg(); + rv.Key_("gplx.arg").Hdr_("arg out of bounds").Add("argName", argName).Add("argVal", i).Add("count", count); + rv.argName = argName; + rv.argValue = i; + return rv; + } + public static boolean ClassCheck(Exception e) { + return ClassAdp_.Eq_typeSafe(e, Err_arg.class); + } +} diff --git a/100_core/src_000_err/gplx/Err_mgr.java b/100_core/src_000_err/gplx/Err_mgr.java new file mode 100644 index 000000000..7aa09dcd9 --- /dev/null +++ b/100_core/src_000_err/gplx/Err_mgr.java @@ -0,0 +1,43 @@ +/* +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; +public class Err_mgr { + Err_mgr(Gfo_msg_root msg_root) {this.msg_root = msg_root;} Gfo_msg_root msg_root; + public Err not_implemented_() {return Err_.new_(Msg_not_implemented.Gen_str_none());} + public Err unhandled_(Object obj) {return Err_.new_(Msg_unhandled.Gen_str_one(obj));} + public Err cast_(Exception e, Class obj_class, Object obj) {return Err_.new_(Msg_cast.Gen_str_many(ClassAdp_.NameOf_type(obj_class), Object_.XtoStr_OrNullStr(obj)));} + public Err parse_(Class type , byte[] raw) {return Err_.new_(Msg_parse.Gen_str_many(ClassAdp_.NameOf_type(type), String_.new_utf8_len_safe_(raw, 0, 255)));} + public Err parse_obj_(Object o , byte[] raw) {return Err_.new_(Msg_parse.Gen_str_many(ClassAdp_.NameOf_obj(o), String_.new_utf8_len_safe_(raw, 0, 255)));} + public Err parse_(String type_name, byte[] raw) {return Err_.new_(Msg_parse.Gen_str_many(type_name, String_.new_utf8_len_safe_(raw, 0, 255)));} + public Err parse_(String type_name, String raw) {return Err_.new_(Msg_parse.Gen_str_many(type_name, String_.MidByLenSafe(raw, 0, 255)));} + public Err fmt_auto_(String grp, String key, Object... vals) {return fmt_(grp, key, Bry_fmtr.New_fmt_str(key, vals), vals);} + public Err fmt_(String grp, String key, String fmt, Object... vals) { + Gfo_msg_data data = msg_root.Data_new_many(Gfo_msg_itm_.Cmd_fail, grp, key, fmt, vals); + return Err_.new_(data.Gen_str_ary()); + } + + public static final Err_mgr _ = new Err_mgr(Gfo_msg_root._); + static final Gfo_msg_grp GRP_OBJ = Gfo_msg_grp_.new_(Gfo_msg_grp_.Root_gplx, "Object"); + static final Gfo_msg_itm + Msg_unhandled = Gfo_msg_itm_.new_fail_(GRP_OBJ, "unhandled" , "unhandled value: '~{0}'") + , Msg_cast = Gfo_msg_itm_.new_fail_(GRP_OBJ, "cast" , "cast failed; expd:'~{0}' actl:'~{1}'") + , Msg_generic = Gfo_msg_itm_.new_fail_(GRP_OBJ, "generic" , "generic error; expd:'~{0}' actl:'~{1}'") + , Msg_parse = Gfo_msg_itm_.new_fail_(GRP_OBJ, "parse" , "parse error; type:'~{0}' raw:'~{1}'") + , Msg_not_implemented = Gfo_msg_itm_.new_fail_(GRP_OBJ, "not_implemented" , "not implemented") + ; +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_data.java b/100_core/src_000_err/gplx/Gfo_msg_data.java new file mode 100644 index 000000000..4d1456804 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_data.java @@ -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 . +*/ +package gplx; +public class Gfo_msg_data { + public int Uid() {return uid;} int uid = uid_next++; + public Gfo_msg_itm Item() {return item;} Gfo_msg_itm item; + public Object[] Vals() {return vals;} Object[] vals; + public byte[] Src_bry() {return src_bry;} private byte[] src_bry; + public int Src_bgn() {return src_bgn;} int src_bgn; + public int Src_end() {return src_end;} int src_end; + public Gfo_msg_data Ctor_val_many(Gfo_msg_itm item, Object[] vals) {this.item = item; this.vals = vals; return this;} + public Gfo_msg_data Ctor_src_many(Gfo_msg_itm item, byte[] src_bry, int src_bgn, int src_end, Object[] vals) {this.item = item; this.src_bry = src_bry; this.src_bgn = src_bgn; this.src_end = src_end; this.vals = vals; return this;} + public void Clear() { + item = null; vals = null; src_bry = null; + } + public String Gen_str_ary() {return item.Gen_str_ary(vals);} + static int uid_next = 0; + public static final Gfo_msg_data[] Ary_empty = new Gfo_msg_data[0]; +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_grp.java b/100_core/src_000_err/gplx/Gfo_msg_grp.java new file mode 100644 index 000000000..b6419ff9b --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_grp.java @@ -0,0 +1,46 @@ +/* +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; +public class Gfo_msg_grp implements Gfo_msg_obj { + public Gfo_msg_grp(Gfo_msg_grp owner, int uid, byte[] key) { + this.owner = owner; this.uid = uid; this.key = key; this.key_str = String_.new_ascii_(key); + if (owner != null) { + owner.subs.Add(this); + path = Gfo_msg_grp_.Path(owner.path, key); + } + else + path = Bry_.Empty; + } + public void Subs_clear() {subs.Clear();} + public Gfo_msg_grp Owner() {return owner;} Gfo_msg_grp owner; + public int Uid() {return uid;} int uid; + public byte[] Key() {return key;} private byte[] key; + public String Key_str() {return key_str;} private String key_str; + public byte[] Path() {return path;} private byte[] path; + public String Path_str() {return String_.new_ascii_(path);} + public Gfo_msg_obj Subs_get_by_key(String sub_key) { + int subs_len = subs.Count(); + for (int i = 0; i < subs_len; i++) { + Gfo_msg_obj sub = (Gfo_msg_obj)subs.FetchAt(i); + if (String_.Eq(sub_key, sub.Key_str())) return sub; + } + return null; + } + public void Subs_add(Gfo_msg_itm item) {subs.Add(item);} + ListAdp subs = ListAdp_.new_(); +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_grp_.java b/100_core/src_000_err/gplx/Gfo_msg_grp_.java new file mode 100644 index 000000000..073035750 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_grp_.java @@ -0,0 +1,30 @@ +/* +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; +public class Gfo_msg_grp_ { + public static final Gfo_msg_grp Root_gplx = new Gfo_msg_grp(null, Gfo_msg_grp_.Uid_next(), Bry_.new_ascii_("gplx")); + public static final Gfo_msg_grp Root = new Gfo_msg_grp(null, Gfo_msg_grp_.Uid_next(), Bry_.Empty); + public static Gfo_msg_grp prj_(String key) {return new Gfo_msg_grp(Root , Gfo_msg_grp_.Uid_next(), Bry_.new_ascii_(key));} + public static Gfo_msg_grp new_(Gfo_msg_grp owner, String key) {return new Gfo_msg_grp(owner , Gfo_msg_grp_.Uid_next(), Bry_.new_ascii_(key));} + public static int Uid_next() {return uid_next++;} static int uid_next = 0; + public static byte[] Path(byte[] owner_path, byte[] key) { + if (owner_path != Bry_.Empty) tmp_bfr.Add(owner_path).Add_byte(Byte_ascii.Dot); // only add "." if owner_path is available; prevents creating ".gplx" + return tmp_bfr.Add(key).XtoAryAndClear(); + } + static Bry_bfr tmp_bfr = Bry_bfr.reset_(256); +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_itm.java b/100_core/src_000_err/gplx/Gfo_msg_itm.java new file mode 100644 index 000000000..0fc9e0f07 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_itm.java @@ -0,0 +1,57 @@ +/* +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; +public class Gfo_msg_itm implements Gfo_msg_obj { + public Gfo_msg_itm(Gfo_msg_grp owner, int uid, byte cmd, byte[] key_bry, byte[] fmt, boolean add_to_owner) { + this.owner = owner; this.uid = uid; this.cmd = cmd; this.key_bry = key_bry; this.fmt = fmt; + this.key_str = String_.new_ascii_(key_bry); + this.path_bry = Gfo_msg_grp_.Path(owner.Path(), key_bry); + if (add_to_owner) owner.Subs_add(this); + } + public Gfo_msg_grp Owner() {return owner;} Gfo_msg_grp owner; + public int Uid() {return uid;} int uid; + public byte[] Path_bry() {return path_bry;} private byte[] path_bry; + public String Path_str() {return String_.new_utf8_(path_bry);} + public byte[] Key_bry() {return key_bry;} private byte[] key_bry; + public String Key_str() {return key_str;} private String key_str; + public Gfo_msg_obj Subs_get_by_key(String sub_key) {return null;} + public byte Cmd() {return cmd;} private byte cmd; + public byte[] Fmt() {return fmt;} private byte[] fmt; + public Bry_fmtr Fmtr() {if (fmtr == null) fmtr = Bry_fmtr.new_bry_(fmt).Compile(); return fmtr;} Bry_fmtr fmtr; + public String Gen_str_many(Object... vals) {return Gen_str_ary(vals);} + public String Gen_str_ary(Object[] vals) { + if (fmtr == null) fmtr = Bry_fmtr.new_bry_(fmt).Compile(); + if (fmtr.Fmt_args_exist()) { + fmtr.Bld_bfr_many(tmp_bfr, vals); + return tmp_bfr.XtoStrAndClear(); + } + else + return String_.new_utf8_(fmt); + } + public String Gen_str_one(Object val) { + if (fmtr == null) fmtr = Bry_fmtr.new_bry_(fmt).Compile(); + if (fmtr.Fmt_args_exist()) { + fmtr.Bld_bfr_one(tmp_bfr, val); + return tmp_bfr.XtoStrAndClear(); + } + else + return String_.new_utf8_(fmt); + } + public String Gen_str_none() {return key_str;} + static Bry_bfr tmp_bfr = Bry_bfr.reset_(255); +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_itm_.java b/100_core/src_000_err/gplx/Gfo_msg_itm_.java new file mode 100644 index 000000000..d7cc728a7 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_itm_.java @@ -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 . +*/ +package gplx; +public class Gfo_msg_itm_ { + public static final byte Cmd_null = 0, Cmd_log = 1, Cmd_note = 2, Cmd_warn = 3, Cmd_stop = 4, Cmd_fail = 5; + public static final byte[][] CmdBry = new byte[][] {Bry_.new_ascii_("null"), Bry_.new_ascii_("log"), Bry_.new_ascii_("note"), Bry_.new_ascii_("warn"), Bry_.new_ascii_("stop"), Bry_.new_ascii_("fail")}; + public static Gfo_msg_itm new_note_(Gfo_msg_grp owner, String key) {return new_(owner, Cmd_note, key, key);} + public static Gfo_msg_itm new_fail_(Gfo_msg_grp owner, String key, String fmt) {return new_(owner, Cmd_warn, key, fmt);} + public static Gfo_msg_itm new_warn_(Gfo_msg_grp owner, String key) {return new_(owner, Cmd_warn, key, key);} + public static Gfo_msg_itm new_warn_(Gfo_msg_grp owner, String key, String fmt) {return new_(owner, Cmd_warn, key, fmt);} + public static Gfo_msg_itm new_(Gfo_msg_grp owner, byte cmd, String key, String fmt) {return new Gfo_msg_itm(owner, Gfo_msg_grp_.Uid_next(), cmd, Bry_.new_ascii_(key), Bry_.new_ascii_(fmt), false);} +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_log.java b/100_core/src_000_err/gplx/Gfo_msg_log.java new file mode 100644 index 000000000..112deb916 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_log.java @@ -0,0 +1,54 @@ +/* +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; +public class Gfo_msg_log { + public Gfo_msg_log(String root_key) {root = new Gfo_msg_root(root_key);} Gfo_msg_root root; + public int Ary_len() {return ary_idx;} + public Gfo_msg_data Ary_get(int i) {return ary[i];} + public Gfo_msg_log Clear() { + for (int i = 0; i < ary_idx; i++) + ary[i].Clear(); + ary_idx = 0; + return this; + } + public Gfo_msg_log Add_str_warn_key_none(String grp, String itm, byte[] src, int pos) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, null, src, pos, pos + 1, null);} + public Gfo_msg_log Add_str_warn_key_none(String grp, String itm, byte[] src, int bgn, int end) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, null, src, bgn, end, null);} + public Gfo_msg_log Add_str_warn_fmt_none(String grp, String itm, String fmt) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , Bry_.Empty, -1, -1, null);} + public Gfo_msg_log Add_str_warn_fmt_none(String grp, String itm, String fmt, byte[] src, int pos) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , src, pos, pos + 1, null);} + public Gfo_msg_log Add_str_warn_fmt_none(String grp, String itm, String fmt, byte[] src, int bgn, int end) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , src, bgn, end, null);} + public Gfo_msg_log Add_str_warn_fmt_many(String grp, String itm, String fmt, Object... vals) {return Add_str(Gfo_msg_itm_.Cmd_warn, grp, itm, fmt , Bry_.Empty, -1, -1, vals);} + Gfo_msg_log Add_str(byte cmd, String owner_key, String itm, String fmt, byte[] src, int bgn, int end, Object[] vals) { + if (ary_idx >= ary_max) ary_expand(); + ary[ary_idx++] = root.Data_new_many(cmd, src, bgn, end, owner_key, itm, fmt, vals); + return this; + } + public Gfo_msg_log Add_itm_none(Gfo_msg_itm itm, byte[] src, int bgn, int end) {return Add_itm(itm, src, bgn, end, null);} + public Gfo_msg_log Add_itm_many(Gfo_msg_itm itm, byte[] src, int bgn, int end, Object... val_ary) {return Add_itm(itm, src, bgn, end, val_ary);} + Gfo_msg_log Add_itm(Gfo_msg_itm itm, byte[] src, int bgn, int end, Object[] vals) { + if (ary_idx >= ary_max) ary_expand(); + ary[ary_idx++] = root.Data_new_many(itm, src, bgn, end, vals); + return this; + } + void ary_expand() { + int new_max = ary_max == 0 ? 2 : ary_max * 2; + ary = (Gfo_msg_data[])Array_.Expand(ary, new Gfo_msg_data[new_max], ary_max); + ary_max = new_max; + } + Gfo_msg_data[] ary = Gfo_msg_data.Ary_empty; int ary_idx, ary_max; + public static Gfo_msg_log Test() {return new Gfo_msg_log("test");} +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_obj.java b/100_core/src_000_err/gplx/Gfo_msg_obj.java new file mode 100644 index 000000000..0e166b958 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_obj.java @@ -0,0 +1,22 @@ +/* +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; +public interface Gfo_msg_obj { + String Key_str(); + Gfo_msg_obj Subs_get_by_key(String sub_key); +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_root.java b/100_core/src_000_err/gplx/Gfo_msg_root.java new file mode 100644 index 000000000..6e3871b33 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_root.java @@ -0,0 +1,80 @@ +/* +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; +public class Gfo_msg_root { + public Gfo_msg_root(String root_key) { + this.root_key = root_key; + this.root = Gfo_msg_grp_.new_(Gfo_msg_grp_.Root, root_key); + } String root_key; + public void Data_ary_clear() { + for (int i = 0; i < data_ary_idx; i++) + data_ary[i].Clear(); + data_ary_idx = 0; + } + public void Data_ary_len_(int v) { + data_ary_len = v; + data_ary = new Gfo_msg_data[data_ary_len]; + for (int i = 0; i < data_ary_len; i++) + data_ary[i] = new Gfo_msg_data(); + data_ary_idx = 0; + } int data_ary_len; int data_ary_idx; Gfo_msg_data[] data_ary; + public void Reset() { + root.Subs_clear(); + owners.Clear(); + uid_list_next = uid_item_next = 0; + Data_ary_clear(); + } + public Gfo_msg_data Data_new_note_many(String owner_key, String key, String fmt, Object... vals) {return Data_new_many(Gfo_msg_itm_.Cmd_note, Bry_.Empty, -1, -1, owner_key, key, fmt, vals);} + public Gfo_msg_data Data_new_many(byte cmd, String owner_key, String key, String fmt, Object[] vals) {return Data_new_many(cmd, Bry_.Empty, -1, -1, owner_key, key, fmt, vals);} + public Gfo_msg_data Data_new_many(byte cmd, byte[] src, int bgn, int end, String owner_key, String key, String fmt, Object[] vals) { + Object owner_obj = owners.Fetch(owner_key); + Gfo_msg_grp owner = null; + if (owner_obj == null) { + owner = New_list_by_key(owner_key); + owners.Add(owner_key, owner); + } + else + owner = (Gfo_msg_grp)owner_obj; + Gfo_msg_itm itm = (Gfo_msg_itm)owner.Subs_get_by_key(key); + if (itm == null) + itm = new Gfo_msg_itm(owner, uid_item_next++, cmd, Bry_.new_utf8_(key), fmt == null ? Bry_.Empty : Bry_.new_ascii_(fmt), false); + return Data_new_many(itm, src, bgn, end, vals); + } + public Gfo_msg_data Data_new_many(Gfo_msg_itm itm, byte[] src, int bgn, int end, Object... vals) {return Data_get().Ctor_src_many(itm, src, bgn, end, vals);} + public Gfo_msg_data Data_get() { + return data_ary_idx < data_ary_len ? data_ary[data_ary_idx++] : new Gfo_msg_data(); + } + Gfo_msg_grp New_list_by_key(String key) { + String[] segs = String_.Split(key, '.'); + int segs_len = segs.length; int segs_last = segs_len - 1; + Gfo_msg_grp cur_list = root; + for (int i = 0; i < segs_last; i++) { + String seg = segs[i]; + Gfo_msg_grp sub_list = (Gfo_msg_grp)cur_list.Subs_get_by_key(seg); + if (sub_list == null) + sub_list = new Gfo_msg_grp(cur_list, uid_list_next++, Bry_.new_ascii_(key)); + cur_list = sub_list; + } + return cur_list; + } + Gfo_msg_grp root; + OrderedHash owners = OrderedHash_.new_(); + int uid_list_next = 0; + int uid_item_next = 0; + public static final Gfo_msg_root _ = new Gfo_msg_root("gplx"); +} diff --git a/100_core/src_000_err/gplx/Gfo_msg_root_tst.java b/100_core/src_000_err/gplx/Gfo_msg_root_tst.java new file mode 100644 index 000000000..4e0b9bd77 --- /dev/null +++ b/100_core/src_000_err/gplx/Gfo_msg_root_tst.java @@ -0,0 +1,62 @@ +/* +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; +import org.junit.*; +public class Gfo_msg_root_tst { + Gfo_msg_root_fxt fxt = new Gfo_msg_root_fxt(); + @Before public void setup() {fxt.Reset();} + @Test public void Str() { + fxt.Clear().Expd_data_str_("failed a0 b0").Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a0", "b0"); + fxt.Clear().Expd_data_str_("failed a1 b1").Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a1", "b1"); + } +// @Test public void Item() { // DISABLED: no longer registering items with owner; +// fxt.Clear().Expd_item_uid_(0).Expd_item_fmtr_arg_exists_(Bool_.Y).Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a0", "b0"); +// fxt.Clear().Expd_item_uid_(1).Expd_item_fmtr_arg_exists_(Bool_.N).Tst_data_new_many("proj.cls.proc", "err_1", "failed"); +// fxt.Clear().Expd_item_uid_(0).Tst_data_new_many("proj.cls.proc", "err_0", "failed ~{0} ~{1}", "a0", "b0"); // make sure item_uid stays the same +// } + @Test public void Cache() { + fxt.Mgr().Data_ary_len_(2); + fxt.Clear().Expd_data_uid_(0).Tst_data_new_many("x", "err_0", "a"); + fxt.Clear().Expd_data_uid_(1).Tst_data_new_many("x", "err_0", "b"); + fxt.Clear().Expd_data_uid_(2).Tst_data_new_many("x", "err_0", "a"); + fxt.Mgr().Data_ary_clear(); + fxt.Clear().Expd_data_uid_(0).Tst_data_new_many("x", "err_0", "a"); + } +} +class Gfo_msg_root_fxt { + Gfo_msg_root root = new Gfo_msg_root("tst"); + public Gfo_msg_root_fxt Reset() {root.Reset(); this.Clear(); return this;} + public Gfo_msg_root_fxt Clear() { + expd_item_uid = -1; + expd_item_fmtr_arg_exists = Bool_.__byte; + expd_data_uid = -1; + expd_data_str = null; + return this; + } + public Gfo_msg_root Mgr() {return root;} + public Gfo_msg_root_fxt Expd_data_uid_(int v) {this.expd_data_uid = v; return this;} int expd_data_uid; + public Gfo_msg_root_fxt Expd_data_str_(String v) {this.expd_data_str = v; return this;} private String expd_data_str; + public Gfo_msg_root_fxt Expd_item_uid_(int v) {this.expd_item_uid = v; return this;} int expd_item_uid; + public Gfo_msg_root_fxt Expd_item_fmtr_arg_exists_(boolean v) {this.expd_item_fmtr_arg_exists = v ? Bool_.Y_byte : Bool_.N_byte; return this;} private byte expd_item_fmtr_arg_exists; + public void Tst_data_new_many(String path, String key, String fmt, Object... vals) { + Gfo_msg_data data = root.Data_new_many(Gfo_msg_itm_.Cmd_note, path, key, fmt, vals); + if (expd_item_uid != -1) Tfds.Eq(expd_item_uid, data.Item().Uid());; + if (expd_item_fmtr_arg_exists != Bool_.__byte) Tfds.Eq(Bool_.int_(expd_item_fmtr_arg_exists), data.Item().Fmtr().Fmt_args_exist()); + if (expd_data_str != null) Tfds.Eq(expd_data_str, data.Item().Gen_str_many(data.Vals())); + } +} diff --git a/100_core/src_100_interface/gplx/Cancelable.java b/100_core/src_100_interface/gplx/Cancelable.java new file mode 100644 index 000000000..6d32f5e09 --- /dev/null +++ b/100_core/src_100_interface/gplx/Cancelable.java @@ -0,0 +1,23 @@ +/* +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; +public interface Cancelable { + boolean Canceled(); + void Cancel(); + void Cancel_reset(); +} diff --git a/100_core/src_100_interface/gplx/Cancelable_.java b/100_core/src_100_interface/gplx/Cancelable_.java new file mode 100644 index 000000000..dc1808aac --- /dev/null +++ b/100_core/src_100_interface/gplx/Cancelable_.java @@ -0,0 +1,26 @@ +/* +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; +public class Cancelable_ { + public static final Cancelable Never = new Cancelable_never(); +} +class Cancelable_never implements Cancelable { + public boolean Canceled() {return false;} + public void Cancel() {} + public void Cancel_reset() {} +} diff --git a/100_core/src_100_interface/gplx/CompareAble.java b/100_core/src_100_interface/gplx/CompareAble.java new file mode 100644 index 000000000..5e8bcb7ef --- /dev/null +++ b/100_core/src_100_interface/gplx/CompareAble.java @@ -0,0 +1,20 @@ +/* +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; +public interface CompareAble extends Comparable {} // URL:/doc/gplx/CompareAble_.txt +// public int compareTo(Object obj) {Type comp = (Type)obj; return prop.compareTo(comp.prop);} diff --git a/100_core/src_100_interface/gplx/CompareAble_.java b/100_core/src_100_interface/gplx/CompareAble_.java new file mode 100644 index 000000000..cab1638c2 --- /dev/null +++ b/100_core/src_100_interface/gplx/CompareAble_.java @@ -0,0 +1,78 @@ +/* +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; +import gplx.lists.*; +public class CompareAble_ { + public static Comparable as_(Object obj) {return obj instanceof Comparable ? (Comparable)obj : null;} + public static int Compare_obj(Object lhs, Object rhs) {return CompareComparables(as_(lhs), as_(rhs));} + public static int CompareComparables(Comparable lhs, Comparable rhs) { + if (lhs == null && rhs == null) return CompareAble_.Same; + else if (lhs == null) return CompareAble_.More; + else if (rhs == null) return CompareAble_.Less; + else return Compare(lhs, rhs); + } + + public static boolean Is_more(Comparable lhs, Comparable rhs) {return Is(More, lhs, rhs);} + public static boolean Is_moreOrSame(Comparable lhs, Comparable rhs) {return Is(MoreOrSame, lhs, rhs);} + public static boolean Is_less(Comparable lhs, Comparable rhs) {return Is(Less, lhs, rhs);} + public static boolean Is_lessOrSame(Comparable lhs, Comparable rhs) {return Is(LessOrSame, lhs, rhs);} + public static boolean Is_same(Comparable lhs, Comparable rhs) {return Is(Same, lhs, rhs);} + public static boolean Is(int expt, Comparable lhs, Comparable rhs) { + int actl = CompareComparables(lhs, rhs); + if (actl == Same && expt % 2 == Same) // actl=Same and expt=(Same||MoreOrSame||LessOrSame) + return true; + else + return (actl * expt) > 0; // actl=More||Less; expd will match if on same side of 0 (ex: expt=Less; actl=Less; -1 * -1 = 1) + } +// public static int FindSlot(ComparerAble comparer, Object[] ary, Object itm) {return FindSlot(comparer, ary, itm, false);} + public static int FindSlot(ComparerAble comparer, Object[] ary, Object itm) {if (itm == null) throw Err_.null_("itm is null"); + int aryLen = ary.length; + switch (aryLen) { + case 0: throw Err_.new_("ary cannot have 0 itms"); + case 1: return 0; + } + int lo = -1, hi = aryLen - 1; // NOTE: -1 is necessary; see test + int curPos = (hi - lo) / 2; + int delta = 1; + while (true) { + Object curSeg = ary[curPos]; + int comp = curSeg == null ? CompareAble_.More : comparer.compare(itm, curSeg); // nulls should only happen for lastAry +// if (dbg) { +// Tfds.Write(curPos, itm.toString(), comp, comp.toString(), curSeg.toString()); +// } + if (comp == CompareAble_.Same) return curPos; + else if (comp > CompareAble_.Same) {lo = curPos; delta = 1;} + else if (comp < CompareAble_.Same) {hi = curPos; delta = -1;} + int dif = hi - lo; + if (dif == 1 || dif == 0) return hi; // NOTE: can be 0 when ary.length == 1 || 2; also, sometimes 0 in some situations + else curPos += (dif / 2) * delta; + } + } + public static int Compare(Comparable lhs, Comparable rhs) {return lhs.compareTo(rhs);} + + public static final int + More = 1 + , Less = -1 + , Same = 0 + , MoreOrSame = 2 + , LessOrSame = -2 + , ReverseMult = -1 + , OffsetCompare = 1 // handle srcPos >= 1 -> srcPosChk > 0 + ; + public static int Multiplier(boolean v) {return v ? 1 : -1;} +} diff --git a/100_core/src_100_interface/gplx/CompareAble_tst.java b/100_core/src_100_interface/gplx/CompareAble_tst.java new file mode 100644 index 000000000..b167ca0a8 --- /dev/null +++ b/100_core/src_100_interface/gplx/CompareAble_tst.java @@ -0,0 +1,38 @@ +/* +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; +import org.junit.*; +import gplx.lists.*; +public class CompareAble_tst implements ComparerAble { + @Test public void Basic() { + String[] slotAry = new String[] {"b", "e", "h"}; // 0=b 1=e 2=h + tst_FindSlot(slotAry, "f", "h"); // f -> 1 2 -> 2 + tst_FindSlot(slotAry, "c", "e"); // c -> -1 1 -> 0 -> 0 1 -> 1 + tst_FindSlot(slotAry, "a", "b"); // a -> -1 1 -> 0 -> -1 0 -> 0 + } + @Test public void Null() { + String[] slotAry = new String[] {"b", "g", "l", "q", "v", null}; + tst_FindSlot(slotAry, "a", "b"); + tst_FindSlot(slotAry, "b", "b"); + tst_FindSlot(slotAry, "c", "g"); + tst_FindSlot(slotAry, "v", "v"); + tst_FindSlot(slotAry, "w", null); + } + public int compare(Object lhsObj, Object rhsObj) {return CompareAble_.Compare_obj(lhsObj, rhsObj);} + void tst_FindSlot(String[] slotAry, String s, String expd) {Tfds.Eq(expd, slotAry[CompareAble_.FindSlot(this, slotAry, s)]);} +} diff --git a/100_core/src_100_interface/gplx/EqAble.java b/100_core/src_100_interface/gplx/EqAble.java new file mode 100644 index 000000000..c23db7e63 --- /dev/null +++ b/100_core/src_100_interface/gplx/EqAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface EqAble { + boolean Eq(Object comp); +} diff --git a/100_core/src_100_interface/gplx/InjectAble.java b/100_core/src_100_interface/gplx/InjectAble.java new file mode 100644 index 000000000..309fca2f9 --- /dev/null +++ b/100_core/src_100_interface/gplx/InjectAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface InjectAble { + void Inject(Object owner); +} diff --git a/100_core/src_100_interface/gplx/NewAble.java b/100_core/src_100_interface/gplx/NewAble.java new file mode 100644 index 000000000..24178b1b7 --- /dev/null +++ b/100_core/src_100_interface/gplx/NewAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface NewAble { + Object NewByKey(Object o); +} diff --git a/100_core/src_100_interface/gplx/ParseAble.java b/100_core/src_100_interface/gplx/ParseAble.java new file mode 100644 index 000000000..8ab6433da --- /dev/null +++ b/100_core/src_100_interface/gplx/ParseAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface ParseAble { + Object ParseAsObj(String raw); +} diff --git a/100_core/src_100_interface/gplx/ParseAble_.java b/100_core/src_100_interface/gplx/ParseAble_.java new file mode 100644 index 000000000..d997313e4 --- /dev/null +++ b/100_core/src_100_interface/gplx/ParseAble_.java @@ -0,0 +1,21 @@ +/* +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; +public class ParseAble_ { + public static final ParseAble Null = null; +} diff --git a/100_core/src_100_interface/gplx/RlsAble.java b/100_core/src_100_interface/gplx/RlsAble.java new file mode 100644 index 000000000..e876c2476 --- /dev/null +++ b/100_core/src_100_interface/gplx/RlsAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface RlsAble { + void Rls(); +} diff --git a/100_core/src_100_interface/gplx/RlsAble_.java b/100_core/src_100_interface/gplx/RlsAble_.java new file mode 100644 index 000000000..07820e9f9 --- /dev/null +++ b/100_core/src_100_interface/gplx/RlsAble_.java @@ -0,0 +1,22 @@ +/* +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; +public class RlsAble_ { + public static RlsAble as_(Object obj) {return obj instanceof RlsAble ? (RlsAble)obj : null;} + public static RlsAble cast_(Object obj) {try {return (RlsAble)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, RlsAble.class, obj);}} +} diff --git a/100_core/src_100_interface/gplx/SrlAble.java b/100_core/src_100_interface/gplx/SrlAble.java new file mode 100644 index 000000000..52709f302 --- /dev/null +++ b/100_core/src_100_interface/gplx/SrlAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface SrlAble { + Object Srl(GfoMsg owner); +} diff --git a/100_core/src_100_interface/gplx/SrlAble_.java b/100_core/src_100_interface/gplx/SrlAble_.java new file mode 100644 index 000000000..60afba846 --- /dev/null +++ b/100_core/src_100_interface/gplx/SrlAble_.java @@ -0,0 +1,64 @@ +/* +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; +public class SrlAble_ { + public static SrlAble as_(Object obj) {return obj instanceof SrlAble ? (SrlAble)obj : null;} + public static String XtoStr(GfoMsg owner) { + String_bldr sb = String_bldr_.new_(); + XtoStr(owner, sb, 0, false); + return sb.XtoStr(); + } + public static String XtoStr(Object o) { + SrlAble s = SrlAble_.as_(o); if (s == null) return Object_.XtoStr_OrNullStr(o); + GfoMsg m = GfoMsg_.new_parse_("root"); + s.Srl(m); + return XtoStr(m); + } + static void XtoStr(GfoMsg owner, String_bldr sb, int depth, boolean indentOn) { + String indent = String_.Repeat(" ", depth * 4); + if (indentOn) sb.Add(indent); + sb.Add(owner.Key()).Add(":"); + for (int i = 0; i < owner.Args_count(); i++) { + if (i != 0) sb.Add(" "); + KeyVal kv = owner.Args_getAt(i); + sb.Add(kv.Key()).Add("=").Add("'").Add(Object_.XtoStr_OrNullStr(kv.Val())).Add("'"); + } + int subsCount = owner.Subs_count(); + if (subsCount == 0) { + sb.Add(";"); + return; + } + else if (subsCount == 1) { + sb.Add("{"); + XtoStr(owner.Subs_getAt(0), sb, depth + 1, false); + sb.Add("}"); + return; + } + else { + sb.Add("{"); + if (subsCount > 1) sb.Add_char_crlf(); + for (int i = 0; i < subsCount; i++) { + GfoMsg sub = owner.Subs_getAt(i); + XtoStr(sub, sb, depth + 1, true); + sb.Add_char_crlf(); + } + sb.Add(indent); + sb.Add("}"); + } + } +} diff --git a/100_core/src_100_interface/gplx/SrlAble__tst.java b/100_core/src_100_interface/gplx/SrlAble__tst.java new file mode 100644 index 000000000..8b7bbd7a2 --- /dev/null +++ b/100_core/src_100_interface/gplx/SrlAble__tst.java @@ -0,0 +1,66 @@ +/* +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; +import org.junit.*; +public class SrlAble__tst { + @Test public void Basic() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1) + , "itm:key='a' val='1';" + ); + + } + @Test public void Depth1_1() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1).Subs_ + ( GfoMsg_.new_cast_("itm").Add("key", "aa").Add("val", 11) + ) + , String_.Concat_lines_crlf_skipLast + ( "itm:key='a' val='1'{itm:key='aa' val='11';}" + ) + ); + } + @Test public void Depth1_2() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1).Subs_ + ( GfoMsg_.new_cast_("itm").Add("key", "aa").Add("val", 11) + , GfoMsg_.new_cast_("itm").Add("key", "ab").Add("val", 12) + ) + , String_.Concat_lines_crlf_skipLast + ( "itm:key='a' val='1'{" + , " itm:key='aa' val='11';" + , " itm:key='ab' val='12';" + , "}" + ) + ); + } + @Test public void Depth1_1_2() { + tst_Srl_ + ( GfoMsg_.new_cast_("itm").Add("key", "a").Add("val", 1).Subs_ + ( GfoMsg_.new_cast_("itm").Add("key", "aa").Add("val", 11).Subs_( + GfoMsg_.new_cast_("itm").Add("key", "aab").Add("val", 112) + ) + ) + , String_.Concat_lines_crlf_skipLast + ( "itm:key='a' val='1'{itm:key='aa' val='11'{itm:key='aab' val='112';}}" + ) + ); + } + void tst_Srl_(GfoMsg m, String expd) {Tfds.Eq(expd, SrlAble_.XtoStr(m));} +} +//class SrlAble__tst diff --git a/100_core/src_100_interface/gplx/XtoStrAble.java b/100_core/src_100_interface/gplx/XtoStrAble.java new file mode 100644 index 000000000..e65d978f1 --- /dev/null +++ b/100_core/src_100_interface/gplx/XtoStrAble.java @@ -0,0 +1,21 @@ +/* +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; +public interface XtoStrAble { + String XtoStr(); +} diff --git a/100_core/src_100_interface/gplx/XtoStrAble_.java b/100_core/src_100_interface/gplx/XtoStrAble_.java new file mode 100644 index 000000000..755b274bf --- /dev/null +++ b/100_core/src_100_interface/gplx/XtoStrAble_.java @@ -0,0 +1,21 @@ +/* +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; +public class XtoStrAble_ { + public static XtoStrAble as_(Object obj) {return obj instanceof XtoStrAble ? (XtoStrAble)obj : null;} +} diff --git a/100_core/src_110_primitive/gplx/Array_.java b/100_core/src_110_primitive/gplx/Array_.java new file mode 100644 index 000000000..41e1e2bdd --- /dev/null +++ b/100_core/src_110_primitive/gplx/Array_.java @@ -0,0 +1,95 @@ +/* +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; +import java.lang.reflect.Array; +public class Array_ { + public static void Sort(Object[] obj) {ListAdp_Sorter.new_().Sort(obj, obj.length);} + public static void Sort(Object[] obj, gplx.lists.ComparerAble comparer) {ListAdp_Sorter.new_().Sort(obj, obj.length, true, comparer);} + public static ListAdp XtoList(Object ary) { + int aryLen = Array_.Len(ary); + ListAdp rv = ListAdp_.new_(); + for (int i = 0; i < aryLen; i++) + rv.Add(Array_.Get(ary, i)); + return rv; + } + public static Object[] Insert(Object[] cur, Object[] add, int addPos) { + int curLen = cur.length, addLen = add.length; + Object[] rv = (Object[])Array_.Create(Array_.ComponentType(cur), curLen + addLen); + for (int i = 0; i < addPos; i++) // copy old up to addPos + rv[i] = cur[i]; + for (int i = 0; i < addLen; i++) // insert add + rv[i + addPos] = add[i]; + for (int i = addPos; i < curLen; i++) // copy old after addPos + rv[i + addLen] = cur[i]; + return rv; + } + public static Object[] ReplaceInsert(Object[] cur, Object[] add, int curReplacePos, int addInsertPos) { + int curLen = cur.length, addLen = add.length; int newLen = addLen - addInsertPos; + Object[] rv = (Object[])Array_.Create(Array_.ComponentType(cur), curLen + newLen); + for (int i = 0; i < curReplacePos; i++) // copy old up to curInsertPos; EX: curReplacePos=5, addInsertPos=2; copy up to element 3; 4, 5 are dropped + rv[i] = cur[i]; + for (int i = 0; i < addLen; i++) // insert add + rv[i + curReplacePos] = add[i]; + for (int i = curReplacePos + addInsertPos; i < curLen; i++) // copy old after curReplacePos + rv[i + newLen] = cur[i]; +// tst_ReplaceInsert(ary_obj(0, 1, 4, 5) , ary_obj(1, 2, 3), 1, 1, ary_obj(0, 1, 2, 3, 4, 5)); +// tst_ReplaceInsert(ary_obj(0, 1, 2, 4, 5), ary_obj(1, 2, 3), 1, 2, ary_obj(0, 1, 2, 3, 4, 5));//3,4 -> 4,5 + return rv; + } + public static Object Resize(Object src, int trgLen) { + Object trg = Create(ComponentType(src), trgLen); + int srcLen = Array.getLength(src); + int copyLen = srcLen > trgLen ? trgLen : srcLen; // trgLen can either expand or shrink + CopyTo(src, 0, trg, 0, copyLen); + return trg; + } + public static String XtoStr(Object ary) { + String_bldr sb = String_bldr_.new_(); + int ary_len = Len(ary); + for (int i = 0; i < ary_len; i++) + sb.Add_obj(Get(ary, i)).Add_char_nl(); + return sb.XtoStr(); + } + public static int Len(Object ary) {return Array.getLength(ary);} + public static final int LenAry(Object[] ary) {return ary == null ? 0 : ary.length;} + public static Object FetchAt(Object ary, int i) {return Array.get(ary, i); } + public static Object Create(Class t, int count) {return Array.newInstance(t, count);} + public static Object Get(Object ary, int i) {return Array.get(ary, i);} + public static void Set(Object ary, int i, Object o) {Array.set(ary, i, o);} + public static Object Expand(Object src, Object trg, int src_len) { + try {System.arraycopy(src, 0, trg, 0, src_len);} + catch (Exception e) {throw Err_.err_(e, "Array_.Expand failed").Add("src_len", src_len);} + return trg; + } + public static void Copy(Object src, Object trg) {System.arraycopy(src, 0, trg, 0, Len(src));} + public static void CopyTo(Object src, Object trg, int trgPos) {System.arraycopy(src, 0, trg, trgPos, Len(src));} + public static void CopyTo(Object src, int srcBgn, Object trg, int trgBgn, int srcLen) {System.arraycopy(src, srcBgn, trg, trgBgn, srcLen);} + public static Class ComponentType(Object ary) { + if (ary == null) throw Err_.null_("ary"); + return ary.getClass().getComponentType(); + } + public static Object Resize_add(Object src, Object add) { + int srcLen = Len(src); + int trgLen = srcLen + Len(add); + Object trg = Create(ComponentType(src), trgLen); + Copy(src, trg); + for (int i = srcLen; i < trgLen; i++) + Set(trg, i, Get(add, i - srcLen)); + return trg; + } + } diff --git a/100_core/src_110_primitive/gplx/Array__tst.java b/100_core/src_110_primitive/gplx/Array__tst.java new file mode 100644 index 000000000..7766ccf86 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Array__tst.java @@ -0,0 +1,41 @@ +/* +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; +import org.junit.*; +public class Array__tst { + @Test public void Resize_add() { + tst_Resize_add(ary_(), ary_(1), ary_(1)); // 0 + 1 = 1 + tst_Resize_add(ary_(0), ary_(), ary_(0)); // 1 + 0 = 1 + tst_Resize_add(ary_(0), ary_(1), ary_(0, 1)); // 1 + 1 = 2 + } void tst_Resize_add(int[] source, int[] added, int[] expd) {Tfds.Eq_ary(expd, (int[])Array_.Resize_add(source, added));} + @Test public void Resize() { + tst_Resize(ary_(0), 0, ary_()); // 1 -> 0 + tst_Resize(ary_(0, 1), 1, ary_(0)); // 2 -> 1 + } void tst_Resize(int[] source, int length, int[] expd) {Tfds.Eq_ary(expd, (int[])Array_.Resize(source, length));} + @Test public void Insert() { + tst_Insert(ary_obj(0, 1, 4, 5), ary_obj(2, 3), 2, ary_obj(0, 1, 2, 3, 4, 5)); + } void tst_Insert(Object[] cur, Object[] add, int addPos, Object[] expd) {Tfds.Eq_ary(expd, Array_.Insert(cur, add, addPos));} + @Test public void ReplaceInsert() { + tst_ReplaceInsert(ary_obj(0, 1, 4, 5) , ary_obj(1, 2, 3), 1, 1, ary_obj(0, 1, 2, 3, 4, 5)); + tst_ReplaceInsert(ary_obj(0, 1, 2, 4, 5) , ary_obj(1, 2, 3), 1, 2, ary_obj(0, 1, 2, 3, 4, 5)); + tst_ReplaceInsert(ary_obj(0, 1, 2, 3, 4, 5) , ary_obj(1, 2, 3), 1, 3, ary_obj(0, 1, 2, 3, 4, 5)); + tst_ReplaceInsert(ary_obj(0, 1, 9, 4, 5) , ary_obj(2, 3) , 2, 1, ary_obj(0, 1, 2, 3, 4, 5)); + } void tst_ReplaceInsert(Object[] cur, Object[] add, int curReplacePos, int addInsertPos, Object[] expd) {Tfds.Eq_ary(expd, Array_.ReplaceInsert(cur, add, curReplacePos, addInsertPos));} + Object[] ary_obj(Object... ary) {return ary;} + int[] ary_(int... ary) {return ary;} +} diff --git a/100_core/src_110_primitive/gplx/Bool_.java b/100_core/src_110_primitive/gplx/Bool_.java new file mode 100644 index 000000000..348d59237 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bool_.java @@ -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 . +*/ +package gplx; +public class Bool_ implements GfoInvkAble { + public static final boolean[] Ary_empty = new boolean[0]; + public static final Class ClassOf = Boolean.class; + public static int XtoInt(boolean v) {return v ? 1 : 0;} + public static String XtoStr_lower(boolean v) {return v ? "true" : "false";} + public static byte Xto_byte(boolean v) {return v ? Y_byte : N_byte;} + public static boolean cast_(Object obj) {try {return (Boolean)obj;} catch (Exception e) {throw Err_.type_mismatch_exc_(e, boolean.class, obj);}} + public static boolean cast_or_(Object obj, boolean v) {try {return (Boolean)obj;} catch (Exception e) {Err_.Noop(e); return v;}} + public static boolean int_(int v) {return v != 0;} + public static boolean TypeMatch(Class type) {return type == boolean.class || type == Boolean.class;} + public static boolean parse_(String raw) { + if ( String_.Eq(raw, "true") + || String_.Eq(raw, "True") // needed for Store_Wtr(){boolVal.toString();} + ) + return true; + else if ( String_.Eq(raw, "false") + || String_.Eq(raw, "False") + ) + return false; + throw Err_.parse_type_(boolean.class, raw); + } + public static final int N_int = 0, Y_int = 1, __int = -1; + public static final byte N_byte = 0, Y_byte = 1, __byte = 127; + public static final boolean N = false, Y = true; + public static final String N_str = "n", Y_str = "y"; + public static final byte[] True_bry = Bry_.new_ascii_("true"), False_bry = Bry_.new_ascii_("false"); + public static final byte[] Y_bry = new byte[] {Byte_ascii.Ltr_y}, N_bry = new byte[] {Byte_ascii.Ltr_n}; + public static final Bool_ Gfs = new Bool_(); + + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_to_str)) { + boolean v = m.ReadBool(GfsCore_.Arg_primitive); + String fmt = m.ReadStrOr("fmt", null); + if (fmt == null) return v ? "true" : "false"; + else if (String_.Eq(fmt, "yn")) return v ? "y" : "n"; + else if (String_.Eq(fmt, "yes_no")) return v ? "yes" : "no"; + else return v ? "true" : "false"; + } + else return GfoInvkAble_.Rv_unhandled; + } public static final String Invk_to_str = "to_str"; +} diff --git a/100_core/src_110_primitive/gplx/Bool_default.java b/100_core/src_110_primitive/gplx/Bool_default.java new file mode 100644 index 000000000..16920118a --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bool_default.java @@ -0,0 +1,45 @@ +/* +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; +public class Bool_default { + public static final byte Tid_n = 0, Tid_y = 1, Tid_i = 2; + public static final String Str_n = "never", Str_y = "always", Str_i = "default"; + public static final byte Chr_n = Byte_ascii.Ltr_n, Chr_y = Byte_ascii.Ltr_y, Chr_i = Byte_ascii.Ltr_i; + public static String Xto_str(byte v) { + switch (v) { + case Tid_n: return Str_n; + case Tid_y: return Str_y; + case Tid_i: return Str_i; + default: throw Err_.unhandled(v); + } + } + public static byte Xto_char_byte(byte v) { + switch (v) { + case Tid_n: return Chr_n; + case Tid_y: return Chr_y; + case Tid_i: return Chr_i; + default: throw Err_.unhandled(v); + } + } + public static byte Xto_tid(String v) { + if (String_.Eq(v, Str_n)) return Tid_n; + else if (String_.Eq(v, Str_y)) return Tid_y; + else if (String_.Eq(v, Str_i)) return Tid_i; + else throw Err_.unhandled(v); + } +} diff --git a/100_core/src_110_primitive/gplx/Bool_obj_ref.java b/100_core/src_110_primitive/gplx/Bool_obj_ref.java new file mode 100644 index 000000000..76dd3490a --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bool_obj_ref.java @@ -0,0 +1,33 @@ +/* +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; +public class Bool_obj_ref { + public boolean Val() {return val;} private boolean val; + public Bool_obj_ref Val_y_() {val = true; return this;} + public Bool_obj_ref Val_n_() {val = false; return this;} + public Bool_obj_ref Val_(boolean v) {val = v; return this;} + public Bool_obj_ref Val_toggle_() {val = !val; return this;} + @Override public String toString() {return Bool_.XtoStr_lower(val);} + public static Bool_obj_ref n_() {return new_(false);} + public static Bool_obj_ref y_() {return new_(true);} + public static Bool_obj_ref new_(boolean val) { + Bool_obj_ref rv = new Bool_obj_ref(); + rv.val = val; + return rv; + } Bool_obj_ref() {} +} diff --git a/100_core/src_110_primitive/gplx/Bool_obj_val.java b/100_core/src_110_primitive/gplx/Bool_obj_val.java new file mode 100644 index 000000000..b380523ff --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bool_obj_val.java @@ -0,0 +1,33 @@ +/* +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; +public class Bool_obj_val { + public boolean Val() {return val == 1;} + Bool_obj_val(int v) {val = v;} int val; + public static final Bool_obj_val + Null = new Bool_obj_val(-1) + , False = new Bool_obj_val(0) + , True = new Bool_obj_val(1); + public static Bool_obj_val read_(Object o) {String s = String_.as_(o); return s == null ? (Bool_obj_val)o : parse_(s);} + public static Bool_obj_val parse_(String raw) { + if (String_.Eq(raw, "y")) return Bool_obj_val.True; + else if (String_.Eq(raw, "n")) return Bool_obj_val.False; + else if (String_.Eq(raw, "")) return Bool_obj_val.Null; + else throw Err_.parse_type_(Bool_obj_val.class, raw); + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_.java b/100_core/src_110_primitive/gplx/Bry_.java new file mode 100644 index 000000000..35e9e7d20 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_.java @@ -0,0 +1,891 @@ +/* +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; +import java.lang.*; +import gplx.ios.*; +public class Bry_ { + public static final int NotFound = -1; + public static final byte[] Empty = new byte[0]; + public static final byte[][] Ary_empty = new byte[0][]; + public static final Class ClassOf = byte[].class; + public static byte[] bytes_(byte... ary) {return ary;} + public static byte[] ints_ (int... ary) { + int len = ary.length; + byte[] rv = new byte[len]; + for (int i = 0; i < len; i++) + rv[i] = (byte)ary[i]; + return rv; + } + public static byte[] new_utf8_(String s) { + try {return s.getBytes("UTF-8");} + catch (Exception e) {throw Err_.err_(e, "unsupported encoding");} + } + public static byte[] new_ascii_(String s) { + try {return s == null ? null : s.getBytes("ASCII");} + catch (Exception e) {throw Err_.err_(e, "unsupported encoding");} + } + public static byte[] new_ascii_safe_null_(String s) {return s == null ? null : new_ascii_(s);} + public static byte[] Coalesce(byte[] orig, byte[] val_if_not_blank) {return Bry_.Len_eq_0(val_if_not_blank) ? orig : val_if_not_blank;} + public static int While_fwd(byte[] src, byte while_byte, int bgn, int end) { + for (int i = bgn; i < end; i++) + if (src[i] != while_byte) return i; + return end; + } + public static byte[][] Ary_add(byte[][] lhs, byte[][] rhs) { + int lhs_len = lhs.length, rhs_len = rhs.length; + if (lhs_len == 0) return rhs; + else if (rhs_len == 0) return lhs; + else { + byte[][] rv = new byte[lhs_len + rhs_len][]; + for (int i = 0; i < lhs_len; i++) + rv[i] = lhs[i]; + for (int i = 0; i < rhs_len; i++) + rv[i + lhs_len] = rhs[i]; + return rv; + } + } + public static byte[][] Ary(byte[]... ary) {return ary;} + public static byte[][] Ary(String... ary) { + int ary_len = ary.length; + byte[][] rv = new byte[ary_len][]; + for (int i = 0; i < ary_len; i++) { + String itm = ary[i]; + rv[i] = itm == null ? null : Bry_.new_utf8_(itm); + } + return rv; + } + public static byte[][] Ary_obj(Object... ary) { + if (ary == null) return Bry_.Ary_empty; + int ary_len = ary.length; + byte[][] rv = new byte[ary_len][]; + for (int i = 0; i < ary_len; i++) { + Object itm = ary[i]; + rv[i] = itm == null ? null : Bry_.new_utf8_(Object_.XtoStr_OrEmpty(itm)); + } + return rv; + } + public static byte[] Repeat_space(int len) {return Repeat(Byte_ascii.Space, len);} + public static byte[] Repeat(byte b, int len) { + byte[] rv = new byte[len]; + for (int i = 0; i < len; i++) + rv[i] = b; + return rv; + } + public static byte[] Copy(byte[] src) { + int src_len = src.length; + byte[] trg = new byte[src_len]; + for (int i = 0; i < src_len; i++) + trg[i] = src[i]; + return trg; + } + public static void Copy_by_pos(byte[] src, int src_bgn, int src_end, byte[] trg, int trg_bgn) { + int trg_adj = trg_bgn - src_bgn; + for (int i = src_bgn; i < src_end; i++) + trg[i + trg_adj] = src[i]; + } + public static void Copy_by_len(byte[] src, int src_bgn, int src_len, byte[] trg, int trg_bgn) { + for (int i = 0; i < src_len; i++) + trg[i + trg_bgn] = src[i + src_bgn]; + } + public static byte[][] XtoByteAryAry(String... strAry) { + int strAryLen = strAry.length; + byte[][] rv = new byte[strAryLen][]; + for (int i = 0; i < strAryLen; i++) + rv[i] = Bry_.new_utf8_(strAry[i]); + return rv; + } + public static byte[] Xto_str_lower(byte[] src, int bgn, int end) { + int len = end - bgn; + byte[] rv = new byte[len]; + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + b += 32; + break; + } + rv[i - bgn] = b; + } + return rv; + } + public static byte[] Replace_one(byte[] src, byte[] find, byte[] repl) { + int src_len = src.length; + int findPos = Bry_finder.Find(src, find, 0, src_len, true); if (findPos == Bry_.NotFound) return src; + int findLen = find.length, replLen = repl.length; + int rvLen = src_len + replLen - findLen; + byte[] rv = new byte[rvLen]; + Copy_by_len(src , 0 , findPos , rv, 0 ); + Copy_by_len(repl, 0 , replLen , rv, findPos ); + Copy_by_len(src , findPos + findLen , src_len - findPos - findLen , rv, findPos + replLen); + return rv; + } + public static void Replace_all_direct(byte[] src, byte find, byte repl) {Replace_all_direct(src, find, repl, 0, src.length);} + public static void Replace_all_direct(byte[] src, byte find, byte repl, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + if (b == find) src[i] = repl; + } + } + public static byte[] Add_w_dlm(byte dlm, byte[]... ary) { + int ary_len = ary.length; + if (ary_len == 0) return Bry_.Empty; + int rv_len = ary_len - 1; // rv will have at least as many dlms as itms - 1 + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (itm != null) rv_len += itm.length; + } + int rv_pos = 0; + byte[] rv = new byte[rv_len]; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + if (i != 0) rv[rv_pos++] = dlm; + if (itm == null) continue; + int itm_len = itm.length; + for (int j = 0; j < itm_len; j++) { + rv[rv_pos++] = itm[j]; + } + } + return rv; + } + public static byte[] Add(byte[] ary, byte b) { + int ary_len = ary.length; + byte[] rv = new byte[ary_len + 1]; + for (int i = 0; i < ary_len; i++) + rv[i] = ary[i]; + rv[ary_len] = b; + return rv; + } + public static byte[] Add(byte b, byte[] ary) { + int ary_len = ary.length + 1; + byte[] rv = new byte[ary_len]; + for (int i = 1; i < ary_len; i++) + rv[i] = ary[i - 1]; + rv[0] = b; + return rv; + } + public static byte[] Add(byte[]... all) { + int all_len = all.length, rv_len = 0; + for (int i = 0; i < all_len; i++) { + byte[] cur = all[i]; if (all[i] == null) continue; + rv_len += cur.length; + } + byte[] rv = new byte[rv_len]; + int rv_idx = 0; + for (int i = 0; i < all_len; i++) { + byte[] cur = all[i]; if (all[i] == null) continue; + int cur_len = cur.length; + for (int j = 0; j < cur_len; j++) + rv[rv_idx++] = cur[j]; + } + return rv; + } + public static int LastIdx(byte[] src) {return src.length - 1;} + public static byte[] Limit(byte[] src, int len) { + if (src == null) return null; + int src_len = src.length; + return len < src_len ? Bry_.Mid(src, 0, len) : src; + } + public static byte[] Mid_by_nearby(byte[] src, int pos, int around) { + int bgn = pos - around; if (bgn < 0) bgn = 0; + int src_len = src.length; + int end = pos + around; if (end > src_len) end = src_len; + return Mid(src, bgn, end); + } + public static byte[] Mid_by_len(byte[] src, int bgn, int len) {return Mid(src, bgn, bgn + len);} + public static byte[] Mid_by_len_safe(byte[] src, int bgn, int len) { + if (len > src.length) len = src.length; + return Mid(src, bgn, bgn + len); + } + public static String MidByLenToStr(byte[] src, int bgn, int len) { + int end = bgn + len; end = Int_.BoundEnd(end, src.length); + byte[] ary = Bry_.Mid(src, bgn, end); + return String_.new_utf8_(ary); + } + public static byte[] Mid(byte[] src, int bgn) {return Mid(src, bgn, src.length);} + public static byte[] Mid_or(byte[] src, int bgn, int end, byte[] or) { + int src_len = src.length; + if ( src == null + || (bgn < 0 || bgn > src_len) + || (end < 0 || end > src_len) + || (end < bgn) + ) + return or; + return Mid(src, bgn, src.length); + } + public static byte[] Mid(byte[] src, int bgn, int end) { + try { + int len = end - bgn; if (len == 0) return Bry_.Empty; + byte[] rv = new byte[len]; + for (int i = bgn; i < end; i++) + rv[i - bgn] = src[i]; + return rv; + } catch (Exception e) { + Err err = Err_.new_("").Add("bgn", bgn).Add("end", end); + if (src != null) err.Add("src", String_.new_utf8_len_safe_(src, bgn, 32)); + if (src == null) err.Hdr_("src is null"); + else if (bgn < 0 || bgn > src.length) err.Hdr_("invalid bgn"); + else if (end < 0 || end > src.length) err.Hdr_("invalid end"); + else if (end < bgn) err.Hdr_("end < bgn"); + else err.Hdr_(Err_.Message_lang(e)); + throw err; + } + } + public static byte[] mask_(int len, byte... itms) { + byte[] rv = new byte[len]; + int itms_len = itms.length; + for (int i = 0; i < itms_len; i++) { + byte itm = itms[i]; + rv[itm & 0xFF] = itm; // PATCH.JAVA:need to convert to unsigned byte + } + return rv; + } + public static final byte[] Trim_ary_ws = mask_(256, Byte_ascii.Tab, Byte_ascii.NewLine, Byte_ascii.CarriageReturn, Byte_ascii.Space); + public static byte[] Trim(byte[] src) {return Trim(src, 0, src.length, true, true, Trim_ary_ws);} + public static byte[] Trim(byte[] src, int bgn, int end) {return Trim(src, bgn, end, true, true, Trim_ary_ws);} + public static byte[] Trim(byte[] src, int bgn, int end, boolean trim_bgn, boolean trim_end, byte[] trim_ary) { + int txt_bgn = bgn, txt_end = end; + boolean all_ws = true; + if (trim_bgn) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Nil) { + txt_bgn = i; + i = end; + all_ws = false; + } + } + if (all_ws) return Bry_.Empty; + } + if (trim_end) { + for (int i = end - 1; i > -1; i--) { + byte b = src[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Nil) { + txt_end = i + 1; + i = -1; + all_ws = false; + } + } + if (all_ws) return Bry_.Empty; + } + return Bry_.Mid(src, txt_bgn, txt_end); + } + public static byte[] Trim_end(byte[] v, byte trim, int end) { + boolean trimmed = false; + int pos = end - 1; // NOTE: -1 b/c callers will always be passing pos + 1; EX: src, src_len + for (; pos > -1; pos--) { + if (v[pos] == trim) { + trimmed = true; + } + else + break; + } + return trimmed ? Bry_.Mid(v, 0, pos + 1) : v; + } + public static boolean Has(byte[] src, byte lkp) { + int len = src.length; + for (int i = 0; i < len; i++) + if (src[i] == lkp) return true; + return false; + } + public static boolean HasAtEnd(byte[] src, byte[] lkp) {int src_len = src.length; return HasAtEnd(src, lkp, src_len - lkp.length, src_len);} + public static boolean HasAtEnd(byte[] src, byte[] lkp, int src_bgn, int src_end) { + int lkp_len = lkp.length; + if (src_bgn < 0) return false; + int pos = src_end - lkp_len; if (pos < src_bgn) return false; // lkp is longer than src + for (int i = 0; i < lkp_len; i++) { + if (lkp[i] != src[i + pos]) return false; + } + return true; + } + public static boolean HasAtBgn(byte[] src, byte[] lkp) {return HasAtBgn(src, lkp, 0, src.length);} + public static boolean HasAtBgn(byte[] src, byte[] lkp, int src_bgn, int srcEnd) { + int lkpLen = lkp.length; + if (lkpLen + src_bgn > srcEnd) return false; // lkp is longer than src + for (int i = 0; i < lkpLen; i++) { + if (lkp[i] != src[i + src_bgn]) return false; + } + return true; + } + public static int Skip_fwd(byte[] src, int src_bgn, byte skip) { + int src_len = src.length; + for (int i = src_bgn; i < src_len; i++) { + byte b = src[i]; + if (b != skip) return i; + } + return 0; + } + public static int Skip_bwd(byte[] src, int src_bgn, byte skip) { + for (int i = src_bgn; i > -1; i--) { + byte b = src[i]; + if (b != skip) return i; + } + return src.length; + } + public static int Skip_fwd_nl(byte[] src, int src_bgn) { + int src_len = src.length; + for (int i = src_bgn; i < src_len; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + break; + default: + return i; + } + } + return 0; + } + public static int Skip_bwd_nl(byte[] src, int src_bgn) { + for (int i = src_bgn; i > -1; i--) { + byte b = src[i]; + switch (b) { + case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + break; + default: + return i; + } + } + return src.length; + } + public static byte[] Resize_manual(byte[] src, int rvLen) { + byte[] rv = new byte[rvLen]; + int src_len = src.length; + if (src_len > rvLen) src_len = rvLen; // resizing smaller; only copy as many elements as in rvLen + for (int i = 0; i < src_len; i++) + rv[i] = src[i]; + return rv; + } + public static byte[] Resize(byte[] src, int trgLen) {return Resize(src, 0, trgLen);} + public static byte[] Resize(byte[] src, int src_bgn, int trgLen) { + byte[] trg = new byte[trgLen]; + int src_len = src.length > trgLen ? trgLen : src.length; // trgLen can either expand or shrink + Copy_by_len(src, src_bgn, src_len, trg, 0); + return trg; + } + public static boolean Match(byte[] src, byte[] find) {return Match(src, 0, src.length, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, byte[] find) {return Match(src, src_bgn, src.length, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find) {return Match(src, src_bgn, src_end, find, 0, find.length);} + public static boolean Match(byte[] src, int src_bgn, int src_end, byte[] find, int find_bgn, int find_end) { + int find_len = find_end - find_bgn; + if (find_len != src_end - src_bgn) return false; + if (find_len == 0) return src_end - src_bgn == 0; // "" only matches "" + for (int i = 0; i < find_len; i++) { + int pos = src_bgn + i; + if (pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[pos] != find[i + find_bgn]) return false; + } + return true; + } + public static boolean Match_bwd_any(byte[] src, int src_end, int src_bgn, byte[] find) { // NOTE: utf8 doesn't matter (matching byte for byte) + int find_len = find.length; + for (int i = 0; i < find_len; i++) { + int src_pos = src_end - i; + int find_pos = find_len - i - 1; + if (src_pos < src_bgn) return false; // ran out of src; exit; EX: src=ab; find=abc + if (src[src_pos] != find[find_pos]) return false; + } + return true; + } + public static int Compare(byte[] lhs, byte[] rhs) { + if (lhs == null) return CompareAble_.More; + else if (rhs == null) return CompareAble_.Less; + else return Compare(lhs, 0, lhs.length, rhs, 0, rhs.length); + } + public static int Compare(byte[] lhs, int lhs_bgn, int lhs_end, byte[] rhs, int rhs_bgn, int rhs_end) { + int lhs_len = lhs_end - lhs_bgn, rhs_len = rhs_end - rhs_bgn; + int min = lhs_len < rhs_len ? lhs_len : rhs_len; + int rv = CompareAble_.Same; + for (int i = 0; i < min; i++) { + rv = (lhs[i + lhs_bgn] & 0xff) - (rhs[i + rhs_bgn] & 0xff); // PATCH.JAVA:need to convert to unsigned byte + if (rv != CompareAble_.Same) return rv > CompareAble_.Same ? CompareAble_.More : CompareAble_.Less; // NOTE: changed from if (rv != CompareAble_.Same) return rv; DATE:2013-04-25 + } + return Int_.Compare(lhs_len, rhs_len); // lhs and rhs share same beginning bytes; return len comparisons + } + public static int Len(byte[] v) {return v == null ? 0 : v.length;} + public static boolean Len_gt_0(byte[] v) {return v != null && v.length > 0;} + public static boolean Len_eq_0(byte[] v) {return v == null || v.length == 0;} + public static void Set(byte[] src, int bgn, int end, byte[] repl) { + int repl_len = repl.length; + for (int i = 0; i < repl_len; i++) + src[i + bgn] = repl[i]; + } + public static boolean Eq_itm(byte[] src, int src_len, int pos, byte chk) { + return pos < src_len + && src[pos] == chk; + } + public static boolean Eq(byte[] lhs, byte[] rhs) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + int lhs_len = lhs.length; + if (lhs_len != rhs.length) return false; + for (int i = 0; i < lhs_len; i++) // NOTE: lhs_len == rhsLen + if (lhs[i] != rhs[i]) return false; + return true; + } + public static boolean Eq(byte[] lhs, byte[] rhs, int rhs_bgn, int rhs_end) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + int lhs_len = lhs.length; + int rhs_len = rhs_end - rhs_bgn; + if (lhs_len != rhs_len) return false; + for (int i = 0; i < lhs_len; i++) + if (lhs[i] != rhs[i + rhs_bgn]) return false; + return true; + } + public static byte[] XtoStrBytesByInt(int val, int padLen) {return XtoStrBytesByInt(val, null, 0, padLen);} + public static byte[] XtoStrBytesByInt(int val, byte[] ary, int aryPos, int padLen) { + int neg = 0; + if (val < 0) { + val *= -1; + neg = 1; + } + int digits = val == 0 ? 0 : Math_.Log10(val); + digits += 1; // digits = log + 1; EX: Log(1-9) = 0, Log(10-99) = 1 + int aryLen = digits + neg, aryBgn = aryPos, pad = 0; + if (aryLen < padLen) { // padding specified + pad = padLen - aryLen; + aryLen = padLen; + } + if (ary == null) ary = new byte[aryLen]; + long factor = 1; // factor needs to be long to handle 1 billion (for which factor would be 10 billion) + for (int i = 0; i < digits; i++) // calc maxFactor + factor *= 10; + if (neg == 1) ary[0] = Byte_NegSign; + + for (int i = 0; i < pad; i++) // fill ary with pad + ary[i + aryBgn] = XtoStrByte(0); + aryBgn += pad; // advance aryBgn by pad + for (int i = neg; i < aryLen - pad; i++) { + int denominator = (int)(factor / 10); // cache denominator to check for divide by 0 + int digit = denominator == 0 ? 0 : (int)((val % factor) / denominator); + ary[aryBgn + i] = XtoStrByte(digit); + factor /= 10; + } + return ary; + } + public static byte X_to_byte_by_int(byte[] ary, int bgn, int end, byte or) {return (byte)X_to_int_or(ary, bgn, end, or);} + public static int X_to_int(byte[] ary) {return X_to_int_or(ary, null, 0, ary.length, -1);} + public static int X_to_int_or_fail(byte[] ary) { + int rv = X_to_int_or(ary, null, 0, ary.length, Int_.MinValue); + if (rv == Int_.MinValue) throw Err_.new_fmt_("could not parse to int; val={0}", String_.new_utf8_(ary)); + return rv; + } + public static boolean X_to_bool_by_int_or_fail(byte[] ary) { + int rv = X_to_int_or(ary, null, 0, ary.length, Int_.MinValue); + switch (rv) { + case 0: return false; + case 1: return true; + default: throw Err_.new_fmt_("could not parse to boolean int; val={0}", String_.new_utf8_(ary)); + } + } + public static int X_to_int_or(byte[] ary, int or) {return X_to_int_or(ary, null, 0, ary.length, or);} + public static int X_to_int_or(byte[] ary, int bgn, int end, int or) {return X_to_int_or(ary, null, bgn, end, or);} + public static int X_to_int_or(byte[] ary, byte[] ignore_ary, int bgn, int end, int or) { + if ( ary == null + || end == bgn // null-len + ) return or; + int rv = 0, multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + break; + case Byte_ascii.Dash: + return i == bgn ? rv * -1 : or; + case Byte_ascii.Plus: + return i == bgn ? rv : or; + default: + boolean invalid = true; + if (ignore_ary != null) { + int ignore_ary_len = ignore_ary.length; + for (int j = 0; j < ignore_ary_len; j++) { + if (b == ignore_ary[j]) { + invalid = false; + break; + } + } + } + if (invalid) return or; + break; + } + } + return rv; + } + public static int X_to_int_or_trim(byte[] ary, int bgn, int end, int or) { // NOTE: same as X_to_int_or, except trims ws at bgn / end; DATE:2014-02-09 + if (end == bgn) return or; // null len + int rv = 0, multiple = 1; + boolean numbers_seen = false, ws_seen = false; + for (int i = end - 1; i >= bgn; i--) { // -1 b/c end will always be next char; EX: {{{1}}}; bgn = 3, end = 4 + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + rv += multiple * (b - Byte_ascii.Num_0); + multiple *= 10; + if (ws_seen) // "number ws number" pattern; invalid ws in middle; see tests + return or; + numbers_seen = true; + break; + case Byte_ascii.Dash: + return i == bgn ? rv * -1 : or; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + if (numbers_seen) + ws_seen = true; + break; + default: return or; + } + } + return rv; + } + public static int X_to_int_or_lax(byte[] ary, int bgn, int end, int or) { + if (end == bgn) return or; // null-len + int end_num = end; + for (int i = bgn; i < end; i++) { + byte b = ary[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + case Byte_ascii.Dash: + if (i != bgn) { + end_num = i; + i = end; + } + break; + default: + end_num = i; + i = end; + break; + } + } + return X_to_int_or(ary, bgn, end_num, or); + } + public static float XtoFloatByPos(byte[] ary, int bgn, int end) {return Float_.parse_(String_.new_utf8_(ary, bgn, end));} + public static double XtoDoubleByPosOr(byte[] ary, int bgn, int end, double or) {return Double_.parseOr_(String_.new_utf8_(ary, bgn, end), or);} + public static double XtoDoubleByPos(byte[] ary, int bgn, int end) {return Double_.parse_(String_.new_utf8_(ary, bgn, end));} + public static DecimalAdp XtoDecimalByPos(byte[] ary, int bgn, int end) {return DecimalAdp_.parse_(String_.new_utf8_(ary, bgn, end));} + public static final byte Dlm_fld = (byte)'|', Dlm_row = (byte)'\n', Dlm_quote = (byte)'"', Dlm_null = 0, Ascii_zero = 48; + public static final String Fmt_csvDte = "yyyyMMdd HHmmss.fff"; + public static DateAdp ReadCsvDte(byte[] ary, Int_obj_ref posRef, byte lkp) {// ASSUME: fmt = yyyyMMdd HHmmss.fff + int y = 0, M = 0, d = 0, H = 0, m = 0, s = 0, f = 0; + int bgn = posRef.Val(); + y += (ary[bgn + 0] - Ascii_zero) * 1000; + y += (ary[bgn + 1] - Ascii_zero) * 100; + y += (ary[bgn + 2] - Ascii_zero) * 10; + y += (ary[bgn + 3] - Ascii_zero); + M += (ary[bgn + 4] - Ascii_zero) * 10; + M += (ary[bgn + 5] - Ascii_zero); + d += (ary[bgn + 6] - Ascii_zero) * 10; + d += (ary[bgn + 7] - Ascii_zero); + H += (ary[bgn + 9] - Ascii_zero) * 10; + H += (ary[bgn + 10] - Ascii_zero); + m += (ary[bgn + 11] - Ascii_zero) * 10; + m += (ary[bgn + 12] - Ascii_zero); + s += (ary[bgn + 13] - Ascii_zero) * 10; + s += (ary[bgn + 14] - Ascii_zero); + f += (ary[bgn + 16] - Ascii_zero) * 100; + f += (ary[bgn + 17] - Ascii_zero) * 10; + f += (ary[bgn + 18] - Ascii_zero); + if (ary[bgn + 19] != lkp) throw Err_.new_("csv date is invalid").Add("txt", String_.new_utf8_len_safe_(ary, bgn, 20)); + posRef.Val_add(19 + 1); // +1=lkp.len + return DateAdp_.new_(y, M, d, H, m, s, f); + } + public static String ReadCsvStr(byte[] ary, Int_obj_ref posRef, byte lkp) {return String_.new_utf8_(ReadCsvBry(ary, posRef, lkp, true));} + public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp) {return ReadCsvBry(ary, posRef, lkp, true);} + public static byte[] ReadCsvBry(byte[] ary, Int_obj_ref posRef, byte lkp, boolean make) { + int bgn = posRef.Val(), aryLen = ary.length; + Bry_bfr bb = null; + if (aryLen > 0 && ary[0] == Dlm_quote) { + int pos = bgn + 1; // +1 to skip quote + if (make) bb = Bry_bfr.new_(16); + while (true) { + if (pos == aryLen) throw Err_.new_("endOfAry reached, but no quote found").Add("txt", String_.new_utf8_len_safe_(ary, bgn, pos)); + byte b = ary[pos]; + if (b == Dlm_quote) { + if (pos == aryLen - 1) throw Err_.new_("endOfAry reached, quote found but lkp not").Add("txt", String_.new_utf8_len_safe_(ary, bgn, pos)); + byte next = ary[pos + 1]; + if (next == Dlm_quote) { // byte followed by quote + if (make) bb.Add_byte(b); + pos += 2; + } + else if (next == lkp) { + posRef.Val_(pos + 2); // 1=endQuote;1=lkp; + return make ? bb.XtoAry() : Bry_.Empty; + } + else throw Err_.new_("quote found, but not doubled").Add("txt", String_.new_utf8_len_safe_(ary, bgn, pos + 1)); + } + else { + if (make) bb.Add_byte(b); + pos++; + } + } + } + else { + for (int i = bgn; i < aryLen; i++) { + if (ary[i] == lkp) { + posRef.Val_(i + 1); // +1 = lkp.Len + return make ? Bry_.Mid(ary, bgn, i) : Bry_.Empty; + } + } + throw Err_.new_("lkp failed").Add("lkp", (char)lkp).Add("txt", String_.new_utf8_len_safe_(ary, bgn, aryLen)); + } + } + public static int ReadCsvInt(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_finder.Find_fwd(ary, lkp, bgn, ary.length); + if (pos == Bry_.NotFound) throw Err_.new_("lkp failed").Add("lkp", (char)lkp).Add("bgn", bgn); + int rv = Bry_.X_to_int_or(ary, posRef.Val(), pos, -1); + posRef.Val_(pos + 1); // +1 = lkp.Len + return rv; + } + public static double ReadCsvDouble(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_finder.Find_fwd(ary, lkp, bgn, ary.length); + if (pos == Bry_.NotFound) throw Err_.new_("lkp failed").Add("lkp", (char)lkp).Add("bgn", bgn); + double rv = Bry_.XtoDoubleByPos(ary, posRef.Val(), pos); + posRef.Val_(pos + 1); // +1 = lkp.Len + return rv; + } + public static void ReadCsvNext(byte[] ary, Int_obj_ref posRef, byte lkp) { + int bgn = posRef.Val(); + int pos = Bry_finder.Find_fwd(ary, lkp, bgn, ary.length); + posRef.Val_(pos + 1); // +1 = lkp.Len + } + public static byte Byte_NegSign = (byte)'-'; + public static int XtoIntBy4Bytes(byte[] v) { + int v_len = v.length; + int mod = 8 * (v_len - 1); + int rv = 0; + for (int i = 0; i < v_len; i++) { + rv |= v[i] << mod; + mod -= 8; + } + return rv; +// return ((0xFF & v[0]) << 24) +// | ((0xFF & v[1]) << 16) +// | ((0xFF & v[2]) << 8) +// | (0xFF & v[3]); + } + public static byte[] XbyInt(int v) { + byte b0 = (byte)(v >> 24); + byte b1 = (byte)(v >> 16); + byte b2 = (byte)(v >> 8); + byte b3 = (byte)(v); + if (b0 != 0) return new byte[] {b0, b1, b2, b3}; + else if (b1 != 0) return new byte[] {b1, b2, b3}; + else if (b2 != 0) return new byte[] {b2, b3}; + else return new byte[] {b3}; + } + public static byte XtoStrByte(int digit) { + switch (digit) { + case 0: return Byte_ascii.Num_0; case 1: return Byte_ascii.Num_1; case 2: return Byte_ascii.Num_2; case 3: return Byte_ascii.Num_3; case 4: return Byte_ascii.Num_4; + case 5: return Byte_ascii.Num_5; case 6: return Byte_ascii.Num_6; case 7: return Byte_ascii.Num_7; case 8: return Byte_ascii.Num_8; case 9: return Byte_ascii.Num_9; + default: throw Err_.new_("unknown digit").Add("digit", digit); + } + } + public static byte[][] Split(byte[] src, byte dlm) {return Split(src, dlm, false);} + public static byte[][] Split(byte[] src, byte dlm, boolean trim) { + if (Bry_.Len_eq_0(src)) return Bry_.Ary_empty; + int src_len = src.length, src_pos = 0, fld_bgn = 0; + ListAdp rv = ListAdp_.new_(); + while (true) { + boolean last = src_pos == src_len; + byte b = last ? dlm : src[src_pos]; + if (b == dlm) { + if (last && (src_pos - fld_bgn == 0)) {} + else { + byte[] itm = Bry_.Mid(src, fld_bgn, src_pos); + if (trim) itm = Bry_.Trim(itm); + rv.Add(itm); + } + fld_bgn = src_pos + 1; + } + if (last) break; + ++src_pos; + } + return (byte[][])rv.XtoAry(byte[].class); + } + public static void Replace_reuse(byte[] src, byte find, byte replace) { + int src_len = src.length; + for (int i = 0; i < src_len; i++) { + if (src[i] == find) src[i] = replace; + } + } + public static byte[] Replace(byte[] src, byte find, byte replace) { + int src_len = src.length; + byte[] rv = new byte[src_len]; + for (int i = 0; i < src_len; i++) { + byte b = src[i]; + rv[i] = b == find ? replace : b; + } + return rv; + } + public static byte[] Replace_safe(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) { + if (src == null || find == null || repl == null) return null; + return Replace(bfr, src, find, repl, 0, src.length); + } + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl) {return Replace(bfr, src, find, repl, 0, src.length);} + public static byte[] Replace(Bry_bfr bfr, byte[] src, byte[] find, byte[] repl, int src_bgn, int src_end) { + int pos = src_bgn; + boolean dirty = false; + int find_len = find.length; + int bfr_bgn = pos; + while (pos < src_end) { + int find_pos = Bry_finder.Find_fwd(src, find, pos); + if (find_pos == Bry_finder.Not_found) break; + dirty = true; + bfr.Add_mid(src, bfr_bgn, find_pos); + bfr.Add(repl); + pos = find_pos + find_len; + bfr_bgn = pos; + } + if (dirty) + bfr.Add_mid(src, bfr_bgn, src_end); + return dirty ? bfr.XtoAryAndClear() : src; + } + public static byte[] Replace(byte[] src, byte[] find, byte[] replace) {return Replace_between(src, find, null, replace);} + public static byte[] Replace_between(byte[] src, byte[] bgn, byte[] end, byte[] replace) { + Bry_bfr bfr = Bry_bfr.new_(); + boolean replace_all = end == null; + int src_len = src.length, bgn_len = bgn.length, end_len = replace_all ? 0 : end.length; + int pos = 0; + while (true) { + if (pos >= src_len) break; + int bgn_pos = Bry_finder.Find_fwd(src, bgn, pos); + if (bgn_pos == Bry_.NotFound) { + bfr.Add_mid(src, pos, src_len); + break; + } + else { + int bgn_rhs = bgn_pos + bgn_len; + int end_pos = replace_all ? bgn_rhs : Bry_finder.Find_fwd(src, end, bgn_rhs); + if (end_pos == Bry_.NotFound) { + bfr.Add_mid(src, pos, src_len); + break; + } + else { + bfr.Add_mid(src, pos, bgn_pos); + bfr.Add(replace); + pos = end_pos + end_len; + } + } + } + return bfr.XtoAryAndClear(); + } + public static int Trim_end_pos(byte[] src, int end) { + for (int i = end - 1; i > -1; i--) { + switch (src[i]) { + case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: case Byte_ascii.Space: + break; + default: + return i + 1; + } + } + return 0; + } + public static byte[][] Split(byte[] src, byte[] dlm) { + if (Bry_.Len_eq_0(src)) return Bry_.Ary_empty; + int cur_pos = 0, src_len = src.length, dlm_len = dlm.length; + ListAdp rv = ListAdp_.new_(); + while (true) { + int find_pos = Bry_finder.Find_fwd(src, dlm, cur_pos); + if (find_pos == Bry_.NotFound) { + if (cur_pos == src_len) break; // dlm is last sequence in src; do not create empty itm + find_pos = src_len; + } + rv.Add(Bry_.Mid(src, cur_pos, find_pos)); + if (find_pos == src_len) break; + cur_pos = find_pos + dlm_len; + } + return (byte[][])rv.XtoAry(byte[].class); + } + public static byte[][] Split_lines(byte[] src) { + if (Bry_.Len_eq_0(src)) return Bry_.Ary_empty; + int src_len = src.length, src_pos = 0, fld_bgn = 0; + ListAdp rv = ListAdp_.new_(); + while (true) { + boolean last = src_pos == src_len; + byte b = last ? Byte_ascii.NewLine : src[src_pos]; + int nxt_bgn = src_pos + 1; + switch (b) { + case Byte_ascii.CarriageReturn: + case Byte_ascii.NewLine: + if ( b == Byte_ascii.CarriageReturn // check for crlf + && nxt_bgn < src_len && src[nxt_bgn] == Byte_ascii.NewLine) { + ++nxt_bgn; + } + if (last && (src_pos - fld_bgn == 0)) {} // ignore trailing itms + else + rv.Add(Bry_.Mid(src, fld_bgn, src_pos)); + fld_bgn = nxt_bgn; + break; + } + if (last) break; + src_pos = nxt_bgn; + } + return (byte[][])rv.XtoAry(byte[].class); + } + public static byte[] Increment_last(byte[] ary) {return Increment_last(ary, ary.length - 1);} + public static byte[] Increment_last(byte[] ary, int end_idx) { + for (int i = end_idx; i > -1; i--) { + byte end_val_old = ary[i]; + byte end_val_new = (byte)(end_val_old + 1); + ary[i] = end_val_new; + if (end_val_new > (end_val_old & 0xff)) break; // PATCH.JAVA:need to convert to unsigned byte + } + return ary; + } + public static byte[] Upper_1st(byte[] ary) { + if (ary == null) return null; + int len = ary.length; + if (len == 0) return ary; + byte b = ary[0]; + if (b > 96 && b < 123) + ary[0] = (byte)(b - 32); + return ary; + } + public static byte[] Upper_ascii(byte[] ary) { + int len = ary.length; + for (int i = 0; i < len; i++) { + byte b = ary[i]; + if (b > 96 && b < 123) + ary[i] = (byte)(b - 32); + } + return ary; + } + public static byte[] Lower_ascii(byte[] ary) { + int len = ary.length; + for (int i = 0; i < len; i++) { + byte b = ary[i]; + if (b > 64 && b < 91) + ary[i] = (byte)(b + 32); + } + return ary; + } + public static byte[] Null_if_empty(byte[] v) {return Len_eq_0(v) ? null : v;} + public static byte Get_at_end(byte[] v) { + int v_len = v.length; + return v_len == 0 ? Byte_ascii.Nil : v[v_len - 1]; + } +} diff --git a/100_core/src_110_primitive/gplx/Bry__tst.java b/100_core/src_110_primitive/gplx/Bry__tst.java new file mode 100644 index 000000000..d84c5ecea --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry__tst.java @@ -0,0 +1,272 @@ +/* +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; +import org.junit.*; +import gplx.texts.*; +public class Bry__tst { + @Test public void MidByPos() { + tst_MidByPos("abcba", 0, 1, "a"); + tst_MidByPos("abcba", 0, 2, "ab"); + tst_MidByPos("abcba", 1, 4, "bcb"); + } void tst_MidByPos(String src, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_utf8_(Bry_.Mid(Bry_.new_utf8_(src), bgn, end)));} + @Test public void Replace_one() { + tst_ReplaceOne("a" , "b" , "c" , "a"); + tst_ReplaceOne("b" , "b" , "c" , "c"); + tst_ReplaceOne("bb" , "b" , "c" , "cb"); + tst_ReplaceOne("abcd" , "bc" , "" , "ad"); + tst_ReplaceOne("abcd" , "b" , "ee" , "aeecd"); + } void tst_ReplaceOne(String src, String find, String repl, String expd) {Tfds.Eq(expd, String_.new_utf8_(Bry_.Replace_one(Bry_.new_utf8_(src), Bry_.new_utf8_(find), Bry_.new_utf8_(repl))));} + @Test public void XtoStrBytesByInt() { + tst_XtoStrBytesByInt(0, 0); + tst_XtoStrBytesByInt(9, 9); + tst_XtoStrBytesByInt(10, 1, 0); + tst_XtoStrBytesByInt(321, 3, 2, 1); + tst_XtoStrBytesByInt(-321, Bry_.Byte_NegSign, 3, 2, 1); + tst_XtoStrBytesByInt(Int_.MaxValue, 2,1,4,7,4,8,3,6,4,7); + } + void tst_XtoStrBytesByInt(int val, int... expdAryAsInt) { + byte[] expd = new byte[expdAryAsInt.length]; + for (int i = 0; i < expd.length; i++) { + int expdInt = expdAryAsInt[i]; + expd[i] = expdInt == Bry_.Byte_NegSign ? Bry_.Byte_NegSign : Bry_.XtoStrByte(expdAryAsInt[i]); + } + Tfds.Eq_ary(expd, Bry_.XtoStrBytesByInt(val, Int_.DigitCount(val))); + } + @Test public void HasAtEnd() { + tst_HasAtEnd("a|bcd|e", "d" , 2, 5, true); // y_basic + tst_HasAtEnd("a|bcd|e", "bcd" , 2, 5, true); // y_many + tst_HasAtEnd("a|bcd|e", "|bcd" , 2, 5, false); // n_long + tst_HasAtEnd("a|bcd|e", "|bc" , 2, 5, false); // n_pos + tst_HasAtEnd("abc", "bc", true); // y + tst_HasAtEnd("abc", "bd", false); // n + tst_HasAtEnd("a", "ab", false); // exceeds_len + } + void tst_HasAtEnd(String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.HasAtEnd(Bry_.new_utf8_(src), Bry_.new_utf8_(find), bgn, end));} + void tst_HasAtEnd(String src, String find, boolean expd) {Tfds.Eq(expd, Bry_.HasAtEnd(Bry_.new_utf8_(src), Bry_.new_utf8_(find)));} + @Test public void HasAtBgn() { + tst_HasAtBgn("y_basic" , "a|bcd|e", "b" , 2, 5, true); + tst_HasAtBgn("y_many" , "a|bcd|e", "bcd" , 2, 5, true); + tst_HasAtBgn("n_long" , "a|bcd|e", "bcde" , 2, 5, false); + tst_HasAtBgn("n_pos" , "a|bcd|e", "|bc" , 2, 5, false); + } void tst_HasAtBgn(String tst, String src, String find, int bgn, int end, boolean expd) {Tfds.Eq(expd, Bry_.HasAtBgn(Bry_.new_utf8_(src), Bry_.new_utf8_(find), bgn, end), tst);} + @Test public void Match() { + tst_Match("abc", 0, "abc", true); + tst_Match("abc", 2, "c", true); + tst_Match("abc", 0, "cde", false); + tst_Match("abc", 2, "abc", false); // bounds check + tst_Match("abc", 0, "abcd", false); + tst_Match("a" , 0, "", false); + tst_Match("" , 0, "a", false); + tst_Match("" , 0, "", true); + tst_Match("ab", 0, "a", false); // FIX: "ab" should not match "a" b/c .length is different + } void tst_Match(String src, int srcPos, String find, boolean expd) {Tfds.Eq(expd, Bry_.Match(Bry_.new_utf8_(src), srcPos, Bry_.new_utf8_(find)));} + @Test public void ReadCsvStr() { + tst_ReadCsvStr("a|" , "a"); + tst_ReadCsvStr("|a|", 1 , "a"); + Int_obj_ref bgn = Int_obj_ref.zero_(); tst_ReadCsvStr("a|b|c|", bgn, "a"); tst_ReadCsvStr("a|b|c|", bgn, "b"); tst_ReadCsvStr("a|b|c|", bgn, "c"); + tst_ReadCsvStr("|", ""); + tst_ReadCsvStr_err("a"); + + tst_ReadCsvStr("'a'|" , "a"); + tst_ReadCsvStr("'a''b'|" , "a'b"); + tst_ReadCsvStr("'a|b'|" , "a|b"); + tst_ReadCsvStr("''|", ""); + tst_ReadCsvStr_err("''"); + tst_ReadCsvStr_err("'a'b'"); + tst_ReadCsvStr_err("'a"); + tst_ReadCsvStr_err("'a|"); + tst_ReadCsvStr_err("'a'"); + } + @Test public void XtoIntBy4Bytes() { // test len=1, 2, 3, 4 + tst_XtoIntBy4Bytes(32, (byte)32); // space + tst_XtoIntBy4Bytes(8707, (byte)34, (byte)3); // ∃ + tst_XtoIntBy4Bytes(6382179, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c); + tst_XtoIntBy4Bytes(1633837924, Byte_ascii.Ltr_a, Byte_ascii.Ltr_b, Byte_ascii.Ltr_c, Byte_ascii.Ltr_d); + } + @Test public void XtoInt() { + tst_XtoInt("1", 1); + tst_XtoInt("123", 123); + tst_XtoInt("a", Int_.MinValue, Int_.MinValue); + tst_XtoInt("-1", Int_.MinValue, -1); + tst_XtoInt("-123", Int_.MinValue, -123); + tst_XtoInt("123-1", Int_.MinValue, Int_.MinValue); + tst_XtoInt("+123", Int_.MinValue, 123); + tst_XtoInt("", -1); + } + void tst_XtoInt(String val, int expd) {tst_XtoInt(val, -1, expd);} + void tst_XtoInt(String val, int or, int expd) {Tfds.Eq(expd, Bry_.X_to_int_or(Bry_.new_utf8_(val), or));} + void tst_XtoIntBy4Bytes(int expd, byte... ary) {Tfds.Eq(expd, Bry_.XtoIntBy4Bytes(ary), "XtoInt"); Tfds.Eq_ary(ary, Bry_.XbyInt(expd), "XbyInt");} + void tst_ReadCsvStr(String raw, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.zero_() , expd);} + void tst_ReadCsvStr(String raw, int bgn, String expd) {tst_ReadCsvStr(raw, Int_obj_ref.new_(bgn), expd);} + void tst_ReadCsvStr(String raw, Int_obj_ref bgnRef, String expd) { + int bgn = bgnRef.Val(); + boolean rawHasQuotes = String_.CharAt(raw, bgn) == '\''; + String actl = String_.Replace(Bry_.ReadCsvStr(Bry_.new_utf8_(String_.Replace(raw, "'", "\"")), bgnRef, (byte)'|'), "\"", "'"); + Tfds.Eq(expd, actl, "rv"); + if (rawHasQuotes) { + int quoteAdj = String_.Count(actl, "'"); + Tfds.Eq(bgn + 1 + String_.Len(actl) + 2 + quoteAdj, bgnRef.Val(), "pos_quote"); // +1=lkp.Len; +2=bgn/end quotes + } + else + Tfds.Eq(bgn + 1 + String_.Len(actl), bgnRef.Val(), "pos"); // +1=lkp.Len + } + void tst_ReadCsvStr_err(String raw) { + try {Bry_.ReadCsvStr(Bry_.new_utf8_(String_.Replace(raw, "'", "\"")), Int_obj_ref.zero_(), (byte)'|');} + catch (Exception e) {Err_.Noop(e); return;} + Tfds.Fail_expdError(); + } + @Test public void ReadCsvDte() { + tst_ReadCsvDte("20110801 221435.987"); + } void tst_ReadCsvDte(String raw) {Tfds.Eq_date(DateAdp_.parse_fmt(raw, Bry_.Fmt_csvDte), Bry_.ReadCsvDte(Bry_.new_utf8_(raw + "|"), Int_obj_ref.zero_(), (byte)'|'));} + @Test public void ReadCsvInt() { + tst_ReadCsvInt("1234567890"); + } void tst_ReadCsvInt(String raw) {Tfds.Eq(Int_.parse_(raw), Bry_.ReadCsvInt(Bry_.new_utf8_(raw + "|"), Int_obj_ref.zero_(), (byte)'|'));} + @Test public void Trim() { + Trim_tst("a b c", 1, 4, "b"); + Trim_tst("a c", 1, 3, ""); + Trim_tst(" ", 0, 2, ""); + } void Trim_tst(String raw, int bgn, int end, String expd) {Tfds.Eq(expd, String_.new_utf8_(Bry_.Trim(Bry_.new_utf8_(raw), bgn, end)));} + @Test public void X_to_int_lax() { + tst_X_to_int_lax("12a", 12); + tst_X_to_int_lax("1", 1); + tst_X_to_int_lax("123", 123); + tst_X_to_int_lax("a", 0); + tst_X_to_int_lax("-1", -1); + } + private void tst_X_to_int_lax(String val, int expd) {Tfds.Eq(expd, Bry_.X_to_int_or_lax(Bry_.new_utf8_(val), 0, String_.Len(val), 0));} + @Test public void X_to_int_or_trim() { + tst_X_to_int_trim("123 " , 123); + tst_X_to_int_trim(" 123" , 123); + tst_X_to_int_trim(" 123 " , 123); + tst_X_to_int_trim(" 1 3 " , -1); + } + private void tst_X_to_int_trim(String val, int expd) {Tfds.Eq(expd, Bry_.X_to_int_or_trim(Bry_.new_utf8_(val), 0, String_.Len(val), -1));} + @Test public void Compare() { + tst_Compare("abcde", 0, 1, "abcde", 0, 1, CompareAble_.Same); + tst_Compare("abcde", 0, 1, "abcde", 1, 2, CompareAble_.Less); + tst_Compare("abcde", 1, 2, "abcde", 0, 1, CompareAble_.More); + tst_Compare("abcde", 0, 1, "abcde", 0, 2, CompareAble_.Less); + tst_Compare("abcde", 0, 2, "abcde", 0, 1, CompareAble_.More); + tst_Compare("abcde", 2, 3, "abçde", 2, 3, CompareAble_.Less); + } void tst_Compare(String lhs, int lhs_bgn, int lhs_end, String rhs, int rhs_bgn, int rhs_end, int expd) {Tfds.Eq(expd, Bry_.Compare(Bry_.new_utf8_(lhs), lhs_bgn, lhs_end, Bry_.new_utf8_(rhs), rhs_bgn, rhs_end));} + @Test public void Increment_last() { + tst_IncrementLast(ary_(0), ary_(1)); + tst_IncrementLast(ary_(0, 255), ary_(1, 0)); + tst_IncrementLast(ary_(104, 111, 112, 101), ary_(104, 111, 112, 102)); + } + byte[] ary_(int... ary) { + byte[] rv = new byte[ary.length]; + for (int i = 0; i < ary.length; i++) + rv[i] = Byte_.int_(ary[i]); + return rv; + } + void tst_IncrementLast(byte[] ary, byte[] expd) {Tfds.Eq_ary(expd, Bry_.Increment_last(Bry_.Copy(ary)));} + @Test public void Split() { + tst_Split("a|b|c" , Byte_ascii.Pipe, "a", "b", "c"); + tst_Split("a|b|c|" , Byte_ascii.Pipe, "a", "b", "c"); + tst_Split("|" , Byte_ascii.Pipe, ""); + tst_Split("" , Byte_ascii.Pipe); + } + void tst_Split(String raw_str, byte dlm, String... expd) { + byte[][] actl_bry = Bry_.Split(Bry_.new_ascii_(raw_str), dlm); + Tfds.Eq_ary_str(expd, String_.Ary(actl_bry)); + } + @Test public void Replace_between() { + tst_Replace_between("a[0]b" , "[", "]", "0", "a0b"); + tst_Replace_between("a[0]b[1]c" , "[", "]", "0", "a0b0c"); + tst_Replace_between("a[0b" , "[", "]", "0", "a[0b"); + } public void tst_Replace_between(String src, String bgn, String end, String repl, String expd) {Tfds.Eq(expd, String_.new_ascii_(Bry_.Replace_between(Bry_.new_ascii_(src), Bry_.new_ascii_(bgn), Bry_.new_ascii_(end), Bry_.new_ascii_(repl))));} + @Test public void Replace() { + Bry_bfr tmp_bfr = Bry_bfr.new_(); + tst_Replace(tmp_bfr, "a0b" , "0", "00", "a00b"); // 1 -> 1 + tst_Replace(tmp_bfr, "a0b0c" , "0", "00", "a00b00c"); // 1 -> 2 + tst_Replace(tmp_bfr, "a00b00c" , "00", "0", "a0b0c"); // 2 -> 1 + tst_Replace(tmp_bfr, "a0b0" , "0", "00", "a00b00"); // 1 -> 2; EOS + tst_Replace(tmp_bfr, "a00b00" , "00", "0", "a0b0"); // 2 -> 1; EOS + tst_Replace(tmp_bfr, "a0b0" , "1", "2", "a0b0"); // no match + tst_Replace(tmp_bfr, "a0b0" , "b1", "b2", "a0b0"); // false match; EOS + } + public void tst_Replace(Bry_bfr tmp_bfr, String src, String bgn, String repl, String expd) { + Tfds.Eq(expd, String_.new_ascii_(Bry_.Replace(tmp_bfr, Bry_.new_ascii_(src), Bry_.new_ascii_(bgn), Bry_.new_ascii_(repl)))); + } + @Test public void Split_bry() { + Split_bry_tst("a|b|c|" , "|" , String_.Ary("a", "b", "c")); + Split_bry_tst("a|" , "|" , String_.Ary("a")); + } + void Split_bry_tst(String src, String dlm, String[] expd) { + String[] actl = String_.Ary(Bry_.Split(Bry_.new_ascii_(src), Bry_.new_ascii_(dlm))); + Tfds.Eq_ary_str(expd, actl); + } + @Test public void Split_lines() { + Tst_split_lines("a\nb" , "a", "b"); // basic + Tst_split_lines("a\nb\n" , "a", "b"); // do not create empty trailing lines + Tst_split_lines("a\r\nb" , "a", "b"); // crlf + Tst_split_lines("a\rb" , "a", "b"); // cr only + } + void Tst_split_lines(String src, String... expd) { + Tfds.Eq_ary(expd, New_ary(Bry_.Split_lines(Bry_.new_ascii_(src)))); + } + String[] New_ary(byte[][] lines) { + int len = lines.length; + String[] rv = new String[len]; + for (int i = 0; i < len; i++) + rv[i] = String_.new_utf8_(lines[i]); + return rv; + } + @Test public void Add_w_dlm() { + Tst_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", "b", "c") , "a|b|c"); // basic + Tst_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a") , "a"); // one item + Tst_add_w_dlm(Byte_ascii.Pipe, String_.Ary("a", null, "c") , "a||c"); // null + } + void Tst_add_w_dlm(byte dlm, String[] itms, String expd) { + byte[] actl = Bry_.Add_w_dlm(dlm, Bry_.Ary(itms)); + Tfds.Eq(expd, String_.new_ascii_(actl)); + } + @Test public void Match_bwd_any() { + Tst_match_bwd_any("abc", 2, 0, "c", true); + Tst_match_bwd_any("abc", 2, 0, "b", false); + Tst_match_bwd_any("abc", 2, 0, "bc", true); + Tst_match_bwd_any("abc", 2, 0, "abc", true); + Tst_match_bwd_any("abc", 2, 0, "zabc", false); + Tst_match_bwd_any("abc", 1, 0, "ab", true); + } + void Tst_match_bwd_any(String src, int src_end, int src_bgn, String find, boolean expd) { + Tfds.Eq(expd, Bry_.Match_bwd_any(Bry_.new_ascii_(src), src_end, src_bgn, Bry_.new_ascii_(find))); + } + private ByteAry_fxt fxt = new ByteAry_fxt(); + @Test public void Trim_end() { + fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.one + fxt.Test_trim_end("a " , Byte_ascii.Space, "a"); // trim.many + fxt.Test_trim_end("a" , Byte_ascii.Space, "a"); // trim.none + fxt.Test_trim_end("" , Byte_ascii.Space, ""); // empty + } + @Test public void XtoByteAry() { + fxt.Test_new_utf8_("a" , Bry_.ints_(97)); + fxt.Test_new_utf8_("a b" , Bry_.ints_(97, 32, 98)); + fxt.Test_new_utf8_("©" , Bry_.ints_(194, 169)); + } +} +class ByteAry_fxt { + public void Test_trim_end(String raw, byte trim, String expd) { + byte[] raw_bry = Bry_.new_ascii_(raw); + Tfds.Eq(expd, String_.new_utf8_(Bry_.Trim_end(raw_bry, trim, raw_bry.length))); + } + public void Test_new_utf8_(String raw, byte[] expd_bry) { + Tfds.Eq_ary(expd_bry, Bry_.new_utf8_(raw)); + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_bfr.java b/100_core/src_110_primitive/gplx/Bry_bfr.java new file mode 100644 index 000000000..cb81215de --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_bfr.java @@ -0,0 +1,458 @@ +/* +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; +public class Bry_bfr { + public byte[] Bfr() {return bfr;} private byte[] bfr; + @gplx.Internal protected int Bfr_max() {return bfr_max;} private int bfr_max; + public int Len() {return bfr_len;} private int bfr_len; + public boolean Len_eq_0() {return bfr_len == 0;} + public boolean Len_gt_0() {return bfr_len > 0;} + public void Bfr_init(byte[] bfr, int bfr_len) { + this.bfr = bfr; + this.bfr_len = bfr_len; + this.bfr_max = bfr.length; // NOTE: must sync bfr_max, else will fail later during add; bfr will think bfr has .length of bfr_max, when it actually has .length of bfr_len; DATE:2014-03-09 + } + @gplx.Internal protected int Mkr_itm() {return mkr_itm;} private int mkr_itm = -1; + @gplx.Internal protected Bry_bfr_mkr_mgr Mkr_mgr() {return mkr_mgr;} Bry_bfr_mkr_mgr mkr_mgr; + @gplx.Internal protected Bry_bfr Mkr_(Bry_bfr_mkr_mgr mkr_mgr, int itm) {this.mkr_mgr = mkr_mgr; this.mkr_itm = itm; return this;} + public Bry_bfr Mkr_rls() {mkr_mgr.Rls(this); return this;} + private void Mkr_clear() { + if (mkr_mgr != null) mkr_mgr.Rls(this); + mkr_mgr = null; + mkr_itm = -1; + } + private Bry_bfr Reset_(int v) {reset = v; return this;} private int reset; + public Bry_bfr Reset_if_gt(int limit) { + if (bfr_max > limit) { + this.bfr_max = limit; + this.bfr = new byte[limit]; + } + bfr_len = 0; + return this; + } + public Bry_bfr Clear() {bfr_len = 0; return this;} + public Bry_bfr ClearAndReset() {bfr_len = 0; if (reset > 0) Reset_if_gt(reset); return this;} + public Bry_bfr Add_safe(byte[] val) {return val == null ? this : Add(val);} + public Bry_bfr Add(byte[] val) { + int val_len = val.length; + if (bfr_len + val_len > bfr_max) Resize((bfr_max + val_len) * 2); + Bry_.Copy_by_pos(val, 0, val_len, bfr, bfr_len); + // Array_.CopyTo(val, 0, bfr, bfr_len, val_len); + bfr_len += val_len; + return this; + } + public Bry_bfr Add_mid(byte[] val, int bgn, int end) { + int len = end - bgn; + if (len < 0) throw Err_.new_fmt_("negative len; bgn={0} end={1} excerpt={2}", bgn, end, String_.new_utf8_len_safe_(val, bgn, bgn + 16)); // NOTE: check for invalid end < bgn, else difficult to debug errors later; DATE:2014-05-11 + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + Bry_.Copy_by_pos(val, bgn, end, bfr, bfr_len); + // Array_.CopyTo(val, bgn, bfr, bfr_len, len); + bfr_len += len; + return this; + } + public Bry_bfr Add_bfr(Bry_bfr src) { + int len = src.bfr_len; + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + Bry_.Copy_by_pos(src.bfr, 0, len, bfr, bfr_len); + // Array_.CopyTo(src.bfr, 0, bfr, bfr_len, len); + bfr_len += len; + return this; + } + public Bry_bfr Add_bfr_and_clear(Bry_bfr src) { + Add_bfr(src); + src.ClearAndReset(); + return this; + } + public Bry_bfr Add_bfr_trim_and_clear(Bry_bfr src, boolean trim_bgn, boolean trim_end) {return Add_bfr_trim_and_clear(src, trim_bgn, trim_end, Bry_.Trim_ary_ws);} + public Bry_bfr Add_bfr_trim_and_clear(Bry_bfr src, boolean trim_bgn, boolean trim_end, byte[] trim_ary) { + int src_len = src.bfr_len; + if (bfr_len + src_len > bfr_max) Resize((bfr_max + src_len) * 2); + byte[] src_bry = src.Bfr(); + int src_bgn = 0, src_end = src_len; + boolean all_ws = true; + if (trim_bgn) { + for (int i = 0; i < src_len; i++) { + byte b = src_bry[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Nil) { + src_bgn = i; + i = src_len; + all_ws = false; + } + } + if (all_ws) return this; + } + if (trim_end) { + for (int i = src_len - 1; i > -1; i--) { + byte b = src_bry[i]; + if (trim_ary[b & 0xFF] == Byte_ascii.Nil) { + src_end = i + 1; + i = -1; + all_ws = false; + } + } + if (all_ws) return this; + } + src_len = src_end - src_bgn; + Bry_.Copy_by_pos(src.bfr, src_bgn, src_end, bfr, bfr_len); + // Array_.CopyTo(src.bfr, src_bgn, bfr, bfr_len, src_len); + bfr_len += src_len; + src.Clear(); + return this; + } + public Bry_bfr Add_byte_eq() {return Add_byte(Byte_ascii.Eq);} + public Bry_bfr Add_byte_pipe() {return Add_byte(Byte_ascii.Pipe);} + public Bry_bfr Add_byte_apos() {return Add_byte(Byte_ascii.Apos);} + public Bry_bfr Add_byte_quote() {return Add_byte(Byte_ascii.Quote);} + public Bry_bfr Add_byte_space() {return Add_byte(Byte_ascii.Space);} + public Bry_bfr Add_byte_nl() {return Add_byte(Byte_ascii.NewLine);} + public Bry_bfr Add_byte(byte val) { + int newPos = bfr_len + 1; + if (newPos > bfr_max) Resize(bfr_len * 2); + bfr[bfr_len] = val; + bfr_len = newPos; + return this; + } + public Bry_bfr Add_byte_repeat(byte b, int len) { + if (bfr_len + len > bfr_max) Resize((bfr_max + len) * 2); + for (int i = 0; i < len; i++) + bfr[i + bfr_len] = b; + bfr_len += len; + return this; + } + public Bry_bfr Add_byte_if_not_last(byte b) { + if (bfr_len == 0 || (bfr_len > 0 && bfr[bfr_len - 1] == b)) return this; + this.Add_byte(b); + return this; + } + public Bry_bfr Add_utf8_int(int val) { + if (bfr_len + 4 > bfr_max) Resize((bfr_max + 4) * 2); + int utf8_len = gplx.intl.Utf16_.Encode_int(val, bfr, bfr_len); + bfr_len += utf8_len; + return this; + } + public Bry_bfr Add_bool(boolean v) {return Add(v ? Const_bool_true : Const_bool_false);} public static final byte[] Const_bool_true = Bry_.new_ascii_("true"), Const_bool_false = Bry_.new_ascii_("false"); + public Bry_bfr Add_int_bool(boolean v) {return Add_int_fixed(v ? 1 : 0, 1);} + public Bry_bfr Add_int_variable(int val) { + int log10 = Int_.Log10(val); + int slots = val > -1 ? log10 + 1 : log10 * -1 + 2; + return Add_int(val, log10, slots); + } + public Bry_bfr Add_int_pad_bgn(byte pad_byte, int str_len, int val) { + int digit_len = Int_.DigitCount(val); + int pad_len = str_len - digit_len; + if (pad_len > 0) // note that this skips pad_len == 0, as well as guarding against negative pad_len; EX: pad(" ", 3, 1234) -> "1234" + Add_byte_repeat(pad_byte, pad_len); + Add_int_fixed(val, digit_len); + return this; + } + public Bry_bfr Add_int_fixed(int val, int digits) {return Add_int(val, Int_.Log10(val), digits);} + public Bry_bfr Add_int(int val, int valLog, int arySlots) { + int aryBgn = bfr_len, aryEnd = bfr_len + arySlots; + if (aryEnd > bfr_max) Resize((aryEnd) * 2); + if (val < 0) { + bfr[aryBgn++] = Byte_ascii.Dash; + val *= -1; // make positive + valLog *= -1; // valLog will be negative; make positive + arySlots -= 1; // reduce slot by 1 + } + if (valLog >= arySlots) { + val %= Int_.Log10Ary[arySlots]; + } + for (int i = 0; i < arySlots; i++) { + int logIdx = arySlots - i - 1; + int div = logIdx < Int_.Log10AryLen ? Int_.Log10Ary[logIdx] : Int_.MaxValue; + bfr[aryBgn + i] = (byte)((val / div) + 48); + val %= div; + } + bfr_len = aryEnd; + return this; + } + public Bry_bfr Add_long_variable(long v) {int digitCount = Long_.DigitCount(v); return Add_long(v, digitCount, digitCount);} + public Bry_bfr Add_long_fixed(long val, int digits) {return Add_long(val, Long_.DigitCount(val), digits);} + protected Bry_bfr Add_long(long val, int digitCount, int arySlots) { + int aryBgn = bfr_len, aryEnd = bfr_len + arySlots; + if (aryEnd > bfr_max) Resize((aryEnd) * 2); + if (val < 0) { + bfr[aryBgn++] = Byte_ascii.Dash; + val *= -1; // make positive + arySlots -= 1; // reduce slot by 1 + } + if (digitCount >= arySlots) { + val %= Long_.Log10Ary[arySlots]; + } + for (int i = 0; i < arySlots; i++) { + int logIdx = arySlots - i - 1; + long div = logIdx < Long_.Log10Ary_len ? Long_.Log10Ary[logIdx] : Long_.MaxValue; + bfr[aryBgn + i] = (byte)((val / div) + 48); + val %= div; + } + bfr_len = aryEnd; + return this; + } + public Bry_bfr Add_bry_comma(byte[] v) {return Add_bry(Byte_ascii.Comma, v);} + public Bry_bfr Add_bry(byte dlm, byte[] v) { + if (v == null) return this; + int v_len = v.length; + for (int i = 0; i < v_len; i++) { + if (i != 0) this.Add_byte(dlm); + this.Add_int_variable(v[i]); + } + return this; + } + public Bry_bfr Add_bry_escape_by_doubling(byte quote_byte, byte[] val) {return Add_bry_escape(quote_byte, quote_byte, val, 0, val.length);} + public Bry_bfr Add_bry_escape(byte quote_byte, byte escape_byte, byte[] val, int bgn, int end) { + boolean clean = true; + for (int i = bgn; i < end; i++) { + byte b = val[i]; + if (clean) { + if (b == quote_byte) { + clean = false; + this.Add_mid(val, bgn, i); + this.Add_byte(escape_byte); + this.Add_byte(quote_byte); + } + else {} + } + else { + if (b == quote_byte) + this.Add_byte(escape_byte); + this.Add_byte(b); + } + } + if (clean) + Add(val); + return this; + } + public Bry_bfr Add_str(String v) {return Add(Bry_.new_utf8_(v));} + public Bry_bfr Add_float(float f) {Add_str(Float_.XtoStr(f)); return this;} + public Bry_bfr Add_double(double v) {Add_str(Double_.XtoStr(v)); return this;} + public Bry_bfr Add_dte(DateAdp val) {return Add_dte_segs(val.Year(), val.Month(),val.Day(), val.Hour(), val.Minute(), val.Second(), val.Frac());} + public Bry_bfr Add_dte_segs(int y, int M, int d, int H, int m, int s, int f) { // yyyyMMdd HHmmss.fff + if (bfr_len + 19 > bfr_max) Resize((bfr_len + 19) * 2); + bfr[bfr_len + 0] = (byte)((y / 1000) + Bry_.Ascii_zero); y %= 1000; + bfr[bfr_len + 1] = (byte)((y / 100) + Bry_.Ascii_zero); y %= 100; + bfr[bfr_len + 2] = (byte)((y / 10) + Bry_.Ascii_zero); y %= 10; + bfr[bfr_len + 3] = (byte)( y + Bry_.Ascii_zero); + bfr[bfr_len + 4] = (byte)((M / 10) + Bry_.Ascii_zero); M %= 10; + bfr[bfr_len + 5] = (byte)( M + Bry_.Ascii_zero); + bfr[bfr_len + 6] = (byte)((d / 10) + Bry_.Ascii_zero); d %= 10; + bfr[bfr_len + 7] = (byte)( d + Bry_.Ascii_zero); + bfr[bfr_len + 8] = Byte_ascii.Space; + bfr[bfr_len + 9] = (byte)((H / 10) + Bry_.Ascii_zero); H %= 10; + bfr[bfr_len + 10] = (byte)( H + Bry_.Ascii_zero); + bfr[bfr_len + 11] = (byte)((m / 10) + Bry_.Ascii_zero); m %= 10; + bfr[bfr_len + 12] = (byte)( m + Bry_.Ascii_zero); + bfr[bfr_len + 13] = (byte)((s / 10) + Bry_.Ascii_zero); s %= 10; + bfr[bfr_len + 14] = (byte)( s + Bry_.Ascii_zero); + bfr[bfr_len + 15] = Byte_ascii.Dot; + bfr[bfr_len + 16] = (byte)((f / 100) + Bry_.Ascii_zero); f %= 100; + bfr[bfr_len + 17] = (byte)((f / 10) + Bry_.Ascii_zero); f %= 10; + bfr[bfr_len + 18] = (byte)( f + Bry_.Ascii_zero); + bfr_len += 19; + return this; + } + public Bry_bfr Add_dte_utc(int y, int M, int d, int H, int m, int s, int f) { // yyyy-MM-ddTHH:mm:ssZ + if (bfr_len + 20 > bfr_max) Resize((bfr_len + 20) * 2); + bfr[bfr_len + 0] = (byte)((y / 1000) + Bry_.Ascii_zero); y %= 1000; + bfr[bfr_len + 1] = (byte)((y / 100) + Bry_.Ascii_zero); y %= 100; + bfr[bfr_len + 2] = (byte)((y / 10) + Bry_.Ascii_zero); y %= 10; + bfr[bfr_len + 3] = (byte)( y + Bry_.Ascii_zero); + bfr[bfr_len + 4] = Byte_ascii.Dash; + bfr[bfr_len + 5] = (byte)((M / 10) + Bry_.Ascii_zero); M %= 10; + bfr[bfr_len + 6] = (byte)( M + Bry_.Ascii_zero); + bfr[bfr_len + 7] = Byte_ascii.Dash; + bfr[bfr_len + 8] = (byte)((d / 10) + Bry_.Ascii_zero); d %= 10; + bfr[bfr_len + 9] = (byte)( d + Bry_.Ascii_zero); + bfr[bfr_len + 10] = Byte_ascii.Ltr_T; + bfr[bfr_len + 11] = (byte)((H / 10) + Bry_.Ascii_zero); H %= 10; + bfr[bfr_len + 12] = (byte)( H + Bry_.Ascii_zero); + bfr[bfr_len + 13] = Byte_ascii.Colon; + bfr[bfr_len + 14] = (byte)((m / 10) + Bry_.Ascii_zero); m %= 10; + bfr[bfr_len + 15] = (byte)( m + Bry_.Ascii_zero); + bfr[bfr_len + 16] = Byte_ascii.Colon; + bfr[bfr_len + 17] = (byte)((s / 10) + Bry_.Ascii_zero); s %= 10; + bfr[bfr_len + 18] = (byte)( s + Bry_.Ascii_zero); + bfr[bfr_len + 19] = Byte_ascii.Ltr_Z; + bfr_len += 20; + return this; + } + public Bry_bfr Add_swap_ws(byte[] src) {return Add_swap_ws(src, 0, src.length);} + public Bry_bfr Add_swap_ws(byte[] src, int bgn, int end) { + int len = end - bgn; + if (bfr_len + (len * 2) > bfr_max) Resize((bfr_max + (len * 2)) * 2); + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.NewLine: bfr[bfr_len] = Byte_ascii.Backslash; bfr[bfr_len + 1] = Byte_ascii.Ltr_n; bfr_len += 2; break; + case Byte_ascii.Tab: bfr[bfr_len] = Byte_ascii.Backslash; bfr[bfr_len + 1] = Byte_ascii.Ltr_t; bfr_len += 2; break; + case Byte_ascii.Backslash: bfr[bfr_len] = Byte_ascii.Backslash; bfr[bfr_len + 1] = Byte_ascii.Backslash; bfr_len += 2; break; + default: bfr[bfr_len] = b; ++bfr_len; break; + } + } + return this; + } + public Bry_bfr Add_str_pad_space_bgn(String v, int pad_max) {return Add_str_pad_space(v, pad_max, Bool_.N);} + public Bry_bfr Add_str_pad_space_end(String v, int pad_max) {return Add_str_pad_space(v, pad_max, Bool_.Y);} + Bry_bfr Add_str_pad_space(String v, int pad_max, boolean pad_end) { + byte[] v_bry = Bry_.new_utf8_(v); + if (pad_end) Add(v_bry); + int pad_len = pad_max - v_bry.length; + if (pad_len > 0) + Add_byte_repeat(Byte_ascii.Space, pad_len); + if (!pad_end) Add(v_bry); + return this; + } + + public Bry_bfr Add_obj(Object o) { + if (o == null) return this; // treat null as empty String; + Class o_type = o.getClass(); + if (o_type == byte[].class) Add((byte[])o); + else if (o_type == Integer.class) Add_int_variable(Int_.cast_(o)); + else if (o_type == Byte.class) Add_byte(Byte_.cast_(o)); + else if (o_type == Long.class) Add_long_variable(Long_.cast_(o)); + else if (o_type == String.class) Add_str((String)o); + else if (o_type == Bry_bfr.class) Add_bfr((Bry_bfr)o); + else if (o_type == DateAdp.class) Add_dte((DateAdp)o); + else if (o_type == Io_url.class) Add(((Io_url)o).RawBry()); + else if (o_type == boolean.class) Add_yn(Bool_.cast_(o)); + else if (o_type == Double.class) Add_double(Double_.cast_(o)); + else if (o_type == Float.class) Add_float(Float_.cast_(o)); + else ((Bry_fmtr_arg)o).XferAry(this, 0); + return this; + } + public Bry_bfr Add_yn(boolean v) {Add_byte(v ? Byte_ascii.Ltr_y : Byte_ascii.Ltr_n); return this;} + public Bry_bfr Add_base85_len_5(int v) {return Add_base85(v, 5);} + public Bry_bfr Add_base85(int v, int pad) { + int new_len = bfr_len + pad; + if (new_len > bfr_max) Resize((new_len) * 2); + Base85_utl.XtoStrByAry(v, bfr, bfr_len, pad); + bfr_len = new_len; + return this; + } + public boolean Match_end_byt(byte b) {return bfr_len == 0 ? false : bfr[bfr_len - 1] == b;} + public boolean Match_end_byt_nl_or_bos() {return bfr_len == 0 ? true : bfr[bfr_len - 1] == Byte_ascii.NewLine;} + public boolean Match_end_ary(byte[] ary) {return Bry_.Match(bfr, bfr_len - ary.length, bfr_len, ary);} + public Bry_bfr Insert_at(int add_pos, byte[] add_bry) {return Insert_at(add_pos, add_bry, 0, add_bry.length);} + public Bry_bfr Insert_at(int add_pos, byte[] add_bry, int add_bgn, int add_end) { + int add_len = add_end - add_bgn; + int new_max = bfr_max + add_len; + byte[] new_bfr = new byte[new_max]; + if (add_pos > 0) + Bry_.Copy_by_pos (bfr , 0, add_pos, new_bfr, 0); + Bry_.Copy_by_pos (add_bry, add_bgn, add_end, new_bfr, add_pos); + Bry_.Copy_by_pos (bfr , add_pos, bfr_len, new_bfr, add_pos + add_len); + bfr = new_bfr; + bfr_len += add_len; + bfr_max = new_max; + return this; + } + public Bry_bfr Delete_rng_to_bgn(int pos) {return Delete_rng(0, pos);} + public Bry_bfr Delete_rng_to_end(int pos) {return Delete_rng(pos, bfr_len);} + public Bry_bfr Delete_rng(int rng_bgn, int rng_end) { + int rng_len = rng_end - rng_bgn; + Bry_.Copy_by_pos(bfr, rng_end, bfr_len, bfr, rng_bgn); + bfr_len -= rng_len; + return this; + } + public Bry_bfr Del_by_1() { + bfr_len -= 1; bfr[bfr_len] = 0; return this; + } + public Bry_bfr Del_by(int count) { + int new_len = bfr_len - count; + if (new_len > -1) bfr_len = new_len; + return this; + } + public Bry_bfr Concat_skip_empty(byte[] dlm, byte[]... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) { + byte[] itm = ary[i]; + boolean itm_has_bytes = Bry_.Len_gt_0(itm); + if ( i != 0 + && itm_has_bytes + && bfr_len > 0 + ) + this.Add(dlm); + if (itm_has_bytes) + this.Add(itm); + } + return this; + } + public boolean Eq(byte b) {return bfr_len == 1 && bfr[0] == b;} + public byte[] XtoAry() {return bfr_len == 0 ? Bry_.Empty : Bry_.Mid(bfr, 0, bfr_len);} + public byte[] XtoAryAndReset(int v) { + byte[] rv = XtoAry(); + this.Clear().Reset_if_gt(v); + return rv; + } + public byte[] XtoAryAndClearAndTrim() {return XtoAryAndClearAndTrim(true, true, Bry_.Trim_ary_ws);} + public byte[] XtoAryAndClearAndTrim(boolean trim_bgn, boolean trim_end, byte[] trim_bry) { + byte[] rv = Bry_.Trim(bfr, 0, bfr_len, trim_bgn, trim_end, trim_bry); + this.Clear(); + return rv; + } + public byte[] XtoAryAndClear() { + byte[] rv = XtoAry(); + this.Clear(); + if (reset > 0) Reset_if_gt(reset); + return rv; + } + public String XtoStr() {return String_.new_utf8_(XtoAry());} + public String XtoStrByPos(int bgn, int end) {return String_.new_utf8_(XtoAry(), bgn, end);} + public String XtoStrAndClear() {return String_.new_utf8_(XtoAryAndClear());} + public String XtoStrAndClearAndTrim() {return String_.new_utf8_(XtoAryAndClearAndTrim());} + public String XtoStrAndReset(int v) {return String_.new_utf8_(XtoAryAndReset(v));} + public int XtoIntAndClear(int or) {int rv = XtoInt(or); this.Clear(); return rv;} + public int XtoInt(int or) { + switch (bfr_len) { + case 0: return or; + case 1: { + byte b = bfr[0]; + return Byte_ascii.Is_num(b) ? b - Byte_ascii.Num_0 : or; + } + default: + long rv = 0, mult = 1; + for (int i = bfr_len - 1; i > -1; i--) { + byte b = bfr[i]; + if (!Byte_ascii.Is_num(b)) return or; + long dif = (b - Byte_ascii.Num_0 ) * mult; + long new_val = rv + dif; + if (new_val > Int_.MaxValue) return or; // if number is > 2^32 consider error (int overflow); return or; DATE:2014-06-10 + rv = new_val; + mult *= 10; + } + return (int)rv; + } + } + public void Rls() { + bfr = null; + Mkr_clear(); + } + @Override public int hashCode() {return Bry_obj_ref.CalcHashCode(bfr, 0, bfr_len);} + @Override public boolean equals(Object obj) {return obj == null ? false : Bry_.Match(bfr, 0, bfr_len, ((Bry_obj_ref)obj).Val());} // NOTE: strange, but null check needed; throws null error; PAGE:c:File:Eug�ne_Delacroix_-_La_libert�_guidant_le_peuple.jpg + public void Resize(int v) { + bfr_max = v; + bfr = Bry_.Resize(bfr, 0, v); + } + public static Bry_bfr new_() {return new Bry_bfr(16);} + public static Bry_bfr new_(int v) {return new Bry_bfr(v);} + public static Bry_bfr reset_(int v) {return new Bry_bfr(16).Reset_(v);} // PERF: set initial size to 16, not reset val; allows for faster "startup"; DATE:2014-06-14 + Bry_bfr(int bfr_max) { + this.bfr_max = bfr_max; + this.bfr = new byte[bfr_max]; + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_bfr_mkr.java b/100_core/src_110_primitive/gplx/Bry_bfr_mkr.java new file mode 100644 index 000000000..12876d43e --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_bfr_mkr.java @@ -0,0 +1,138 @@ +/* +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; +public class Bry_bfr_mkr { + public static final byte Tid_b128 = 0, Tid_b512 = 1, Tid_k004 = 2, Tid_m001 = 3; + private Bry_bfr_mkr_mgr mkr_b128 = new Bry_bfr_mkr_mgr(Tid_b128, 128), mkr_b512 = new Bry_bfr_mkr_mgr(Tid_b512, 512), mkr_k004 = new Bry_bfr_mkr_mgr(Tid_k004, 4 * Io_mgr.Len_kb), mkr_m001 = new Bry_bfr_mkr_mgr(Tid_m001, 1 * Io_mgr.Len_mb); + public Bry_bfr Get_b128() {return mkr_b128.Get();} + public Bry_bfr Get_b512() {return mkr_b512.Get();} + public Bry_bfr Get_k004() {return mkr_k004.Get();} + public Bry_bfr Get_m001() {return mkr_m001.Get();} + public void Rls(Bry_bfr v) { + v.Mkr_mgr().Rls(v); + } + public void Reset_if_gt(int v) { + for (byte i = Tid_b128; i <= Tid_m001; i++) + mkr(i).Reset_if_gt(v); + } + public void Clear_fail_check() { + for (byte i = Tid_b128; i <= Tid_m001; i++) + mkr(i).Clear_fail_check(); + } + + public void Clear() { + for (byte i = Tid_b128; i <= Tid_m001; i++) + mkr(i).Clear(); + } + Bry_bfr_mkr_mgr mkr(byte tid) { + switch (tid) { + case Tid_b128: return mkr_b128; + case Tid_b512: return mkr_b512; + case Tid_k004: return mkr_k004; + case Tid_m001: return mkr_m001; + default: throw Err_.unhandled(tid); + } + } +} +class Bry_bfr_mkr_mgr { + public Bry_bfr_mkr_mgr(byte mgr_id, int reset) {this.mgr_id = mgr_id; this.reset = reset;} private int reset; + public byte Mgr_id() {return mgr_id;} private byte mgr_id; + private Bry_bfr[] ary = Ary_empty; private int nxt_idx = 0, ary_max = 0; + private int[] free = Int_.Ary_empty; private int free_len = 0; + private Object thread_lock = new Object(); + public void Reset_if_gt(int v) { + this.Clear(); // TODO: for now, just call clear + } + public void Clear_fail_check() { + synchronized (thread_lock) { + for (int i = 0; i < ary_max; i++) { + Bry_bfr itm = ary[i]; + if (itm != null) { + if (itm.Mkr_mgr() != null) throw Err_.new_("failed to clear bfr: " + Int_.XtoStr(i)); + itm.Clear(); + } + ary[i] = null; + } + ary = Ary_empty; + free = Int_.Ary_empty; + free_len = 0; + nxt_idx = ary_max = 0; + } + } + public void Clear() { + synchronized (thread_lock) { + for (int i = 0; i < ary_max; i++) { + Bry_bfr itm = ary[i]; + if (itm != null) itm.Clear(); + ary[i] = null; + } + ary = Ary_empty; + free = Int_.Ary_empty; + free_len = 0; + nxt_idx = ary_max = 0; + } + } + public Bry_bfr[] Ary() {return ary;} + public int Nxt_idx() {return nxt_idx;} + public Bry_bfr Get() { + synchronized (thread_lock) { + Bry_bfr rv = null; + int rv_idx = -1; + if (free_len > 0) { + rv_idx = free[--free_len]; + rv = ary[rv_idx]; + } + else { + if (nxt_idx == ary_max) { + Expand(); + } + rv_idx = nxt_idx++; + rv = ary[rv_idx]; + if (rv == null) { + rv = Bry_bfr.reset_(reset); + ary[rv_idx] = rv; + } + } + rv.Mkr_(this, rv_idx); + return rv.Clear(); // NOTE: ALWAYS call Clear when doing Get. caller may forget to call Clear, and reused bfr may have leftover bytes. unit tests will not catch, and difficult to spot in app + } + } + private void Expand() { + int new_max = ary_max == 0 ? 2 : ary_max * 2; + Bry_bfr[] new_ary = new Bry_bfr[new_max]; + Array_.CopyTo(ary, 0, new_ary, 0, ary_max); + ary = new_ary; + ary_max = new_max; + int[] new_free = new int[ary_max]; + Array_.CopyTo(free, 0, new_free, 0, free_len); + free = new_free; + } + public void Rls(Bry_bfr v) { + synchronized (thread_lock) { + int idx = v.Mkr_itm(); + if (idx == -1) throw Err_mgr._.fmt_("gplx.Bry_bfr", "rls_failed", "rls called on bfr that was not created by factory"); + int new_ary_len = nxt_idx - 1; + if (idx == new_ary_len) + nxt_idx = new_ary_len; + else + free[free_len++] = idx; + v.Mkr_(null, -1); + } + } + public static final Bry_bfr[] Ary_empty = new Bry_bfr[0]; +} diff --git a/100_core/src_110_primitive/gplx/Bry_bfr_mkr_tst.java b/100_core/src_110_primitive/gplx/Bry_bfr_mkr_tst.java new file mode 100644 index 000000000..475ecfe28 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_bfr_mkr_tst.java @@ -0,0 +1,74 @@ +/* +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; +import org.junit.*; +public class Bry_bfr_mkr_tst { + Bry_bfr_mkr_fxt fxt = new Bry_bfr_mkr_fxt(); + @Before public void setup() {fxt.Clear();} + @Test public void Get_1() {fxt.Clear().Get().Tst_idxs(0);} + @Test public void Get_2() {fxt.Clear().Get().Get().Tst_idxs(0, 1);} + @Test public void Get_3() {fxt.Clear().Get().Get().Get().Tst_idxs(0, 1, 2);} + @Test public void Rls() { + fxt.Clear().Get().Rls(0).Tst_idxs(); + } + @Test public void Rls_skip_1() { + fxt.Clear().Get().Get().Rls(0).Tst_idxs(-1, 1); + fxt.Get().Tst_idxs(0, 1); + } + @Test public void Rls_skip_2_1() { + fxt.Clear().Get().Get().Get().Rls(1).Rls(0).Tst_idxs(-1, -1, 2); + fxt.Get().Tst_idxs(0, -1, 2); + fxt.Get().Tst_idxs(0, 1, 2); + fxt.Get().Tst_idxs(0, 1, 2, 3); + } + @Test public void Get_rls_get() { // PURPOSE: defect in which last rls failed b/c was not doing ++ if rv existed + fxt.Clear().Get().Rls(0).Get().Get().Rls(1).Rls(0).Tst_idxs(); + } + public static final int Int_null = -2; +} +class Bry_bfr_mkr_fxt { + Bry_bfr_mkr_mgr mkr; + public Bry_bfr_mkr_fxt Clear() { + if (mkr == null) { + mkr = new Bry_bfr_mkr_mgr(Byte_.Zero, 32); + } + mkr.Clear(); + return this; + } + public Bry_bfr_mkr_fxt Get() { + mkr.Get(); + return this; + } + public Bry_bfr_mkr_fxt Rls(int i) { + Bry_bfr bfr = mkr.Ary()[i]; + mkr.Rls(bfr); + return this; + } + public Bry_bfr_mkr_fxt Tst_idxs(int... expd) { + int actl_len = mkr.Nxt_idx(); + int[] actl = new int[actl_len]; + for (int i = 0; i < actl_len; i++) { + Bry_bfr bfr = mkr.Ary()[i]; + int actl_val = Bry_bfr_mkr_tst.Int_null; + if (bfr != null) actl_val = bfr.Mkr_itm(); + actl[i] = actl_val; + } + Tfds.Eq_ary(expd, actl); + return this; + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_bfr_tst.java b/100_core/src_110_primitive/gplx/Bry_bfr_tst.java new file mode 100644 index 000000000..510ac8f16 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_bfr_tst.java @@ -0,0 +1,227 @@ +/* +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; +import org.junit.*; +public class Bry_bfr_tst { + Bry_bfr bb = Bry_bfr.new_(16); + @Before public void setup() {bb.Clear();} private ByteAryBfr_fxt fxt = new ByteAryBfr_fxt(); + @Test public void AddByte() { + bb = Bry_bfr.new_(2); // NOTE: make sure auto-expands + tst_AddByte("a", "a", 2); + tst_AddByte("b", "ab", 2); + tst_AddByte("c", "abc", 4); + } + @Test public void AddAry() { // NOTE: make sure auto-expands + bb = Bry_bfr.new_(2); + tst_AddByte("abcd", "abcd", 12); + } + @Test public void Add_byte_repeat() { // NOTE: make sure auto-expands + bb = Bry_bfr.new_(2); + tst_Add_byte_repeat(Byte_ascii.Space, 12, String_.Repeat(" ", 12)); + } void tst_Add_byte_repeat(byte b, int len, String expd) {Tfds.Eq(expd, bb.Add_byte_repeat(b, len).XtoStrAndClear());} + void tst_AddByte(String s, String expdStr, int expdLen) { + if (String_.Len(s) == 1) + bb.Add_byte((byte)String_.CharAt(s, 0)); + else + bb.Add(Bry_.new_utf8_(s)); + Tfds.Eq(expdStr, String_.new_utf8_(bb.XtoAry())); + Tfds.Eq(expdLen, bb.Bfr_max()); + } + @Test public void Add_dte() { + tst_AddDte("20110801 221435.987"); + } + void tst_AddDte(String raw) { + bb.Add_dte(DateAdp_.parse_fmt(raw, Bry_.Fmt_csvDte)); + Tfds.Eq(raw, String_.new_utf8_(bb.XtoAry())); + } + @Test public void Add_int_variable() { + Add_int_variable(-1); + Add_int_variable(-12); + Add_int_variable(-1234); + Add_int_variable(2); + Add_int_variable(12); + Add_int_variable(1234); + Add_int_variable(123456789); + } + @Test public void Add_float() { + tst_Add_float(1 / 3); + tst_Add_float(-1 / 3); + } + void tst_Add_float(float v) { + bb.Add_float(v); + Tfds.Eq(v, Float_.parse_(String_.new_utf8_(bb.XtoAry()))); + } + void Add_int_variable(int val) { + bb.Clear(); + bb.Add_int_variable(val); + Tfds.Eq(val, Int_.parse_(String_.new_utf8_(bb.XtoAry()))); + } + @Test public void Add_int_fixed_len3() {tst_Add_int_fixed(123, 3, "123");} + @Test public void Add_int_fixed_pad_1() {tst_Add_int_fixed(2, 1, "2");} + @Test public void Add_int_fixed_pad_2() {tst_Add_int_fixed(2, 2, "02");} + @Test public void Add_int_fixed_pad_16() {tst_Add_int_fixed(2, 16, "0000000000000002");} // test overflows int + @Test public void Add_int_fixed_neg() {tst_Add_int_fixed(-2, 2, "-2");} + @Test public void Add_int_fixed_neg_pad1() {tst_Add_int_fixed(-2, 1, "-");} + @Test public void Add_int_fixed_chop_1() {tst_Add_int_fixed(123, 1, "3");} + @Test public void Add_int_fixed_chop_neg() {tst_Add_int_fixed(-21, 2, "-1");} + void tst_Add_int_fixed(int val, int digits, String expd) {Tfds.Eq(expd, String_.new_utf8_(bb.Add_int_fixed(val, digits).XtoAry()));} + @Test public void Add_long_fixed_len3() {tst_Add_long_fixed(123, 3, "123");} + @Test public void Add_long_fixed_pad_1() {tst_Add_long_fixed(2, 1, "2");} + @Test public void Add_long_fixed_pad_2() {tst_Add_long_fixed(2, 2, "02");} + @Test public void Add_long_fixed_pad_16() {tst_Add_long_fixed(2, 16, "0000000000000002");} // test overflows long + @Test public void Add_long_fixed_neg() {tst_Add_long_fixed(-2, 2, "-2");} + @Test public void Add_long_fixed_neg_pad1() {tst_Add_long_fixed(-2, 1, "-");} + @Test public void Add_long_fixed_chop_1() {tst_Add_long_fixed(123, 1, "3");} + @Test public void Add_long_fixed_chop_neg() {tst_Add_long_fixed(-21, 2, "-1");} + @Test public void Add_long_fixed_large() {tst_Add_long_fixed(123456789012345L, 15, "123456789012345");} + void tst_Add_long_fixed(long val, int digits, String expd) {Tfds.Eq(expd, String_.new_utf8_(bb.Add_long_fixed(val, digits).XtoAry()));} + @Test public void AddDte_short() { + tst_AddDte_short("2010-08-26T22:38:36Z"); + } + void tst_AddDte_short(String raw) { +// byte[] ary = String_.XtoByteAryAscii(raw); +// Bry_fmtr_IntBldr ib = new Bry_fmtr_IntBldr(); +// int y = 0, m = 0, d = 0, h = 0, n = 0, s = 0, aryLen = ary.length; +// for (int i = 0; i < aryLen; i++) { +// byte b = ary[i]; +// switch (i) { +// case 4: y = ib.XtoIntAndClear(); break; +// case 7: m = ib.XtoIntAndClear(); break; +// case 10: d = ib.XtoIntAndClear(); break; +// case 13: h = ib.XtoIntAndClear(); break; +// case 16: n = ib.XtoIntAndClear(); break; +// case 19: s = ib.XtoIntAndClear(); break; +// default: ib.Add(b); break; +// } +// } +// long l = Pow38_to(y, m, d, h, n, s); +//// Base85_utl.XtoStrByAry(l, bb. +// bb.Add_int(l); + } +// @Test public void InsertAt_str() { +// tst_InsertAt_str("", 0, "c", "c"); +// tst_InsertAt_str("ab", 0, "c", "cab"); +// tst_InsertAt_str("ab", 0, "cdefghij", "cdefghijab"); +// } +// void tst_InsertAt_str(String orig, int insertAt, String insertStr, String expd) { +// bb = Bry_bfr.new_(16); +// bb.Add_str(orig); +// bb.InsertAt_str(insertAt, insertStr); +// String actl = bb.XtoStrAndClear(); +// Tfds.Eq(expd, actl); +// } + @Test public void XtoAryAndClearAndTrim() { + tst_XtoAryAndClearAndTrim("a" , "a"); + tst_XtoAryAndClearAndTrim(" a " , "a"); + tst_XtoAryAndClearAndTrim(" a b " , "a b"); + tst_XtoAryAndClearAndTrim(" " , ""); + } + void tst_XtoAryAndClearAndTrim(String raw, String expd) { + bb.Add_str(raw); + Tfds.Eq(expd, String_.new_utf8_(bb.XtoAryAndClearAndTrim())); + } + @Test public void XtoInt() { + tst_XtoInt("123", 123); + tst_XtoInt("a", Int_.MinValue); + tst_XtoInt("9999999999", Int_.MinValue); + } + void tst_XtoInt(String raw, int expd) { + bb.Add_str(raw); + Tfds.Eq(expd, bb.XtoIntAndClear(Int_.MinValue)); + } + static long Pow38_to(int year, int month, int day, int hour, int minute, int second, int frac) { + return ((long)year) << 26 + | ((long)month & 0x0f) << 22 // 16 + | ((long)day & 0x1f) << 17 // 32 + | ((long)hour & 0x1f) << 12 // 32 + | ((long)minute & 0x3f) << 6 // 64 + | ((long)second & 0x3f) // 64 + ; + } + static DateAdp Pow38_by(long v) { + int year = (int) (v >> 26); + int month = (int)((v >> 22) & 0x0f); + int day = (int)((v >> 17) & 0x1f); + int hour = (int)((v >> 12) & 0x1f); + int minute = (int)((v >> 6) & 0x3f); + int second = (int)((v ) & 0x3f); + return DateAdp_.new_(year, month, day, hour, minute, second, 0); + } + @Test public void Add_bfr_trimEnd_and_clear() { + tst_Add_bfr_trimEnd_and_clear("a ", "a"); + } + void tst_Add_bfr_trimEnd_and_clear(String raw, String expd) { + Bry_bfr tmp = Bry_bfr.new_().Add_str(raw); + Tfds.Eq(expd, bb.Add_bfr_trim_and_clear(tmp, false, true).XtoStrAndClear()); + } + @Test public void Add_bfr_trimAll_and_clear() { + tst_Add_bfr_trimAll_and_clear(" a ", "a"); + tst_Add_bfr_trimAll_and_clear(" a b ", "a b"); + tst_Add_bfr_trimAll_and_clear("a", "a"); + tst_Add_bfr_trimAll_and_clear("", ""); + } + void tst_Add_bfr_trimAll_and_clear(String raw, String expd) { + Bry_bfr tmp = Bry_bfr.new_().Add_str(raw); + Tfds.Eq(expd, bb.Add_bfr_trim_and_clear(tmp, true, true).XtoStrAndClear()); + } + @Test public void Add_int_pad_bgn() { + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 0, "000"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 1, "001"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 10, "010"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 100, "100"); + fxt.Test_Add_int_pad_bgn(Byte_ascii.Num_0, 3, 1000, "1000"); + } + @Test public void Add_bry_escape() { + fxt.Test_Add_bry_escape("abc" , "abc"); // nothing to escape + fxt.Test_Add_bry_escape("a'bc" , "a''bc"); // single escape (code handles first quote differently) + fxt.Test_Add_bry_escape("a'b'c" , "a''b''c"); // double escape (code handles subsequent quotes different than first) + } + @Test public void Insert_at() { + fxt.Test_Insert_at("abcd", 0, "xyz" , "xyzabcd"); // bgn + fxt.Test_Insert_at("abcd", 4, "xyz" , "abcdxyz"); // end + fxt.Test_Insert_at("abcd", 2, "xyz" , "abxyzcd"); // mid + fxt.Test_Insert_at("abcd", 2, "xyz", 1, 2 , "abycd"); // mid + } + @Test public void Delete_rng() { + fxt.Test_Delete_rng("abcd", 0, 2 , "cd"); // bgn + fxt.Test_Delete_rng("abcd", 2, 4 , "ab"); // end + fxt.Test_Delete_rng("abcd", 1, 3 , "ad"); // mid + } + @Test public void Delete_rng_to_bgn() { + fxt.Test_Delete_rng_to_bgn("abcd", 2 , "cd"); + } + @Test public void Delete_rng_to_end() { + fxt.Test_Delete_rng_to_end("abcd", 2 , "ab"); + } +} +class ByteAryBfr_fxt { + private Bry_bfr bfr = Bry_bfr.reset_(16); + public void Clear() { + bfr.ClearAndReset(); + } + public void Test_Add_int_pad_bgn(byte pad_byte, int str_len, int val, String expd) {Tfds.Eq(expd, bfr.Add_int_pad_bgn(pad_byte, str_len, val).XtoStrAndClear());} + public void Test_Add_bry_escape(String val, String expd) { + byte[] val_bry = Bry_.new_utf8_(val); + Tfds.Eq(expd, bfr.Add_bry_escape(Byte_ascii.Apos, Byte_ascii.Apos, val_bry, 0, val_bry.length).XtoStrAndClear()); + } + public void Test_Insert_at(String init, int pos, String val, String expd) {Tfds.Eq(expd, bfr.Add_str(init).Insert_at(pos, Bry_.new_utf8_(val)).XtoStrAndClear());} + public void Test_Insert_at(String init, int pos, String val, int val_bgn, int val_end, String expd) {Tfds.Eq(expd, bfr.Add_str(init).Insert_at(pos, Bry_.new_utf8_(val), val_bgn, val_end).XtoStrAndClear());} + public void Test_Delete_rng(String init, int bgn, int end, String expd) {Tfds.Eq(expd, bfr.Add_str(init).Delete_rng(bgn, end).XtoStrAndClear());} + public void Test_Delete_rng_to_bgn(String init, int pos, String expd) {Tfds.Eq(expd, bfr.Add_str(init).Delete_rng_to_bgn(pos).XtoStrAndClear());} + public void Test_Delete_rng_to_end(String init, int pos, String expd) {Tfds.Eq(expd, bfr.Add_str(init).Delete_rng_to_end(pos).XtoStrAndClear());} +} diff --git a/100_core/src_110_primitive/gplx/Bry_finder.java b/100_core/src_110_primitive/gplx/Bry_finder.java new file mode 100644 index 000000000..333af9e1a --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_finder.java @@ -0,0 +1,219 @@ +/* +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; +public class Bry_finder { + public static final int Not_found = -1; + public static int Find_fwd(byte[] src, byte lkp) {return Find_fwd(src, lkp, 0, src.length);} + public static int Find_fwd(byte[] src, byte lkp, int cur) {return Find_fwd(src, lkp, cur, src.length);} + public static int Find_fwd(byte[] src, byte lkp, int cur, int end) { + for (int i = cur; i < end; i++) + if (src[i] == lkp) return i; + return Bry_finder.Not_found; + } + public static int Find_bwd(byte[] src, byte lkp) {return Find_bwd(src, lkp, src.length, 0);} + public static int Find_bwd(byte[] src, byte lkp, int cur) {return Find_bwd(src, lkp, cur , 0);} + public static int Find_bwd(byte[] src, byte lkp, int cur, int end) { + --cur; // always subtract 1 from cur; allows passing in src_len or cur_pos without forcing caller to subtract - 1; DATE:2014-02-11 + --end; + for (int i = cur; i > end; i--) + if (src[i] == lkp) return i; + return Bry_finder.Not_found; + } + public static int Find_fwd(byte[] src, byte[] lkp) {return Find(src, lkp, 0 , src.length, true);} + public static int Find_fwd(byte[] src, byte[] lkp, int cur) {return Find(src, lkp, cur , src.length, true);} + public static int Find_fwd(byte[] src, byte[] lkp, int cur, int end) {return Find(src, lkp, cur , end, true);} + public static int Find(byte[] src, byte[] lkp, int src_bgn, int src_end, boolean fwd) { + if (src_bgn < 0 || src.length == 0) return Bry_finder.Not_found; + int dif, lkp_len = lkp.length, lkp_bgn, lkp_end, src_end_chk; + if (fwd) { + if (src_bgn > src_end) return Bry_finder.Not_found; + dif = 1; lkp_bgn = 0; lkp_end = lkp_len; src_end_chk = src_end - CompareAble_.OffsetCompare; + } + else { + if (src_bgn < src_end) return Bry_finder.Not_found; + dif = -1; lkp_bgn = lkp_len - 1; lkp_end = -1; src_end_chk = src.length - CompareAble_.OffsetCompare; // src_end_chk needed when going bwd, b/c lkp_len may be > 1 + } + while (src_bgn != src_end) { // while src is not done; + int lkp_cur = lkp_bgn; + while (lkp_cur != lkp_end) { // while lkp is not done + int pos = src_bgn + lkp_cur; + if ( pos > src_end_chk // outside bounds; occurs when lkp_len > 1 + || src[pos] != lkp[lkp_cur]) // srcByte doesn't match lkpByte + break; + else + lkp_cur += dif; + } + if (lkp_cur == lkp_end) return src_bgn; // lkp matches src; exit + src_bgn += dif; + } + return Bry_finder.Not_found; + } + public static int Find_bwd(byte[] src, byte[] lkp, int cur) {return Find_bwd(src, lkp, cur , 0);} + public static int Find_bwd(byte[] src, byte[] lkp, int cur, int end) { + if (cur < 1) return Bry_finder.Not_found; + --cur; // always subtract 1 from cur; allows passing in src_len or cur_pos without forcing caller to subtract - 1; DATE:2014-02-11 + --end; + int src_len = src.length; + int lkp_len = lkp.length; + for (int i = cur; i > end; i--) { + if (i + lkp_len > src_len) continue; // lkp too small for pos; EX: src=abcde; lkp=bcd; pos=4 + boolean match = true; + for (int j = 0; j < lkp_len; j++) { + if (lkp[j] != src[i + j]) { + match = false; + break; + } + } + if (match) return i; + } + return Bry_finder.Not_found; + } + public static int Find_bwd_last_ws(byte[] src, int cur) { + if (cur < 1) return Bry_finder.Not_found; + --cur; + int rv = Bry_finder.Not_found; + for (int i = cur; i > -1; i--) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + rv = i; + break; + default: + i = -1; + break; + } + } + return rv; + } + public static int Find_fwd_last_ws(byte[] src, int cur) { + int end = src.length; + if (cur >= end) return Bry_finder.Not_found; + int rv = Bry_finder.Not_found; + for (int i = cur; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + rv = i; + break; + default: + i = -1; + break; + } + } + return rv; + } + public static int Find_bwd_non_ws(byte[] src, int cur, int end) { // get pos of 1st char that is not ws; + if (cur >= src.length) return Bry_finder.Not_found; + for (int i = cur; i >= end; i--) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + break; + default: + return i; + } + } + return Bry_finder.Not_found; + } + public static int Find_bwd_while(byte[] src, int cur, int end, byte while_byte) { + --cur; + while (true) { + if ( cur < end + || src[cur] != while_byte) return cur; + --cur; + } + } + public static int Find_fwd_while(byte[] src, int cur, int end, byte while_byte) { + while (true) { + if ( cur == end + || src[cur] != while_byte) return cur; + cur++; + } + } + public static int Find_fwd_until(byte[] src, int cur, int end, byte until_byte) { + while (true) { + if ( cur == end + || src[cur] == until_byte) return cur; + cur++; + } + } + public static int Find_fwd_while_space_or_tab(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + switch (src[cur]) { + case Byte_ascii.Space: case Byte_ascii.Tab: ++cur; break; + default: return cur; + } + } + } + public static int Find_fwd_while_ws(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + switch (src[cur]) { + case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + case Byte_ascii.Space: case Byte_ascii.Tab: ++cur; break; + default: return cur; + } + } + } + public static int Find_fwd_while_letter(byte[] src, int cur, int end) { + while (cur < end) { + switch (src[cur]) { + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + break; + default: + return cur; + } + ++cur; + } + return cur; + } + public static int Find_fwd_while_num(byte[] src) {return Find_fwd_while_num(src, 0, src.length);} + public static int Find_fwd_while_num(byte[] src, int cur, int end) { + while (cur < end) { + if (!Byte_ascii.Is_num(src[cur])) + return cur; + ++cur; + } + return cur; + } + public static int Find_fwd_while_not_ws(byte[] src, int cur, int end) { + while (true) { + if (cur == end) return cur; + switch (src[cur]) { + case Byte_ascii.Space: + case Byte_ascii.NewLine: + case Byte_ascii.Tab: + case Byte_ascii.CarriageReturn: + ++cur; + break; + default: + return cur; + } + } + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_finder_tst.java b/100_core/src_110_primitive/gplx/Bry_finder_tst.java new file mode 100644 index 000000000..6262e9a5c --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_finder_tst.java @@ -0,0 +1,47 @@ +/* +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; +import org.junit.*; +import gplx.texts.*; +public class Bry_finder_tst { + @Test public void Find_fwd() { + tst_Find_fwd("abcba", "b", 0, 1); + tst_Find_fwd("abcba", "z", 0, -1); + tst_Find_fwd("abcba", "b", 1, 1); + tst_Find_fwd("abcba", "b", 2, 3); + tst_Find_fwd("abcba", "b", 4, -1); + tst_Find_fwd("abcba", "zb", 4, -1); + tst_Find_fwd("abcba", "a", 6, -1); + } void tst_Find_fwd(String src, String lkp, int bgn, int expd) {Tfds.Eq(expd, Bry_finder.Find_fwd(Bry_.new_utf8_(src), Bry_.new_utf8_(lkp), bgn));} + @Test public void Find_bwd() { + tst_Find_bwd("abcba", "b", 4, 3); + tst_Find_bwd("abcba", "z", 4, -1); + tst_Find_bwd("abcba", "b", 3, 1); + tst_Find_bwd("abcba", "b", 2, 1); + tst_Find_bwd("abcba", "b", 0, -1); + tst_Find_bwd("abcba", "zb", 4, -1); + tst_Find_fwd("abcba", "a", -1, -1); + tst_Find_bwd("abcba", "ab", 4, 0); + } void tst_Find_bwd(String src, String lkp, int bgn, int expd) {Tfds.Eq(expd, Bry_finder.Find_bwd(Bry_.new_utf8_(src), Bry_.new_utf8_(lkp), bgn));} + @Test public void Find_bwd_last_ws() { + Find_bwd_1st_ws_tst("a b" , 2, 1); // basic + Find_bwd_1st_ws_tst("a b" , 3, 1); // multiple + Find_bwd_1st_ws_tst("ab" , 1, Bry_.NotFound); // none + } + void Find_bwd_1st_ws_tst(String src, int pos, int expd) {Tfds.Eq(expd, Bry_finder.Find_bwd_last_ws(Bry_.new_ascii_(src), pos));} +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr.java b/100_core/src_110_primitive/gplx/Bry_fmtr.java new file mode 100644 index 000000000..16a296302 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr.java @@ -0,0 +1,262 @@ +/* +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; +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._; + public Bry_fmtr Fmt_(byte[] v) {fmt = v; dirty = true; return this;} public Bry_fmtr Fmt_(String v) {return Fmt_(Bry_.new_utf8_(v));} + public Bry_fmtr Keys_(String... ary) { + if (keys == null) keys = HashAdp_.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_utf8_(ary[i])), Int_obj_val.new_(i)); + dirty = true; + return this; + } HashAdp 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, Bry_fmtr_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].XferAry(bfr, itm.ArgIdx); + 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.XtoAryAndClear();} + public byte[] Bld_bry_many(Bry_bfr bfr, Object... args) { + Bld_bfr_ary(bfr, args); + return bfr.XtoAryAndClear(); + } + public String Bld_str_many(Bry_bfr bfr, String fmt, Object... args) { + this.Fmt_(fmt).Bld_bfr_many(bfr, args); + return bfr.XtoStrAndClear(); + } + + 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.XtoStr(); + } 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_utf8_("~{"), missing_end_static = Bry_.new_utf8_("}"); + 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() { + Bry_bfr lkp_bfr = Bry_bfr.new_(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; + ListAdp list = ListAdp_.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.XtoInt(0) - baseInt)); + else { + byte[] key_fmt = lkp_bfr.XtoAry(); + Object idx_ref = keys.Fetch(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 Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + 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_("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 + 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 = Byte_ascii.NewLine; + else if (nxt_byte == char_escape_tab) tmp_byte = Byte_ascii.Tab; + else { + if (fail_when_invalid_escapes) throw Err_.new_("unknown escape code").Add("code", Char_.XbyInt(nxt_byte)).Add("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_("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.XtoAry(Bry_fmtr_itm.class); + itms_len = itms.length; + return this; + } + int Compile_eval_cmd(byte[] fmt, int fmt_len, int eval_lhs_bgn, ListAdp list) { + int eval_lhs_end = Bry_finder.Find_fwd(fmt, char_eval_end, eval_lhs_bgn + Int_.Const_dlm_len, fmt_len); if (eval_lhs_end == Bry_.NotFound) throw Err_mgr._.fmt_(GRP_KEY, "eval_lhs_end_invalid", "could not find eval_lhs_end: ~{0}", String_.new_utf8_(fmt, eval_lhs_bgn, fmt_len)); + byte[] eval_dlm = Bry_.Mid(fmt, eval_lhs_bgn , eval_lhs_end + Int_.Const_dlm_len); + int eval_rhs_bgn = Bry_finder.Find_fwd(fmt, eval_dlm , eval_lhs_end + Int_.Const_dlm_len, fmt_len); if (eval_rhs_bgn == Bry_.NotFound) throw Err_mgr._.fmt_(GRP_KEY, "eval_rhs_bgn_invalid", "could not find eval_rhs_bgn: ~{0}", String_.new_utf8_(fmt, eval_lhs_end, fmt_len)); + byte[] eval_cmd = Bry_.Mid(fmt, eval_lhs_end + Int_.Const_dlm_len, eval_rhs_bgn); + byte[] eval_rslt = eval_mgr.Eval(eval_cmd); + int eval_rhs_end = eval_rhs_bgn + Int_.Const_dlm_len + 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 = Byte_ascii.Tilde, char_arg_bgn = Byte_ascii.Curly_bgn, char_arg_end = Byte_ascii.Curly_end, char_escape_nl = Byte_ascii.Ltr_n, char_escape_tab = Byte_ascii.Ltr_t, char_eval_bgn = Byte_ascii.Lt, char_eval_end = Byte_ascii.Gt; + public static final Bry_fmtr Null = new Bry_fmtr().Fmt_(""); + public static Bry_fmtr 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_() {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(key); + tmp_bfr.Add_byte(Byte_ascii.Colon); + int args_len = args.length; + for (int i = 0; i < args_len; i++) { // add " 0='~{0}'" + tmp_bfr.Add_byte(Byte_ascii.Space); + tmp_bfr.Add_int_variable(i); + tmp_bfr.Add_byte(Byte_ascii.Eq); + tmp_bfr.Add_byte(Byte_ascii.Apos); + tmp_bfr.Add_byte(Byte_ascii.Tilde); + tmp_bfr.Add_byte(Byte_ascii.Curly_bgn); + tmp_bfr.Add_int_variable(i); + tmp_bfr.Add_byte(Byte_ascii.Curly_end); + tmp_bfr.Add_byte(Byte_ascii.Apos); + } + return tmp_bfr.XtoStrAndClear(); + } 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.XtoAryAndClear(); + this.Fmt_(bry).Compile(); + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_arg.java b/100_core/src_110_primitive/gplx/Bry_fmtr_arg.java new file mode 100644 index 000000000..bd97503fa --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_arg.java @@ -0,0 +1,21 @@ +/* +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; +public interface Bry_fmtr_arg { + void XferAry(Bry_bfr bfr, int idx); +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_arg_.java b/100_core/src_110_primitive/gplx/Bry_fmtr_arg_.java new file mode 100644 index 000000000..b7f8ae135 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_arg_.java @@ -0,0 +1,33 @@ +/* +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; +import gplx.brys.*; +public class Bry_fmtr_arg_ { + public static Bry_fmtr_arg_bry bry_(String v) {return new Bry_fmtr_arg_bry(Bry_.new_utf8_(v));} + public static Bry_fmtr_arg_bry bry_(byte[] v) {return new Bry_fmtr_arg_bry(v);} + public static Bry_fmtr_arg_byt byt_(byte v) {return new Bry_fmtr_arg_byt(v);} + public static Bry_fmtr_arg_int int_(int v) {return new Bry_fmtr_arg_int(v);} + public static Bry_fmtr_arg_bfr bfr_(Bry_bfr v) {return new Bry_fmtr_arg_bfr(v);} + public static Bry_fmtr_arg_bfr_retain bfr_retain_(Bry_bfr v) {return new Bry_fmtr_arg_bfr_retain(v);} + public static Bry_fmtr_arg fmtr_(Bry_fmtr v, Bry_fmtr_arg... arg_ary) {return new Bry_fmtr_arg_fmtr(v, arg_ary);} + public static Bry_fmtr_arg_fmtr_objs fmtr_null_() {return new Bry_fmtr_arg_fmtr_objs(null, null);} + public static final Bry_fmtr_arg Null = new Bry_fmtr_arg_null(); +} +class Bry_fmtr_arg_null implements Bry_fmtr_arg { + public void XferAry(Bry_bfr trg, int idx) {} +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_arg_fmtr_objs.java b/100_core/src_110_primitive/gplx/Bry_fmtr_arg_fmtr_objs.java new file mode 100644 index 000000000..edf7179a4 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_arg_fmtr_objs.java @@ -0,0 +1,25 @@ +/* +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; +public class Bry_fmtr_arg_fmtr_objs implements Bry_fmtr_arg { + public Bry_fmtr_arg_fmtr_objs Atrs_(Bry_fmtr fmtr, Object... objs) {this.fmtr = fmtr; this.objs = objs; return this;} + public void XferAry(Bry_bfr trg, int idx) { + fmtr.Bld_bfr_many(trg, objs); + } + public Bry_fmtr_arg_fmtr_objs(Bry_fmtr fmtr, Object[] objs) {this.fmtr = fmtr; this.objs = objs;} Bry_fmtr fmtr; Object[] objs; +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr.java b/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr.java new file mode 100644 index 000000000..83abe2873 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr.java @@ -0,0 +1,22 @@ +/* +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; +public interface Bry_fmtr_eval_mgr { + boolean Enabled(); void Enabled_(boolean v); + byte[] Eval(byte[] cmd); +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr_.java b/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr_.java new file mode 100644 index 000000000..32d55dbb0 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr_.java @@ -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 . +*/ +package gplx; +public class Bry_fmtr_eval_mgr_ { + public static Io_url Eval_url(Bry_fmtr_eval_mgr eval_mgr, byte[] fmt) { + if (eval_mgr == null) return Io_url_.new_any_(String_.new_utf8_(fmt)); + Bry_bfr bfr = Bry_bfr.reset_(255); + Bry_fmtr fmtr = Bry_fmtr.tmp_(); + fmtr.Eval_mgr_(eval_mgr).Fmt_(fmt).Bld_bfr_none(bfr); + return Io_url_.new_any_(bfr.XtoStrAndClear()); + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr_gfs.java b/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr_gfs.java new file mode 100644 index 000000000..6d8cc0c9e --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_eval_mgr_gfs.java @@ -0,0 +1,25 @@ +/* +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; +public class Bry_fmtr_eval_mgr_gfs implements Bry_fmtr_eval_mgr { + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public byte[] Eval(byte[] cmd) { + return enabled ? Bry_.new_utf8_(Object_.XtoStr_OrNullStr(GfsCore._.ExecText(String_.new_utf8_(cmd)))) : null; + } + public static final Bry_fmtr_eval_mgr_gfs _ = new Bry_fmtr_eval_mgr_gfs(); Bry_fmtr_eval_mgr_gfs() {} +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_itm.java b/100_core/src_110_primitive/gplx/Bry_fmtr_itm.java new file mode 100644 index 000000000..f12481303 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_itm.java @@ -0,0 +1,33 @@ +/* +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; +public class Bry_fmtr_itm { + public boolean Arg; + public int ArgIdx; + public byte[] Dat; + public String DatStr() { + if (datStr == null) datStr = String_.new_utf8_(Dat); + return datStr; + } String datStr; + public static Bry_fmtr_itm arg_(int idx) {return new Bry_fmtr_itm(true, idx, Bry_.Empty);} + public static Bry_fmtr_itm dat_(byte[] dat, int len) {return new Bry_fmtr_itm(false, -1, Bry_.Mid(dat, 0, len));} + public static Bry_fmtr_itm dat_bry_(byte[] bry) {return new Bry_fmtr_itm(false, -1, bry);} + Bry_fmtr_itm(boolean arg, int argIdx, byte[] dat) { + this.Arg = arg; this.ArgIdx = argIdx; this.Dat = dat; + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_fmtr_tst.java b/100_core/src_110_primitive/gplx/Bry_fmtr_tst.java new file mode 100644 index 000000000..c84d88625 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_fmtr_tst.java @@ -0,0 +1,75 @@ +/* +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; +import org.junit.*; +public class Bry_fmtr_tst { + @Test public void Idx_text() {tst_Format("a", "a");} + @Test public void Idx_1() {tst_Format("a", "~{0}", "a");} + @Test public void Idx_3() {tst_Format("abc", "~{0}~{1}~{2}", "a", "b", "c");} + @Test public void Idx_mix() {tst_Format("abcde", "a~{0}c~{1}e", "b", "d");} + @Test public void Key_basic() {tst("~{key}" , String_.Ary("key") , ary_("a") , "a");} + @Test public void Key_mult() {tst("~{key1}~{key2}" , String_.Ary("key1", "key2") , ary_("a", "b") , "ab");} + @Test public void Key_mix() {tst("~{key1}~{1}" , String_.Ary("key1", "key2") , ary_("a", "b") , "ab");} + @Test public void Key_repeat() {tst("~{key1}~{key1}" , String_.Ary("key1") , ary_("a") , "aa");} + @Test public void Simple() { + Bry_fmtr fmtr = Bry_fmtr.new_("0~{key1}1~{key2}2", "key1", "key2"); + Tfds.Eq("0.1,2", fmtr.Bld_str_many(".", ",")); + } + @Test public void Cmd() { + Bry_fmtr_tst_mok mok = new Bry_fmtr_tst_mok(); + Bry_fmtr fmtr = Bry_fmtr.new_("0~{key1}2~{<>3<>}4", "key1").Eval_mgr_(mok); + Tfds.Eq("012~{<>3<>}4", fmtr.Bld_str_many("1")); + mok.Enabled_(true); + Tfds.Eq("01234", fmtr.Bld_str_many("1")); + } + @Test public void Err_missing_idx() {tst_Format("~{0}", "~{0}");} + String[] ary_(String... ary) {return ary;} + void tst(String fmt, String[] keys, String[] args, String expd) { + Bry_fmtr fmtr = new Bry_fmtr().Fmt_(Bry_.new_utf8_(fmt)); + fmtr.Keys_(keys); + String actl = fmtr.Bld_str_many(args); + Tfds.Eq(expd, actl); + } + void tst_Format(String expd, String fmt, String... args) { + Bry_fmtr fmtr = new Bry_fmtr().Fmt_(fmt); + Tfds.Eq(expd, fmtr.Bld_str_many(args)); + } + @Test public void Bld_bfr_many_and_set_fmt() { + Bry_fmtr_fxt fxt = new Bry_fmtr_fxt().Clear(); + fxt.Bld_bfr_many_and_set_fmt("a~{0}c", Object_.Ary("b"), "abc"); + } +} +class Bry_fmtr_tst_mok implements Bry_fmtr_eval_mgr { + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public byte[] Eval(byte[] cmd) { + return enabled ? cmd : null; + } +} +class Bry_fmtr_fxt { + public Bry_fmtr_fxt Clear() { + if (fmtr == null) { + fmtr = Bry_fmtr.new_(); + } + return this; + } private Bry_fmtr fmtr; + public void Bld_bfr_many_and_set_fmt(String fmt, Object[] args, String expd) { + fmtr.Fmt_(fmt); + fmtr.Bld_bfr_many_and_set_fmt(args); + Tfds.Eq(expd, String_.new_ascii_(fmtr.Fmt())); + } +} diff --git a/100_core/src_110_primitive/gplx/Bry_obj_ref.java b/100_core/src_110_primitive/gplx/Bry_obj_ref.java new file mode 100644 index 000000000..66f4acdec --- /dev/null +++ b/100_core/src_110_primitive/gplx/Bry_obj_ref.java @@ -0,0 +1,35 @@ +/* +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; +public class Bry_obj_ref { + public byte[] Val() {return val;} public Bry_obj_ref Val_(byte[] v) {val = v; return this;} private byte[] val; + @Override public int hashCode() {return CalcHashCode(val, 0, val.length);} + @Override public boolean equals(Object obj) {return obj == null ? false : Bry_.Eq(val, ((Bry_obj_ref)obj).Val());} // NOTE: strange, but null check needed; throws null error; EX.WP: File:Eug�ne Delacroix - La libert� guidant le peuple.jpg + public static int CalcHashCode(byte[] ary, int bgn, int end) { + int rv = 0; + for (int i = bgn; i < end; i++) + rv = (31 * rv) + ary[i]; + return rv; + } + public static Bry_obj_ref null_() {return new_(null);} + public static Bry_obj_ref new_(byte[] val) { + Bry_obj_ref rv = new Bry_obj_ref(); + rv.val = val; + return rv; + } private Bry_obj_ref() {} +} diff --git a/100_core/src_110_primitive/gplx/Byte_.java b/100_core/src_110_primitive/gplx/Byte_.java new file mode 100644 index 000000000..8950579a0 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Byte_.java @@ -0,0 +1,48 @@ +/* +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; +public class Byte_ { + public static final byte MinValue = Byte.MIN_VALUE; + public static byte[] Ary(byte... ary) {return ary;} + public static byte[] Ary_by_ints(int... ary) { + int ary_len = ary.length; + byte[] rv = new byte[ary_len]; + for (int i = 0; i < ary_len; i++) + rv[i] = int_(ary[i]); + return rv; + } + public static String XtoStr(byte v) {return new Byte(v).toString();} + public static int XtoInt(byte v) {return v < 0 ? (int)v + 256 : v;} + public static boolean In(byte v, byte... ary) { + for (byte itm : ary) + if (v == itm) return true; + return false; + } + public static int Compare(byte lhs, byte rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } + public static byte cast_(Object o) {try {return (Byte)o;} catch (Exception e) {throw Err_.type_mismatch_exc_(e, byte.class, o);}} + public static byte parse_(String raw) {return Byte.parseByte(raw);} + public static byte int_(int v) {return v > 127 ? (byte)(v - 256) : (byte)v;} // PERF?: (byte)(v & 0xff) + public static byte X_to_boolean_byte(boolean v) { + return v ? Bool_.Y_byte : Bool_.N_byte; + } + public static final byte Zero = 0, MaxValue_127 = 127; +} diff --git a/100_core/src_110_primitive/gplx/Byte__tst.java b/100_core/src_110_primitive/gplx/Byte__tst.java new file mode 100644 index 000000000..e67c93429 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Byte__tst.java @@ -0,0 +1,35 @@ +/* +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; +import org.junit.*; +public class Byte__tst { + @Test public void int_() { + tst_int_( 0, 0); + tst_int_( 127, 127); + tst_int_( 128, 128); // NOTE: JAVA defines byte as -128 -> 127 + tst_int_( 255, 255); + tst_int_( 256, 0); // NOTE: 256 will cast to 1; (byte)256 works same in both JAVA/.NET + } void tst_int_(int v, int expd) {Tfds.Eq((byte)expd, Byte_.int_(v));} // WORKAROUND/JAVA: expd is of type int b/c java promotes numbers to ints + @Test public void XtoInt() { + tst_XtoInt( 0, 0); + tst_XtoInt( 127, 127); + tst_XtoInt( 128, 128); + tst_XtoInt( 255, 255); + tst_XtoInt( 256, 0); + } void tst_XtoInt(int v, int expd) {Tfds.Eq(expd, Byte_.XtoInt((byte)v));} // WORKAROUND/JAVA: v is of type int b/c java promotes numbers to ints +} diff --git a/100_core/src_110_primitive/gplx/Byte_ascii.java b/100_core/src_110_primitive/gplx/Byte_ascii.java new file mode 100644 index 000000000..781fb61e7 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Byte_ascii.java @@ -0,0 +1,76 @@ +/* +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; +public class Byte_ascii { + public static final byte + Nil = 0 , Backfeed = 8, Tab = 9 + , NewLine = 10, Formfeed = 12, CarriageReturn = 13 + , Space = 32, Bang = 33, Quote = 34 + , Hash = 35, Dollar = 36, Percent = 37, Amp = 38, Apos = 39 + , Paren_bgn = 40, Paren_end = 41, Asterisk = 42, Plus = 43, Comma = 44 + , Dash = 45, Dot = 46, Slash = 47, Num_0 = 48, Num_1 = 49 + , Num_2 = 50, Num_3 = 51, Num_4 = 52, Num_5 = 53, Num_6 = 54 + , Num_7 = 55, Num_8 = 56, Num_9 = 57, Colon = 58, Semic = 59 + , Lt = 60, Eq = 61, Gt = 62, Question = 63, At = 64 + , Ltr_A = 65, Ltr_B = 66, Ltr_C = 67, Ltr_D = 68, Ltr_E = 69 + , Ltr_F = 70, Ltr_G = 71, Ltr_H = 72, Ltr_I = 73, Ltr_J = 74 + , Ltr_K = 75, Ltr_L = 76, Ltr_M = 77, Ltr_N = 78, Ltr_O = 79 + , Ltr_P = 80, Ltr_Q = 81, Ltr_R = 82, Ltr_S = 83, Ltr_T = 84 + , Ltr_U = 85, Ltr_V = 86, Ltr_W = 87, Ltr_X = 88, Ltr_Y = 89 + , Ltr_Z = 90, Brack_bgn = 91, Backslash = 92, Brack_end = 93, Pow = 94 // Circumflex + , Underline = 95, Tick = 96, Ltr_a = 97, Ltr_b = 98, Ltr_c = 99 + , Ltr_d = 100, Ltr_e = 101, Ltr_f = 102, Ltr_g = 103, Ltr_h = 104 + , Ltr_i = 105, Ltr_j = 106, Ltr_k = 107, Ltr_l = 108, Ltr_m = 109 + , Ltr_n = 110, Ltr_o = 111, Ltr_p = 112, Ltr_q = 113, Ltr_r = 114 + , Ltr_s = 115, Ltr_t = 116, Ltr_u = 117, Ltr_v = 118, Ltr_w = 119 + , Ltr_x = 120, Ltr_y = 121, Ltr_z = 122, Curly_bgn = 123, Pipe = 124 + , Curly_end = 125, Tilde = 126 + ; + public static final byte Max_7_bit = (byte)127, Ascii_min = 0, Ascii_max = 127; + public static final byte[] Space_len2 = new byte[] {Space, Space}, Space_len4 = new byte[] {Space, Space, Space, Space}; + public static boolean Is_ltr(byte b) { + return ( b >= Byte_ascii.Ltr_a && b <= Byte_ascii.Ltr_z + || b >= Byte_ascii.Ltr_A && b <= Byte_ascii.Ltr_Z); + } + public static boolean Is_ws(byte b) { + switch (b) { + case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: case Byte_ascii.Space: return true; + default: return false; + } + } + public static boolean Is_num(byte b) { + return b > Byte_ascii.Slash && b < Byte_ascii.Colon; + } + public static int X_to_digit(byte b) {return b - Byte_ascii.Num_0;} + public static byte Case_upper(byte b) { + return b > 96 && b < 123 + ? (byte)(b - 32) + : b + ; + } + public static byte Case_lower(byte b) { + return b > 64 && b < 91 + ? (byte)(b + 32) + : b + ; + } + public static final byte[] + Dot_bry = new byte[] {Byte_ascii.Dot} + , NewLine_bry = new byte[] {Byte_ascii.NewLine} + ; +} diff --git a/100_core/src_110_primitive/gplx/Byte_obj_ref.java b/100_core/src_110_primitive/gplx/Byte_obj_ref.java new file mode 100644 index 000000000..0c190fb3c --- /dev/null +++ b/100_core/src_110_primitive/gplx/Byte_obj_ref.java @@ -0,0 +1,31 @@ +/* +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; +public class Byte_obj_ref { + public byte Val() {return val;} private byte val; + public Byte_obj_ref Val_(byte v) {val = v; return this;} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Byte_obj_ref)obj).Val();} + @Override public String toString() {return Int_.XtoStr(val);} + public static Byte_obj_ref zero_() {return new_(Byte_.Zero);} + public static Byte_obj_ref new_(byte val) { + Byte_obj_ref rv = new Byte_obj_ref(); + rv.val = val; + return rv; + } private Byte_obj_ref() {} +} diff --git a/100_core/src_110_primitive/gplx/Byte_obj_val.java b/100_core/src_110_primitive/gplx/Byte_obj_val.java new file mode 100644 index 000000000..fd19c2b5a --- /dev/null +++ b/100_core/src_110_primitive/gplx/Byte_obj_val.java @@ -0,0 +1,29 @@ +/* +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; +public class Byte_obj_val { + public byte Val() {return val;} private byte val; + @Override public String toString() {return Int_.XtoStr(val);} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Byte_obj_val)obj).Val();} + public static Byte_obj_val new_(byte val) { + Byte_obj_val rv = new Byte_obj_val(); + rv.val = val; + return rv; + } private Byte_obj_val() {} +} diff --git a/100_core/src_110_primitive/gplx/Char_.java b/100_core/src_110_primitive/gplx/Char_.java new file mode 100644 index 000000000..b1d9537e4 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Char_.java @@ -0,0 +1,76 @@ +/* +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; +public class Char_ { + public static final char Null = '\0', NewLine = '\n'; + public static final int CharLen = 1; + public static final int AsciiZero = 48; + public static boolean IsNumber(char c) { + switch (c) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return true; + default: return false; + } + } + public static boolean IsCaseLower(char c) {return Character.isLowerCase(c);} + public static boolean IsLetterOrDigit(char c) {return Character.isLetterOrDigit(c);} + public static boolean IsLetterEnglish(char c) { + switch (c) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': return true; + default: return false; + } + } + public static boolean IsLetterLowerEnglish(char c) { + switch (c) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': return true; + default: return false; + } + } + public static boolean IsWhitespace(char c) { + switch (c) { + case ' ': case '\t': case '\n': case '\r': return true; + default: return false; + } + } + public static int To_int_or(char c, int or) { + switch (c) { + case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; + case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; + default: return or; + } + } + public static boolean In(char match, char... ary) { + for (char itm : ary) + if (itm == match) return true; + return false; + } + public static String XtoStr(char[] ary, int pos, int length) {return new String(ary, pos, length);} + public static byte[] XtoByteAry(int v) {return Bry_.new_utf8_(Char_.XtoStr((char)v));} + public static char XbyInt(int i) {return (char)i;} + public static String XtoStr(int b) {return XtoStr((char)b);} + public static String XtoStr(char c) {return String.valueOf(c);} + public static byte XtoByte(char c) {return (byte)c;} + public static char cast_(Object o) {try {return (Character)o;} catch(Exception e) {throw Err_.type_mismatch_exc_(e, char.class, o);}} + public static char parse_(String raw) {try {return raw.charAt(0);} catch(Exception exc) {throw Err_.parse_type_exc_(exc, char.class, raw);}} +} diff --git a/100_core/src_110_primitive/gplx/Double_.java b/100_core/src_110_primitive/gplx/Double_.java new file mode 100644 index 000000000..52d7e01d4 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Double_.java @@ -0,0 +1,37 @@ +/* +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; +public class Double_ { + public static final Class ClassOf = Double.class; + public static final double Inf_pos = Double.POSITIVE_INFINITY;; + public static final double NaN = Double.NaN;; + public static final byte[] NaN_bry = Bry_.new_ascii_("NaN"); + public static boolean IsNaN(double v) {return Double.isNaN(v);} + public static double coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Double_.cast_(v) : Double_.parse_(s);} + catch (Exception e) {throw Err_.cast_(e, double.class, v);} + } + public static String XtoStr(double v) { + int v_int = (int)v; + return v - v_int == 0 ? Int_.XtoStr(v_int) : Double.toString(v); + } + public static double cast_(Object o) {try {return (Double)o;} catch(Exception e) {throw Err_.type_mismatch_exc_(e, double.class, o);}} + public static double parse_(String raw) {try {return Double.parseDouble(raw);} catch(Exception e) {throw Err_.parse_type_exc_(e, double.class, raw);}} + public static double parseOr_(String raw, double v) {try {return Double.parseDouble(raw);} catch(Exception e) {Err_.Noop(e); return v;}} + public static final double MinValue = Double.MIN_VALUE; +} diff --git a/100_core/src_110_primitive/gplx/Float_.java b/100_core/src_110_primitive/gplx/Float_.java new file mode 100644 index 000000000..dd09ac362 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Float_.java @@ -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 . +*/ +package gplx; +public class Float_ { + public static final float NaN = Float.NaN;; + public static boolean IsNaN(float v) {return Float.isNaN(v);} + public static int RoundUp(float val) { + int rv = (int)val; + return (rv == val) ? rv : rv + 1; + } + public static float Div(int val, int divisor) {return (float)val / (float)divisor;} + public static float Div(long val, long divisor) {return (float)val / (float)divisor;} + public static String XtoStr(float v) { + int v_int = (int)v; + return v - v_int == 0 ? Int_.XtoStr(v_int) : Float.toString(v); + } + public static float cast_double_(double v) {return (float)v;} + public static float cast_(Object obj) {try {return (Float)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, float.class, obj);}} + public static float read_(Object o) {String s = String_.as_(o); return s != null ? Float_.parse_(s) : Float_.cast_(o);} + public static float parse_(String raw) {try {return Float.parseFloat(raw);} catch(Exception exc) {throw Err_.parse_type_exc_(exc, float.class, raw);}} + public static float parseOr_(String raw, float v) { + if (raw == null || raw.length() == 0) return v; + try {return Float.parseFloat(raw);} catch(Exception e) {Err_.Noop(e); return v;} + } +} diff --git a/100_core/src_110_primitive/gplx/Int_.java b/100_core/src_110_primitive/gplx/Int_.java new file mode 100644 index 000000000..b459063b1 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Int_.java @@ -0,0 +1,251 @@ +/* +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; +public class Int_ implements GfoInvkAble { + public static final Class ClassOf = Integer.class; + public static final int Base1 = 1; + public static final int Const_dlm_len = 1; + public static final int Const_position_after_char = 1; + public static final int Null = Int_.MinValue; + public static int coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Int_.cast_(v) : Int_.parse_(s);} + catch (Exception e) {throw Err_.cast_(e, int.class, v);} + } + public static int[] Ary_empty = new int[0]; + public static int[] Ary(int... v) {return v;} + public static int[] Ary_copy(int[] ary) {return Ary_copy(ary, ary.length);} + public static int[] Ary_copy(int[] ary, int new_len) { + int old_len = ary.length; + int[] rv = new int[new_len]; + for (int i = 0; i < old_len; i++) + rv[i] = ary[i]; + return rv; + } + public static int[] AryRng(int bgn, int end) { + int len = end - bgn + 1; + int[] rv = new int[len]; + for (int i = 0; i < len; i++) + rv[i] = bgn + i; + return rv; + } + public static final int + MinValue = Integer.MIN_VALUE + , MaxValue = Integer.MAX_VALUE + , Neg1 = -1 + , Neg1_count = -1 + ; + public static int parse_or_(String raw, int or) { + if (raw == null) return or; + int rawLen = String_.Len(raw); if (rawLen == 0) return or; + int rv = 0, tmp = 0, factor = 1; + for (int i = rawLen; i > 0; i--) { + char c = String_.CharAt(raw, i - 1); + switch (c) { + case '0': tmp = 0; break; case '1': tmp = 1; break; case '2': tmp = 2; break; case '3': tmp = 3; break; case '4': tmp = 4; break; + case '5': tmp = 5; break; case '6': tmp = 6; break; case '7': tmp = 7; break; case '8': tmp = 8; break; case '9': tmp = 9; break; + case '-': rv *= -1; continue; // NOTE: note continue + default: return or; + } + rv += (tmp * factor); + factor *= 10; + } + return rv; + } + public static int EnsureLessThan(int v, int max) {return v >= max ? max : v;} + public static boolean In(int v, int comp0, int comp1) {return v == comp0 || v == comp1;} + public static boolean In(int v, int... ary) { + for (int itm : ary) + if (v == itm) return true; + return false; + } + public static int BoundEnd(int v, int end) {return v >= end ? end - 1 : v;} + public static int Min(int lhs, int rhs) {return lhs < rhs ? lhs : rhs;} + public static int Max(int lhs, int rhs) {return lhs > rhs ? lhs : rhs;} + public static int ModIfNeg1(int v, int or) {return v == -1 ? or : v;} + public static boolean RangeCheck(int v, int max) {return v >= 0 && v < max;} + public static void RangeCheckOrFail_list(int v, int max, String s) {if (v < 0 || v >= max) throw Err_.new_("bounds check failed").Add("msg", s).Add("v", v).Add("min", 0).Add("max", max - 1);} + public static void RangeCheckOrFail(int v, int min, int max, String s) {if (v < min || v >= max) throw Err_.new_("bounds check failed").Add("msg", s).Add("v", v).Add("min", min).Add("max", max);} + public static boolean Between(int v, int lhs, int rhs) { + int lhsCompare = v == lhs ? 0 : (v < lhs ? -1 : 1); + int rhsCompare = v == rhs ? 0 : (v < rhs ? -1 : 1); + return (lhsCompare * rhsCompare) != 1; // 1 when v is (a) greater than both or (b) less than both + } + public static int Div(int v, float divisor) {return (int)((float)v / divisor);} + public static int DivAndRoundUp(int v, int divisor) { + int whole = v / divisor; + int partial = v % divisor == 0 ? 0 : 1; + return whole + partial; + } + public static int Mult(int v, float multiplier) { + float product = ((float)v * multiplier); // WORKAROUND (DotNet): (int)((float)v * multiplier) returns 0 for 100 and .01f + return (int)product; + } + public static int Compare(int lhs, int rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } + public static int DigitCount(int v) { + int log10 = Log10(v); + return v > -1 ? log10 + 1 : log10 * -1 + 2; + } + public static int Log10(int v) { + if (v == 0) return 0; + int sign = 1; + if (v < 0) { + if (v == Int_.MinValue) return -9; // NOTE: Int_.MinValue * -1 = Int_.MinValue + v *= -1; + sign = -1; + } + int rv = Log10AryLen - 2; // rv will only happen when v == Int_.MaxValue + int bgn = 0; + if (v > 1000) { // optimization to reduce number of ops to < 5 + bgn = 3; + if (v > 1000000) bgn = 6; + } + for (int i = bgn; i < Log10AryLen; i++) { + if (v < Log10Ary[i]) {rv = i - 1; break;} + } + return rv * sign; + } public static int[] Log10Ary = new int[] {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, Int_.MaxValue}; public static int Log10AryLen = 11; + public Int_ FailIfNeg1(String key, int val) { + if (val < 0) throw Err_.new_("key must be >= 0").Add("key", key).Add("val", val); + return this; + } + public static String XtoStr_PadBgn_space(int v, int reqdPlaces) {return Xto_str_pad_bgn(v, reqdPlaces, Byte_ascii.Space, true);} // EX: 1, 3 returns " 1" + public static String XtoStr_PadBgn(int v, int reqdPlaces) {return Xto_str_pad_bgn(v, reqdPlaces, Byte_ascii.Num_0, true);} // EX: 1, 3 returns "001" + static String Xto_str_pad_bgn(int val, int places, byte pad_chr, boolean bgn) { + int len = DigitCount(val); + int pad_len = places - len; if (pad_len < 0) return Int_.XtoStr(val); + Bry_bfr bfr = Bry_bfr.new_(); + boolean neg = val < 0; + if (bgn) { // special logic to handle negative numbers; EX: -1 -> "-001", not "00-1" + if (neg) { + bfr.Add_byte(Byte_ascii.Dash); + val *= -1; + --len; + } + } + else + bfr.Add_int_fixed(val, len); + bfr.Add_byte_repeat(pad_chr, pad_len); + if (bgn) bfr.Add_int_fixed(val, len); // NOTE: neg handled above + return bfr.XtoStr(); + } + public static int read_(Object o) {String s = String_.as_(o); return s != null ? Int_.parse_(s) : Int_.cast_(o);} + public static int parse_(String raw) {try {return Integer.parseInt(raw);} catch(Exception e) {throw Err_.parse_type_exc_(e, int.class, raw);}} + public static int cast_(Object obj) {try {return (Integer)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, int.class, obj);}} + public static int cast_or_(Object obj, int or) {try {return (Integer)obj;} catch(Exception e) {Err_.Noop(e); return or;}} + public static int X_by_double_(double v) {return (int)v;} + public static String XtoStr(int v) {return new Integer(v).toString();} + public static String XtoStr_fmt(int v, String fmt) {return new java.text.DecimalFormat(fmt).format(v);} + public static boolean TypeMatch(Class type) {return type == int.class || type == Integer.class;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_XtoStr_PadBgn)) { + int v = m.ReadInt(GfsCore_.Arg_primitive), pad = m.ReadInt("pad"); + return ctx.Deny() ? (Object)this : XtoStr_PadBgn(v, pad); + } + else if (ctx.Match(k, "Add")) { + int v = m.ReadInt(GfsCore_.Arg_primitive), operand = m.ReadInt("operand"); + return ctx.Deny() ? (Object)this : v + operand; + } + else return GfoInvkAble_.Rv_unhandled; + } public static final String Invk_XtoStr_PadBgn = "XtoStr_PadBgn"; + public static final Int_ Gfs = new Int_(); +// public static int Xto_int_hex(String v) {return Integer.parseInt(v, 16);} + public static int Xto_int_hex(byte[] src) {return Xto_int_hex(src, 0, src.length);} + public static int Xto_int_hex(byte[] src, int bgn, int end) { + int rv = 0; int factor = 1; + for (int i = end - 1; i >= bgn; i--) { + int val = Xto_int_hex(src[i]); + rv += (val * factor); + factor *= 16; + } + return rv; + } + public static int Xto_int_hex(byte b) { + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + return b - Byte_ascii.Num_0; + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: case Byte_ascii.Ltr_F: + return b - Byte_ascii.Ltr_A + 10; + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: case Byte_ascii.Ltr_f: + return b - Byte_ascii.Ltr_a + 10; + default: + return -1; + } + } + public static String XtoStr_hex(int v) { + String rv = Integer.toHexString(v); + int rvLen = String_.Len(rv); + if (rvLen < 8) rv = String_.Repeat("0", 8 - rvLen) + rv; + return String_.Upper(rv); + } + public static String XtoStr(int[] ary) { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < ary.length; i++) + sb.Add_spr_unless_first(Int_.XtoStr(ary[i]), " ", i); + return sb.XtoStr(); + } + public static int[] Ary_parse(String raw_str, int reqd_len, int[] or) { + byte[] raw_bry = Bry_.new_ascii_(raw_str); + int raw_bry_len = raw_bry.length; + int[] rv = new int[reqd_len]; + int cur_val = 0, cur_mult = 1, cur_idx = reqd_len - 1; boolean signed = false; + for (int i = raw_bry_len - 1; i > -2; i--) { + byte b = i == -1 ? Byte_ascii.Comma : raw_bry[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + if (signed) return or; + cur_val += (b - Byte_ascii.Num_0) * cur_mult; + cur_mult *= 10; + break; + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: case Byte_ascii.Tab: + break; + case Byte_ascii.Comma: + if (cur_idx < 0) return or; + rv[cur_idx--] = cur_val; + cur_val = 0; cur_mult = 1; + signed = false; + break; + case Byte_ascii.Dash: + if (signed) return or; + cur_val *= -1; + signed = true; + break; + case Byte_ascii.Plus: // noop; all values positive by default + if (signed) return or; + signed = true; + break; + default: + return or; + } + } + return cur_idx == -1 ? rv : or; // cur_idx == -1 checks for unfilled; EX: Ary_parse("1,2", 3, null) is unfilled + } + public static int[] Ary_parse(String raw_str, String spr) { + String[] ary = String_.Split(raw_str, spr); + int len = ary.length; + int[] rv = new int[len]; + for (int i = 0; i < len; i++) + rv[i] = Int_.parse_(ary[i]); + return rv; + } +} diff --git a/100_core/src_110_primitive/gplx/Int__tst.java b/100_core/src_110_primitive/gplx/Int__tst.java new file mode 100644 index 000000000..d551c1b38 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Int__tst.java @@ -0,0 +1,115 @@ +/* +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; +import org.junit.*; +public class Int__tst { + @Test public void XtoStr_PadBgn() { +// tst_XtoStr_PadLeft_Zeroes(1 , 3, "001"); // pad +// tst_XtoStr_PadLeft_Zeroes(123 , 3, "123"); // no pad +// tst_XtoStr_PadLeft_Zeroes(1234 , 3, "1234"); // val exceeds pad; confirm noop + tst_XtoStr_PadLeft_Zeroes(-1 , 3, "-01"); // negative + tst_XtoStr_PadLeft_Zeroes(-12 , 3, "-12"); // negative + tst_XtoStr_PadLeft_Zeroes(-123 , 3, "-123"); // negative + tst_XtoStr_PadLeft_Zeroes(-1234 , 3, "-1234"); // negative + } void tst_XtoStr_PadLeft_Zeroes(int val, int zeros, String expd) {Tfds.Eq(expd, Int_.XtoStr_PadBgn(val, zeros));} + @Test public void parseOr_() { + tst_ParseOr("", -1); // empty + tst_ParseOr("123", 123); // single + tst_ParseOr("1a", -1); // fail + } void tst_ParseOr(String raw, int expd) {Tfds.Eq(expd, Int_.parse_or_(raw, -1));} + @Test public void Between() { + tst_Between(1, 0, 2, true); // simple true + tst_Between(3, 0, 2, false); // simple false + tst_Between(0, 0, 2, true); // bgn true + tst_Between(2, 0, 2, true); // end true + } void tst_Between(int val, int lhs, int rhs, boolean expd) {Tfds.Eq(expd, Int_.Between(val, lhs, rhs));} + @Test public void Xto_fmt() { + tst_XtoStr_fmt(1, "1"); + tst_XtoStr_fmt(1000, "1,000"); + } void tst_XtoStr_fmt(int v, String expd) {Tfds.Eq(expd, Int_.XtoStr_fmt(v, "#,###"));} + @Test public void AryRng() { + tst_AryRng(1, 3, Int_.Ary(1, 2, 3)); + } void tst_AryRng(int bgn, int end, int[] expd) {Tfds.Eq_ary(expd, Int_.AryRng(bgn, end));} + @Test public void Log10_pos() { + tst_Log10(0, 0); + tst_Log10(1, 0); + tst_Log10(9, 0); + tst_Log10(10, 1); + tst_Log10(100, 2); + tst_Log10(1000000, 6); + tst_Log10(1000000000, 9); + tst_Log10(Int_.MaxValue, 9); + } + @Test public void Log10_neg() { + tst_Log10(-1, 0); + tst_Log10(-10, -1); + tst_Log10(-100, -2); + tst_Log10(-1000000, -6); + tst_Log10(-1000000000, -9); + tst_Log10(Int_.MinValue, -9); + tst_Log10(Int_.MinValue + 1, -9); + } + void tst_Log10(int val, int expd) {Tfds.Eq(expd, Int_.Log10(val));} + @Test public void DigitCount() { + tst_DigitCount(0, 1); + tst_DigitCount(9, 1); + tst_DigitCount(100, 3); + tst_DigitCount(-1, 2); + tst_DigitCount(-100, 4); + } void tst_DigitCount(int val, int expd) {Tfds.Eq(expd, Int_.DigitCount(val), Int_.XtoStr(val));} + @Test public void Log10() { + tst_Log10( 0, 0); + tst_Log10( 1, 0); + tst_Log10( 2, 0); + tst_Log10( 10, 1); + tst_Log10( 12, 1); + tst_Log10( 100, 2); + tst_Log10( 123, 2); + tst_Log10( 1000, 3); + tst_Log10( 1234, 3); + tst_Log10( 10000, 4); + tst_Log10( 12345, 4); + tst_Log10( 100000, 5); + tst_Log10( 123456, 5); + tst_Log10( 1000000, 6); + tst_Log10( 1234567, 6); + tst_Log10( 10000000, 7); + tst_Log10( 12345678, 7); + tst_Log10( 100000000, 8); + tst_Log10( 123456789, 8); + tst_Log10( 1000000000, 9); + tst_Log10( 1234567890, 9); + tst_Log10(Int_.MaxValue, 9); + } + @Test public void Xto_int_hex_tst() { + Xto_int_hex("007C", 124); + } void Xto_int_hex(String raw, int expd) {Tfds.Eq(expd, Int_.Xto_int_hex(Bry_.new_ascii_(raw)));} + @Test public void Ary_parse() { + Ary_parse__tst("1,2,3" , 3, Int_.Ary_empty, 1, 2, 3); + Ary_parse__tst("123,321,213" , 3, Int_.Ary_empty, 123, 321, 213); + Ary_parse__tst(" 1, 2,3" , 3, Int_.Ary_empty, 1, 2, 3); + Ary_parse__tst("-1,+2,-3" , 3, Int_.Ary_empty, -1, 2, -3); + Ary_parse__tst(Int_.XtoStr(Int_.MinValue) , 1, Int_.Ary_empty, Int_.MinValue); + Ary_parse__tst(Int_.XtoStr(Int_.MaxValue) , 1, Int_.Ary_empty, Int_.MaxValue); + Ary_parse__tst("1,2" , 1, Int_.Ary_empty); + Ary_parse__tst("1" , 2, Int_.Ary_empty); + Ary_parse__tst("a" , 1, Int_.Ary_empty); + Ary_parse__tst("1-2," , 1, Int_.Ary_empty); + } + void Ary_parse__tst(String raw, int reqd_len, int[] or, int... expd) {Tfds.Eq_ary(expd, Int_.Ary_parse(raw, reqd_len, or));} +} diff --git a/100_core/src_110_primitive/gplx/Int_ary_.java b/100_core/src_110_primitive/gplx/Int_ary_.java new file mode 100644 index 000000000..31bcf28fd --- /dev/null +++ b/100_core/src_110_primitive/gplx/Int_ary_.java @@ -0,0 +1,95 @@ +/* +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; +public class Int_ary_ { + public static int[] Parse_list_or(byte[] src, int[] or) { + try { + if (Bry_.Len_eq_0(src)) return or; // null, "" should return [0] + int raw_len = src.length; + int[] rv = null; int rv_idx = 0, rv_len = 0; + int pos = 0; + int num_bgn = -1, num_end = -1; + boolean itm_done = false, itm_is_rng = false; + int rng_bgn = Int_.MinValue; + while (true) { + boolean pos_is_last = pos == raw_len; + if ( itm_done + || pos_is_last + ) { + if (num_bgn == -1) return or; // empty itm; EX: "1,"; "1,,2" + int num = Bry_.X_to_int_or(src, num_bgn, num_end, Int_.MinValue); + if (num == Int_.MinValue) return or; // not a number; parse failed + if (rv_len == 0) { // rv not init'd + rv_len = (raw_len / 2) + 1; // default rv_len to len of String / 2; + 1 to avoid fraction rounding down + rv = new int[rv_len]; + } + int add_len = 1; + if (itm_is_rng) { + add_len = num - rng_bgn + ListAdp_.Base1; + if (add_len == 0) return or; // bgn >= end; + } + if (add_len + rv_idx > rv_len) { // ary out of space; resize + rv_len = (add_len + rv_idx) * 2; + rv = (int[])Array_.Resize(rv, rv_len); + } + if (itm_is_rng) { + for (int i = rng_bgn; i <= num; i++) + rv[rv_idx++] = i; + } + else { + rv[rv_idx++] = num; + } + num_bgn = num_end = -1; + itm_done = itm_is_rng = false; + rng_bgn = Int_.MinValue; + if (pos_is_last) break; + } + byte b = src[pos]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + if (num_bgn == -1) // num_bgn not set + num_bgn = pos; + num_end = pos + 1; // num_end is always after pos; EX: "9": num_end = 1; "98,7": num_end=2 + break; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: // NOTE: parseNumList replaces ws with '', so "1 1" will become "11" + break; + case Byte_ascii.Comma: + if (pos == raw_len -1) return or; // eos; EX: "1," + if (num_bgn == -1) return or; // empty itm; EX: ","; "1,,2" + itm_done = true; + break; + case Byte_ascii.Dash: + if (pos == raw_len -1) return or; // eos; EX: "1-" + if (num_bgn == -1) return or; // no rng_bgn; EX: "-2" + rng_bgn = Bry_.X_to_int_or(src, num_bgn, pos, Int_.MinValue); + if (rng_bgn == Int_.MinValue) return or; + num_bgn = -1; + itm_is_rng = true; + break; + default: + return or; + } + ++pos; + } + return (rv_idx == rv_len) // on the off-chance that rv_len == rv_idx; EX: "1" + ? rv + : (int[])Array_.Resize(rv, rv_idx); + } catch (Exception e) {Err_.Noop(e); return or;} + } +} diff --git a/100_core/src_110_primitive/gplx/Int_ary__tst.java b/100_core/src_110_primitive/gplx/Int_ary__tst.java new file mode 100644 index 000000000..30f9978a0 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Int_ary__tst.java @@ -0,0 +1,44 @@ +/* +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; +import org.junit.*; +public class Int_ary__tst { + private Int_ary__fxt fxt = new Int_ary__fxt(); + @Test public void Parse_list_or_() { + fxt.Test_Parse_list_or("1", 1); + fxt.Test_Parse_list_or("123", 123); + fxt.Test_Parse_list_or("1,2,123", 1, 2, 123); + fxt.Test_Parse_list_or("1,2,12,123", 1, 2, 12, 123); + fxt.Test_Parse_list_or("1-5", 1, 2, 3, 4, 5); + fxt.Test_Parse_list_or("1-1", 1); + fxt.Test_Parse_list_or("1-3,7,11-13,21", 1, 2, 3, 7, 11, 12, 13, 21); + + fxt.Test_Parse_list_empty("1 2"); // NOTE: MW would gen 12; treat as invalid + fxt.Test_Parse_list_empty("1,"); // eos + fxt.Test_Parse_list_empty("1,,2"); // empty comma + fxt.Test_Parse_list_empty("1-"); // eos + fxt.Test_Parse_list_empty("3-1"); // bgn > end + fxt.Test_Parse_list_empty("1,a,2"); + fxt.Test_Parse_list_empty("a-1,2"); + fxt.Test_Parse_list_empty("-1"); // no rng bgn + } +} +class Int_ary__fxt { + public void Test_Parse_list_empty(String raw) {Tfds.Eq_ary(Int_.Ary_empty, Int_ary_.Parse_list_or(Bry_.new_ascii_(raw), Int_.Ary_empty));} + public void Test_Parse_list_or(String raw, int... expd) {Tfds.Eq_ary(expd, Int_ary_.Parse_list_or(Bry_.new_ascii_(raw), Int_.Ary_empty));} +} diff --git a/100_core/src_110_primitive/gplx/Int_obj_ref.java b/100_core/src_110_primitive/gplx/Int_obj_ref.java new file mode 100644 index 000000000..bb0ea7a47 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Int_obj_ref.java @@ -0,0 +1,44 @@ +/* +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; +public class Int_obj_ref { + public int Val() {return val;} public Int_obj_ref Val_(int v) {val = v; return this;} int val; + public int Val_add() {val++; return val;} + public int Val_add_post() {return val++;} + public int Val_add(int v) {val += v; return val;} + public Int_obj_ref Val_zero_() {val = 0; return this;} + public Int_obj_ref Val_neg1_() {val = -1; return this;} + @Override public String toString() {return Int_.XtoStr(val);} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return val == ((Int_obj_ref)obj).Val();} + public static Int_obj_ref neg1_() {return new_(-1);} + public static Int_obj_ref zero_() {return new_(0);} + public static Int_obj_ref new_(int val) { + Int_obj_ref rv = new Int_obj_ref(); + rv.val = val; + return rv; + } Int_obj_ref() {} + + public static int[] Ary_xto_int_ary(Int_obj_ref[] ary) { + int len = ary.length; + int[] rv = new int[len]; + for (int i = 0; i < len; ++i) + rv[i] = ary[i].val; + return rv; + } +} diff --git a/100_core/src_110_primitive/gplx/Int_obj_val.java b/100_core/src_110_primitive/gplx/Int_obj_val.java new file mode 100644 index 000000000..32df5211e --- /dev/null +++ b/100_core/src_110_primitive/gplx/Int_obj_val.java @@ -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 . +*/ +package gplx; +public class Int_obj_val implements CompareAble { + public int Val() {return val;} int val; + @Override public String toString() {return Int_.XtoStr(val);} + @Override public int hashCode() {return val;} + @Override public boolean equals(Object obj) {return obj == null ? false : val == ((Int_obj_val)obj).Val();} + public int compareTo(Object obj) {Int_obj_val comp = (Int_obj_val)obj; return Int_.Compare(val, comp.val);} + public static Int_obj_val neg1_() {return new_(-1);} + public static Int_obj_val zero_() {return new_(0);} + public static Int_obj_val new_(int val) { + Int_obj_val rv = new Int_obj_val(); + rv.val = val; + return rv; + } Int_obj_val() {} +} diff --git a/100_core/src_110_primitive/gplx/Long_.java b/100_core/src_110_primitive/gplx/Long_.java new file mode 100644 index 000000000..2c6104a38 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Long_.java @@ -0,0 +1,110 @@ +/* +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; +public class Long_ { + public static final long MinValue = Long.MIN_VALUE; + public static final long MaxValue = Long.MAX_VALUE; + public static final long Neg1 = -1; + public static final int Log10Ary_len = 21; + public static long[] Log10Ary = new long[] + { 1, 10, 100, 1000, 10000 + , 100000, 1000000, 10000000, 100000000, 1000000000 + , Long_.Pow(10, 10), Long_.Pow(10, 11), Long_.Pow(10, 12), Long_.Pow(10, 13), Long_.Pow(10, 14) + , Long_.Pow(10, 15), Long_.Pow(10, 16), Long_.Pow(10, 17), Long_.Pow(10, 18), Long_.Pow(10, 19) + , Long_.MaxValue + }; + public static String XtoStr(long v) {return Long.toString(v);} + public static String XtoStr_PadBgn(long v, int reqdPlaces) {return String_.Pad(XtoStr(v), reqdPlaces, "0", true);} // ex: 1, 3 returns 001 + public static long parse_or_(String raw, int or) { + if (raw == null) return or; + try { + int rawLen = String_.Len(raw); + if (raw == null || rawLen == 0) return or; + long rv = 0, factor = 1; int tmp = 0; + for (int i = rawLen; i > 0; i--) { + tmp = Char_.To_int_or(String_.CharAt(raw, i - 1), Int_.MinValue); + if (tmp == Int_.MinValue) return or; + rv += (tmp * factor); + factor *= 10; + } + return rv; + } catch (Exception e) {Err_.Noop(e); return or;} + } + public static int Compare(long lhs, long rhs) { + if (lhs == rhs) return CompareAble_.Same; + else if (lhs < rhs) return CompareAble_.Less; + else return CompareAble_.More; + } + public static long parse_(String raw) {try {return Long.parseLong(raw);} catch(Exception e) {throw Err_.parse_type_exc_(e, long.class, raw);}} + public static long cast_(Object obj) {try {return (Long)obj;} catch(Exception e) {throw Err_.type_mismatch_exc_(e, long.class, obj);}} + public static long coerce_(Object v) { + try {String s = String_.as_(v); return s == null ? Long_.cast_(v) : Long_.parse_(s);} + catch (Exception e) {throw Err_.cast_(e, long.class, v);} + } + public static int FindIdx(long[] ary, long find_val) { + int ary_len = ary.length; + int adj = 1; + int prv_pos = 0; + int prv_len = ary_len; + int cur_len = 0; + int cur_idx = 0; + long cur_val = 0; + while (true) { + cur_len = prv_len / 2; + if (prv_len % 2 == 1) ++cur_len; + cur_idx = prv_pos + (cur_len * adj); + if (cur_idx < 0) cur_idx = 0; + else if (cur_idx >= ary_len) cur_idx = ary_len - 1; + cur_val = ary[cur_idx]; + if (find_val < cur_val) adj = -1; + else if (find_val > cur_val) adj = 1; + else if (find_val == cur_val) return cur_idx; + if (cur_len == 1) { + if (adj == -1 && cur_idx > 0) + return --cur_idx; + return cur_idx; + } + prv_len = cur_len; + prv_pos = cur_idx; + } + } + public static int DigitCount(long v) { + int adj = Int_.Base1; + if (v < 0) { + if (v == Long_.MinValue) return 19; // NOTE: Long_.MinValue * -1 = Long_.MinValue + v *= -1; + ++adj; + } + return FindIdx(Log10Ary, v) + adj; + } + public static long Pow(int val, int exp) { + long rv = val; + for (int i = 1; i < exp; i++) + rv *= val; + return rv; + } + public static long Int_merge(int hi, int lo) {return (long)hi << 32 | (lo & 0xFFFFFFFFL);} + public static int Int_split_lo(long v) {return (int)(v);} + public static int Int_split_hi(long v) {return (int)(v >> 32);} + public static long X_by_int(int v) {return (long)v;} +} +/* alternate for Int_merge does not work in java + public static long MergeInts(int lo, int hi) {return (uint)(hi << 32) | (lo & 0xffffffff);} + public static int SplitLo(long v) {return (int)(((ulong)v & 0x00000000ffffffff));} + public static int SplitHi(long v) {return (int)(((ulong)v & 0xffffffff00000000)) >> 32;} +*/ diff --git a/100_core/src_110_primitive/gplx/Long__tst.java b/100_core/src_110_primitive/gplx/Long__tst.java new file mode 100644 index 000000000..dc1c4aa99 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Long__tst.java @@ -0,0 +1,49 @@ +/* +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; +import org.junit.*; +public class Long__tst { + @Test public void DigitCount() { + tst_DigitCount(0, 1); + tst_DigitCount(1, 1); + tst_DigitCount(9, 1); + tst_DigitCount(10, 2); + tst_DigitCount(100, 3); + tst_DigitCount(10000, 5); + tst_DigitCount(100000, 6); + tst_DigitCount(1000000, 7); + tst_DigitCount(1000000000, 10); + tst_DigitCount(10000000000L, 11); + tst_DigitCount(100000000000L, 12); + tst_DigitCount(10000000000000000L, 17); + tst_DigitCount(-1, 2); + } void tst_DigitCount(long val, int expd) {Tfds.Eq(expd, Long_.DigitCount(val));} + @Test public void Int_merge() { + tst_Int_merge(123, 456, 528280977864L); + tst_Int_merge(123, 457, 528280977865L); + } + void tst_Int_merge(int hi, int lo, long expd) { + Tfds.Eq(expd, Long_.Int_merge(hi, lo)); + Tfds.Eq(hi, Long_.Int_split_hi(expd)); + Tfds.Eq(lo, Long_.Int_split_lo(expd)); + } + @Test public void parse_or_() { + parse_or_tst("10000000000", 10000000000L); + } + void parse_or_tst(String raw, long expd) {Tfds.Eq(expd, Long_.parse_or_(raw, -1));} +} diff --git a/100_core/src_110_primitive/gplx/Object_.java b/100_core/src_110_primitive/gplx/Object_.java new file mode 100644 index 000000000..f4c92fc1d --- /dev/null +++ b/100_core/src_110_primitive/gplx/Object_.java @@ -0,0 +1,41 @@ +/* +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; +public class Object_ { + public static final Object[] Ary_empty = new Object[0]; + public static Object[] Ary(Object... ary) {return ary;} + public static boolean Eq(Object lhs, Object rhs) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + else return lhs.equals(rhs); + } + public static Object Parse(String val, String valType) { + if (String_.Eq(valType, IntClassXtn.Key_const)) return Int_.parse_(val); + else return val; + } + public static String XtoStr_OrNull(Object v) {return v == null ? null : ToString_lang(v);} + public static String XtoStr_OrNullStr(Object v) {return v == null ? String_.Null_mark : ToString_lang(v);} + public static String XtoStr_OrEmpty(Object v) {return v == null ? String_.Empty : ToString_lang(v);} + static String ToString_lang(Object v) { + if (v == null) return null; + Class c = v.getClass(); + if (ClassAdp_.Eq(c, Bry_.ClassOf)) return String_.new_utf8_((byte[])v); + else if (ClassAdp_.Eq(c, String_.ClassOf)) return (String)v; + else return v.toString(); + } +} \ No newline at end of file diff --git a/100_core/src_110_primitive/gplx/Object__tst.java b/100_core/src_110_primitive/gplx/Object__tst.java new file mode 100644 index 000000000..9f9e91d50 --- /dev/null +++ b/100_core/src_110_primitive/gplx/Object__tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class Object__tst { + @Test public void Eq() { + tst_Eq(null, null, true); // both null + tst_Eq(5, 5, true); // both non-null + tst_Eq(5, null, false); // rhs non-null + tst_Eq(null, 5, false); // lhs non-null + } void tst_Eq(Object lhs, Object rhs, boolean expd) {Tfds.Eq(expd, Object_.Eq(lhs, rhs));} +} diff --git a/100_core/src_110_primitive/gplx/Short_.java b/100_core/src_110_primitive/gplx/Short_.java new file mode 100644 index 000000000..c21430b3a --- /dev/null +++ b/100_core/src_110_primitive/gplx/Short_.java @@ -0,0 +1,21 @@ +/* +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; +public class Short_ { + public static short cast_(Object obj) {try {return (Short)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, short.class, obj);}} +} diff --git a/100_core/src_110_primitive/gplx/String_.java b/100_core/src_110_primitive/gplx/String_.java new file mode 100644 index 000000000..5ee37db27 --- /dev/null +++ b/100_core/src_110_primitive/gplx/String_.java @@ -0,0 +1,523 @@ +/* +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; +import java.lang.*; +public class String_ implements GfoInvkAble { + public static final Class ClassOf = String.class; + public static final int Find_none = -1, Pos_neg1 = -1; + public static final String Null = null, Empty = "", Null_mark = "<>", Tab = "\t", Lf = "\n", CrLf = "\r\n"; + public static String cast_(Object v) {return (String)v;} + public static String as_(Object obj) {return obj instanceof String ? (String)obj : null;} + public static String new_ascii_(byte[] v) {return v == null ? null : new_ascii_(v, 0, v.length);} + public static String new_ascii_(byte[] v, int bgn, int end) { + try { + return v == null + ? null + : new String(v, bgn, end - bgn, "ASCII"); + } + catch (Exception e) {throw Err_.err_(e, "unsupported encoding");} + } + public static String new_utf8_(byte[] v) {return v == null ? null : new_utf8_(v, 0, v.length);} + public static String new_utf8_mid_safe_(byte[] v, int bgn, int end) {return v == null ? null : new_utf8_(v, bgn, end);} + public static String new_utf8_(byte[] v, int bgn, int end) { + try { + return v == null + ? null + : new String(v, bgn, end - bgn, "UTF-8"); + } + catch (Exception e) {throw Err_.err_(e, "unsupported encoding");} + } + public static String new_utf8_len_safe_(byte[] v, int bgn, int len) { + int v_len = v.length; + if (bgn + len > v_len) len = v_len - bgn; + return new_utf8_(v, bgn, bgn + len); + } + public static String[] Ary_add(String[]... arys) { + if (arys == null) return String_.Ary_empty; + int arys_len = arys.length; + int rv_len = 0; + for (int i = 0; i < arys_len; i++) { + String[] ary = arys[i]; + rv_len += ary.length; + } + int rv_idx = 0; + String[] rv = new String[rv_len]; + for (int i = 0; i < arys_len; i++) { + String[] ary = arys[i]; + int ary_len = ary.length; + for (int j = 0; j < ary_len; j++) + rv[rv_idx++] = ary[j]; + } + return rv; + } + public static boolean Len_gt_0(String s) {return s != null && s.length() > 0;} + public static boolean Len_eq_0(String s) {return s == null || s.length() == 0;} + public static int Len(String s) {return s.length();} + public static String Lower(String s) {return s.toLowerCase();} + public static String Upper(String s) {return s.toUpperCase();} + public static String CaseNormalize(boolean caseSensitive, String s) {return caseSensitive ? s : String_.Lower(s);} + public static String Trim(String s) {return s.trim();} + public static String Mid(String s, int bgn) {return s.substring(bgn);} + public static String Replace(String s, String find, String replace) {return s.replace(find, replace);} + public static char[] XtoCharAry(String s) {return s.toCharArray();} + public static char CharAt(String s, int i) {return s.charAt(i);} + public static int CodePointAt(String s, int i) {return s.codePointAt(i);} + public static boolean Has(String s, String find) {return s.indexOf(find) != String_.Find_none;} + public static boolean HasAtBgn(String s, String v) {return s.startsWith(v);} + public static boolean HasAtEnd(String s, String v) {return s.endsWith(v);} + public static int FindFwd(String s, String find) {return s.indexOf(find);} + public static int FindFwd(String s, String find, int pos) {return s.indexOf(find, pos);} + public static int FindBwd(String s, String find) {return s.lastIndexOf(find);} + public static int FindBwd(String s, String find, int pos) { + return s.lastIndexOf(find, pos); + } + public static int FindBetween(String s, String find, int bgn, int end) { + int rv = FindFwd(s, find, bgn); + return (rv > end) ? String_.Find_none : rv; + } + public static int FindAfter(String s, String find, int bgn) { + int rv = FindFwd(s, find, bgn); + return rv == String_.Find_none ? String_.Find_none : rv + Len(find); + } + public static int FindAfterRev(String s, String find, int pos) { + int rv = FindBwd(s, find, pos); + return rv == String_.Find_none ? String_.Find_none : rv + Len(find); + } + public static int Count(String s, String part) { + int count = 0, pos = -1; // -1 b/c first pass must be 0 (see pos + 1 below) + do { + pos = FindFwd(s, part, pos + 1); + if (pos == String_.Find_none) break; + count++; + } while (true); + return count; + } + public static boolean Eq(String lhs, String rhs) {return lhs == null ? rhs == null : lhs.equals(rhs);} + public static boolean EqAny(String lhs, String... rhsAry) { + for (int i = 0; i < rhsAry.length; i++) + if (Eq(lhs, rhsAry[i])) return true; + return false; + } + public static boolean EqNot(String lhs, String rhs) {return !Object_.Eq(lhs, rhs);} + public static boolean EqEmpty(String lhs, String rhs) {return lhs.equals("");} + public static String IfNullOrEmpty(String s, String or) {return s == null || s.length() == 0 ? or : s;} + public static int Compare(String lhs, String rhs) {return lhs.compareTo(rhs);} // NOTE: Compare instead of compareTo b/c javafy lowercases compareTo + public static int Compare_ignoreCase(String lhs, String rhs) { + if (lhs == null && rhs != null) return CompareAble_.Less; + else if (lhs != null && rhs == null) return CompareAble_.More; + else if (lhs == null && rhs == null) return CompareAble_.Same; + else return lhs.compareToIgnoreCase(rhs); + //#- + /* + if (lhs == null && rhs != null) return CompareAble_.Less; + else if (lhs != null && rhs == null) return CompareAble_.More; + else if (lhs == null && rhs == null) return CompareAble_.Same; + else return lhs.compareToIgnoreCase(rhs); + */ + } + public static int Compare_strict(String lhs, String rhs) { + int compare = String_.Compare(lhs, rhs); + if (compare == CompareAble_.Same) return CompareAble_.Same; + else if (compare < CompareAble_.Same) return CompareAble_.Less; + else /* (compare > CompareAble_.Same) */ return CompareAble_.More; + } + public static int Compare_byteAry(String lhs, String rhs) { + int lhsLen = lhs.length(), rhsLen = rhs.length(); + int aryLen = lhsLen < rhsLen ? lhsLen : rhsLen; + int[] lhsAry = XtoIntAry(lhs, aryLen), rhsAry = XtoIntAry(rhs, aryLen); + for (int i = 0; i < aryLen; i++) { + int comp = Int_.Compare(lhsAry[i], rhsAry[i]); + if (comp != CompareAble_.Same) return comp; + } + return Int_.Compare(lhsLen, rhsLen); + } + public static int[] XtoIntAry(String s, int len) { + int[] rv = new int[len]; + for (int i = 0; i < len; i++) + rv[i] = (int)s.charAt(i); + return rv; + } + public static String Coalesce(String s, String alt) {return Len(s) == 0 ? alt : s;} + public static boolean In(String s, String... ary) { + for (String itm : ary) + if (String_.Eq(s, itm)) return true; + return false; + } + + public static String new_charAry_(char[] ary, int bgn, int len) {return new String(ary, bgn, len);} + public static String Mid(String s, int bgn, int end) { + try {return Mid_lang(s, bgn, end - bgn);} + catch (Exception e) { + int len = s == null ? 0 : Len(s); + Err err = Err_.new_("unknown error").Add("s", s).Add("bgn", bgn).Add("end", end).Add("len", len).Add("e", Err_.Message_lang(e)); + if (s == null) err.Hdr_("s is null"); + else if (bgn > end) err.Hdr_("@bgn > @end"); + else if (bgn < 0 || bgn >= len) err.Hdr_("@bgn is invalid"); + else if (end < 0 || end > len) err.Hdr_("@end is invalid"); + throw err; + } + } + public static String MidByLenSafe(String s, int bgn, int len) { + if (bgn + len >= Len(s)) len = Len(s) - bgn; + return Mid_lang(s, bgn, len); + } + public static String MidByLen(String s, int bgn, int len) {return Mid_lang(s, bgn, len);} + public static String GetStrBefore(String s, String spr) { + int sprPos = String_.FindFwd(s, spr); if (sprPos == String_.Find_none) throw Err_.new_("could not find spr").Add("s", s).Add("spr", spr); + return Mid(s, 0, sprPos); + } + public static String GetStrAfter(String s, String spr) { + int sprPos = String_.FindFwd(s, spr); if (sprPos == String_.Find_none) throw Err_.new_("could not find spr").Add("s", s).Add("spr", spr); + return Mid(s, sprPos + 1); + } + public static String LimitToFirst(String s, int len) { + if (len < 0) throw Err_arg.cannotBe_("< 0", "len", len); + int sLen = Len(s); if (len > sLen) return s; + return Mid_lang(s, 0, len); + } + public static String LimitToLast(String s, int len) { + if (len < 0) throw Err_arg.cannotBe_("< 0", "len", len); + int sLen = Len(s); if (len > sLen) return s; + return Mid_lang(s, sLen - len, len); + } + public static String DelBgn(String s, int count) { + if (count < 0) throw Err_arg.cannotBe_("< 0", "count", count); + if (s == null) throw Err_arg.null_("s"); + int len = Len(s); if (count > len) throw Err_arg.cannotBe_("> @len", "count", count).Add("len", len); + return String_.Mid(s, count); + } + public static String DelBgnIf(String s, String find) { + if (s == null) throw Err_arg.null_("s"); if (find == null) throw Err_arg.null_("find"); + return HasAtBgn(s, find) ? String_.Mid(s, Len(find)) : s; + } + public static String DelEnd(String s, int count) { + if (count < 0) throw Err_arg.cannotBe_("< 0", "count", count); + if (s == null) throw Err_arg.null_("s"); + int len = Len(s); if (count > len) throw Err_arg.cannotBe_("> len", "count", count).Add("len", len); + return Mid_lang(s, 0, len + -count); + } + public static String DelEndIf(String s, String find) { + if (s == null) throw Err_arg.null_("s"); if (find == null) throw Err_arg.null_("find"); + return HasAtEnd(s, find) ? Mid_lang(s, 0, Len(s) - Len(find)) : s; + } + public static String LowerFirst(String s) { + int len = Len(s); if (len == 0) return String_.Empty; + String char0 = Lower(Mid_lang(s, 0, 1)); + return len == 1 ? char0 : char0 + Mid(s, 1); + } + public static String UpperFirst(String s) { + int len = Len(s); if (len == 0) return String_.Empty; + String char0 = Upper(Mid_lang(s, 0, 1)); + return len == 1 ? char0 : char0 + Mid(s, 1); + } + public static String PadBgn(String s, int totalLen, String pad) {return Pad(s, totalLen, pad, true);} + public static String PadEnd(String s, int totalLen, String pad) {return Pad(s, totalLen, pad, false);} + @gplx.Internal protected static String Pad(String s, int totalLen, String pad, boolean bgn) { + int sLen = Len(s); + int padLen = totalLen - sLen; if (padLen < 0) return s; + String_bldr sb = String_bldr_.new_(); + if (!bgn) sb.Add(s); + for (int i = 0; i < padLen; i++) + sb.Add(pad); + if (bgn) sb.Add(s); + return sb.XtoStr(); + } + public static String TrimEnd(String s) {if (s == null) return null; + int len = String_.Len(s); + if (len == 0) return s; + int last = len; + for (int i = len; i > 0; i--) { + char c = s.charAt(i - 1); + last = i; + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { + break; + } + } + return (last == len) ? s : Mid_lang(s, 0, last); + } + public static String Repeat(String s, int count) { + if (count < 0) throw Err_.new_("count cannot be negative").Add("count", count).Add("s", s); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < count; i++) + sb.Add(s); + return sb.XtoStr(); + } + public static String Insert(String s, int pos, String toInsert) { + if (pos < 0 || pos >= String_.Len(s)) throw Err_.new_("String_.Insert failed; pos invalid").Add("pos", pos).Add("s", s).Add("toInsert", toInsert); + return s.substring(0, pos) + toInsert + s.substring(pos); + } + public static String Format(String fmt, Object... args) {return Format_do(fmt, args);} + public static String FormatOrEmptyStrIfNull(String fmt, Object arg) {return arg == null ? "" : Format(fmt, arg);} + public static String Concat(char... ary) {return new String(ary);} + public static String Concat(String s1, String s2, String s3) {return s1 + s2 + s3;} + public static String Concat(String... ary) { + String_bldr sb = String_bldr_.new_(); + for (String val : ary) + sb.Add(val); + return sb.XtoStr(); + } + public static String Concat_any(Object... ary) { + String_bldr sb = String_bldr_.new_(); + for (Object val : ary) + sb.Add_obj(val); + return sb.XtoStr(); + } + public static String ConcatWith_any(String separator, Object... ary) { + String_bldr sb = String_bldr_.new_(); + int aryLen = Array_.Len(ary); + for (int i = 0; i < aryLen; i++) { + if (i != 0) sb.Add(separator); + Object val = ary[i]; + sb.Add_obj(Object_.XtoStr_OrEmpty(val)); + } + return sb.XtoStr(); + } + public static String Concat_with_str(String spr, String... ary) { + String_bldr sb = String_bldr_.new_(); + int len = ary.length; + for (int i = 0; i < len; i++) { + if (i != 0) sb.Add(spr); + sb.Add_obj(ary[i]); + } + return sb.XtoStr(); + } + public static String Concat_lines_crlf(String... values) { + String_bldr sb = String_bldr_.new_(); + for (String val : values) + sb.Add(val).Add(String_.CrLf); + return sb.XtoStr(); + } + public static String Concat_lines_crlf_skipLast(String... values) { + String_bldr sb = String_bldr_.new_(); + for (String val : values) { + if (sb.Count() != 0) sb.Add(String_.CrLf); + sb.Add(val); + } + return sb.XtoStr(); + } + public static String Concat_lines_nl(String... values) { + String_bldr sb = String_bldr_.new_(); + for (String val : values) + sb.Add(val).Add("\n"); + return sb.XtoStr(); + } + public static String Concat_lines_nl_skip_last(String... ary) { + String_bldr sb = String_bldr_.new_(); + int ary_len = ary.length; int ary_end = ary_len - 1; + for (int i = 0; i < ary_len; i++) { + sb.Add(ary[i]); + if (i != ary_end) sb.Add("\n"); + } + return sb.XtoStr(); + } + + public static String[] Ary(String... ary) {return ary;} + public static String AryXtoStr(String... ary) { + String_bldr sb = String_bldr_.new_(); + for (String s : ary) + sb.Add(s).Add(";"); + return sb.XtoStr(); + } + public static final String[] Ary_empty = new String[0]; + public static String[] Split(String raw, char dlm) {return Split(raw, dlm, false);} + public static String[] Split(String raw, char dlm, boolean addEmptyIfDlmIsLast) { + ListAdp list = ListAdp_.new_(); String_bldr sb = String_bldr_.new_(); + int rawLen = String_.Len(raw); char c = '\0'; + for (int i = 0; i < rawLen; i++) { + c = String_.CharAt(raw, i); + if (c == dlm) { + if (!addEmptyIfDlmIsLast && sb.Count() == 0 && i == rawLen - 1) {} + else list.Add(sb.XtoStrAndClear()); + } + else + sb.Add(c); + } + if (sb.Count() > 0) + list.Add(sb.XtoStrAndClear()); + return list.XtoStrAry(); + } + public static String[] Split(String s, String separator) {return Split_do(s, separator, false);} + public static String[] SplitLines_crlf(String s) {return Split(s, Op_sys.Wnt.Nl_str());} + public static String[] SplitLines_nl(String s) {return Split(s, Op_sys.Lnx.Nl_str());} + public static String[] SplitLines_any(String s) {return Split_do(s, Op_sys.Lnx.Nl_str(), true);} + + static String Format_do(String s, Object[] ary) { + int aryLength = Array_.LenAry(ary); if (aryLength == 0) return s; // nothing to format + String_bldr sb = String_bldr_.new_(); + char bracketBgn = '{', bracketEnd = '}'; + String aryVal = null; char c, next; + int pos = 0; int textLength = Len(s); String numberStr = ""; boolean bracketsOn = false; + while (true) { + if (pos == textLength) break; + c = CharAt(s, pos); + if (bracketsOn) { // mode=bracketsOn + if (c == bracketBgn) { // first bracketBgn is fake; add bracketBgn and whatever is in numberStr + sb.Add(bracketBgn).Add(numberStr); + numberStr = ""; + } + else if (c == bracketEnd) { + int aryIdx = Int_.parse_or_(numberStr, Int_.MinValue); + if (aryIdx != Int_.MinValue && Int_.Between(aryIdx, 0, aryLength - 1)) // check (a) aryIdx is num; (b) aryIdx is in bounds + aryVal = Object_.XtoStr_OrEmpty(ary[aryIdx]); + else + aryVal = String_.Concat_any(bracketBgn, numberStr, bracketEnd); // not valid, just add String + sb.Add(aryVal); + bracketsOn = false; + numberStr = ""; + } + else // char=anythingElse + numberStr += c; + } + else { // mode=bracketsOff + if (c == bracketBgn || c == bracketEnd) { + boolean isEnd = pos == textLength - 1; + if (isEnd) + sb.Add(c); + else { + next = CharAt(s, pos + 1); + if (next == c) { // "{{" or "}}": escape by doubling + sb.Add(c); + pos++; + } + else + bracketsOn = true; + } + } + else // char=anythingElse + sb.Add(c); + } + pos++; + } + if (Len(numberStr) > 0) // unclosed bracket; add bracketBgn and whatever is in numberStr; ex: "{0" + sb.Add(bracketBgn).Add(numberStr); + return sb.XtoStr(); + } + static String[] Split_do(String s, String spr, boolean skipChar13) { + if (String_.Eq(s, "") // "".Split('a') return array with one member: "" + || String_.Eq(spr, "")) // "a".Split('\0') returns array with one member: "a" + return new String[] {s}; + ListAdp list = ListAdp_.new_(); String_bldr sb = String_bldr_.new_(); + int i = 0, sprPos = 0; boolean sprMatched = false; char spr0 = CharAt(spr, 0); + int textLength = Len(s); int sprLength = Len(spr); + while (true) { + if (sprMatched + || i == textLength) { // last pass; add whatever's in sb to list + list.Add(sb.XtoStrAndClear()); + if (sprMatched && i == textLength) list.Add(""); // if s ends with spr and last pass, add emptyString as last + sprMatched = false; + } + if (i == textLength) break; + char c = CharAt(s, i); + if (skipChar13 && c == (char)13) {i++; continue;} + if (c == spr0) { // matches first char of spr + sprPos = 1; + while (true) { + if (sprPos == sprLength) { // made it to end, must be match + sprMatched = true; + break; + } + if (i + sprPos == textLength) break; // ran out of s; handles partial match at end of String; ab+, +- + if (CharAt(s, i + sprPos) != CharAt(spr, sprPos)) break; // no match + sprPos++; + } + if (!sprMatched) // add partial match to sb + sb.Add(Mid_lang(s, i, sprPos)); + i += sprPos; + } + else { // no spr match; just add char, increment pos + sb.Add(c); + i++; + } + } + return (String[])list.XtoAry(String.class); + } + static String Mid_lang(String s, int bgn, int len) {return s.substring(bgn, bgn + len);} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Replace)) { + String s = m.ReadStr(GfsCore_.Arg_primitive), find = m.ReadStr("find"), replace = m.ReadStr("replace"); + if (ctx.Deny()) return this; + return Replace(s, find, replace); + } + else if (ctx.Match(k, Invk_Len)) { + String s = m.ReadStr(GfsCore_.Arg_primitive); + if (ctx.Deny()) return this; + return Len(s); + } + else if (ctx.Match(k, Invk_PadBgn)) { + String s = m.ReadStr(GfsCore_.Arg_primitive); int totalLen = m.ReadInt("totalLen"); String pad = m.ReadStr("pad"); + if (ctx.Deny()) return this; + return PadBgn(s, totalLen, pad); + } + else return GfoInvkAble_.Rv_unhandled; + } public static final String Invk_Replace = "Replace", Invk_Len = "Len", Invk_PadBgn = "PadBgn"; + public static final String_ Gfs = new String_(); + public static String Extract_after_bwd(String src, String dlm) { + int dlm_pos = String_.FindBwd(src, dlm); if (dlm_pos == String_.Find_none) return String_.Empty; + int src_len = String_.Len(src); if (dlm_pos == src_len - 1) return String_.Empty; + return String_.Mid(src, dlm_pos + 1, src_len); + } + public static String Replace_by_pos(String v, int del_bgn, int del_end, String repl) { + return String_.Mid(v, 0, del_bgn) + repl + String_.Mid(v, del_end, String_.Len(v)); + } + public static String read_(Object obj) {// NOTE: same as cast_; for consistent readability only + String rv = as_(obj); + if (rv == null && obj != null) throw Err_.type_mismatch_(String.class, obj); // NOTE: obj != null needed; EX: cast_(null) --> null + return rv; + } + public static String[] Ary(byte[]... ary) { + if (ary == null) return String_.Ary_empty; + int ary_len = ary.length; + String[] rv = new String[ary_len]; + for (int i = 0; i < ary_len; i++) + rv[i] = String_.new_utf8_(ary[i]); + return rv; + } + public static String [] Ary_filter(String[] src, String[] filter) { + HashAdp hash = HashAdp_.new_(); + int len = filter.length; + for (int i = 0; i < len; i++) { + String itm = filter[i]; + hash.AddReplace(itm, itm); + } + ListAdp rv = ListAdp_.new_(); + len = src.length; + for (int i = 0; i < len; i++) { + String itm = src[i]; + if (hash.Has(itm)) rv.Add(itm); + } + return rv.XtoStrAry(); + } + public static String[] Ary_flatten(String[][] src_ary) { + int trg_len = 0; + int src_len = Array_.Len(src_ary); + for (int i = 0; i < src_len; i++) { + String[] itm = src_ary[i]; + if (itm != null) trg_len += Array_.Len(itm); + } + String[] trg_ary = new String[trg_len]; + trg_len = 0; + for (int i = 0; i < src_len; i++) { + String[] itm = src_ary[i]; + if (itm == null) continue; + int itm_len = Array_.Len(itm); + for (int j = 0; j < itm_len; j++) + trg_ary[trg_len++] = itm[j]; + } + return trg_ary; + } +} diff --git a/100_core/src_110_primitive/gplx/String__tst.java b/100_core/src_110_primitive/gplx/String__tst.java new file mode 100644 index 000000000..7885279e1 --- /dev/null +++ b/100_core/src_110_primitive/gplx/String__tst.java @@ -0,0 +1,184 @@ +/* +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; +import org.junit.*; +public class String__tst { + @Test public void Len() { + tst_Len("", 0); + tst_Len("abc", 3); + } void tst_Len(String v, int expd) {Tfds.Eq(expd, String_.Len(v), "Len");} + + @Test public void LimitToFirst() { + tst_LimitToFirst("abc", 0, ""); + tst_LimitToFirst("abc", 1, "a"); + tst_LimitToFirst("abc", 2, "ab"); + tst_LimitToFirst("abc", 3, "abc"); + tst_LimitToFirst("abc", 4, "abc"); + err_LimitToFirst("abc", -1); + } + void tst_LimitToFirst(String s, int v, String expd) {Tfds.Eq(expd, String_.LimitToFirst(s, v));} + void err_LimitToFirst(String s, int v) {try {String_.LimitToFirst(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err_arg.class); return;} Tfds.Fail_expdError();} + @Test public void LimitToLast() { + tst_LimitToLast("abc", 0, ""); + tst_LimitToLast("abc", 1, "c"); + tst_LimitToLast("abc", 2, "bc"); + tst_LimitToLast("abc", 3, "abc"); + tst_LimitToLast("abc", 4, "abc"); + err_LimitToLast("abc", -1); + } + void tst_LimitToLast(String s, int v, String expd) {Tfds.Eq(expd, String_.LimitToLast(s, v));} + void err_LimitToLast(String s, int v) {try {String_.LimitToLast(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err_arg.class); return;} Tfds.Fail_expdError();} + @Test public void DelBgn() { + tst_DelBgn("abc", 0, "abc"); + tst_DelBgn("abc", 1, "bc"); + tst_DelBgn("abc", 2, "c"); + tst_DelBgn("abc", 3, ""); + err_DelBgn(null, 0); + err_DelBgn("abc", 4); + } + void tst_DelBgn(String s, int v, String expd) {Tfds.Eq(expd, String_.DelBgn(s, v));} + void err_DelBgn(String s, int v) {try {String_.DelBgn(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err_arg.class); return;} Tfds.Fail_expdError();} + @Test public void DelBgnIf() { + tst_DelBgnIf("abc", "", "abc"); + tst_DelBgnIf("abc", "a", "bc"); + tst_DelBgnIf("abc", "ab", "c"); + tst_DelBgnIf("abc", "abc", ""); + tst_DelBgnIf("abc", "abcd", "abc"); + tst_DelBgnIf("abc", "bcd", "abc"); + err_DelBgnIf(null, "abc"); + err_DelBgnIf("abc", null); + } + void tst_DelBgnIf(String s, String v, String expd) {Tfds.Eq(expd, String_.DelBgnIf(s, v));} + void err_DelBgnIf(String s, String v) {try {String_.DelBgnIf(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err_arg.class); return;} Tfds.Fail_expdError();} + @Test public void DelEnd() { + tst_DelEnd("abc", 0, "abc"); + tst_DelEnd("abc", 1, "ab"); + tst_DelEnd("abc", 2, "a"); + tst_DelEnd("abc", 3, ""); + err_DelEnd(null, 0); + err_DelEnd("abc", 4); + } + void tst_DelEnd(String s, int v, String expd) {Tfds.Eq(expd, String_.DelEnd(s, v));} + void err_DelEnd(String s, int v) {try {String_.DelEnd(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err_arg.class); return;} Tfds.Fail_expdError();} + @Test public void DelEndIf() { + tst_DelEndIf("abc", "", "abc"); + tst_DelEndIf("abc", "c", "ab"); + tst_DelEndIf("abc", "bc", "a"); + tst_DelEndIf("abc", "abc", ""); + tst_DelEndIf("abc", "abcd", "abc"); + tst_DelEndIf("abc", "ab", "abc"); + err_DelEndIf(null, ""); + err_DelEndIf("", null); + } + void tst_DelEndIf(String s, String v, String expd) {Tfds.Eq(expd, String_.DelEndIf(s, v));} + void err_DelEndIf(String s, String v) {try {String_.DelEndIf(s, v);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err_arg.class); return;} Tfds.Fail_expdError();} + @Test public void MidByPos() { + tst_MidByPos("abc", 0, 0, ""); + tst_MidByPos("abc", 0, 1, "a"); + tst_MidByPos("abc", 0, 2, "ab"); + tst_MidByPos("abc", 0, 3, "abc"); + tst_MidByPos("abc", 2, 3, "c"); + err_MidByPos("abc", 1, 5); +// err_MidByPos("abc", 0, 4); + } + void tst_MidByPos(String s, int bgn, int end, String expd) {Tfds.Eq(expd, String_.Mid(s, bgn, end));} + void err_MidByPos(String s, int bgn, int end) {try {String_.Mid(s, bgn, end);} catch (Exception exc) {Tfds.Err_classMatch(exc, Err.class); return;} Tfds.Fail_expdError();} + @Test public void TrimEnd() { + tst_TrimEnd("a", "a"); + tst_TrimEnd("a ", "a"); + tst_TrimEnd("a\t", "a"); + tst_TrimEnd("a\n", "a"); + tst_TrimEnd("a\r", "a"); + tst_TrimEnd("a\r\n \t", "a"); + tst_TrimEnd(" a", " a"); + tst_TrimEnd(null, null); + } + void tst_TrimEnd(String s, String expd) {Tfds.Eq(expd, String_.TrimEnd(s));} + + @Test public void Count() { + String text = "0 0 0"; + Tfds.Eq(3, String_.Count(text, "0")); + } + @Test public void Has() { + String text = "find word"; + Tfds.Eq_true(String_.Has(text, "word")); + Tfds.Eq_false(String_.Has(text, "nothing")); + } + @Test public void Repeat() { + Tfds.Eq("333", String_.Repeat("3", 3)); + } + @Test public void Format() { + tst_Format("", ""); // empty + tst_Format("no args", "no args"); // no args + tst_Format("0", "{0}", 0); // one + tst_Format("0 and 1", "{0} and {1}", 0, 1); // many + tst_Format("{", "{{", 0); // escape bracketBgn + tst_Format("}", "}}", 0); // escape bracketEnd + tst_Format("{a0c}", "{a{0}c}", 0); // nested; + tst_Format("{a{b}c}", "{a{b}c}", 0); // invalid invalid + tst_Format("{1}", "{1}", 1); // invalid array index + tst_Format("{a} {b}", "{a} {b}", 0); // invalid many + tst_Format("{a}0{b}1", "{a}{0}{b}{1}", 0, 1); // invalid and valid + tst_Format("{0", "{0", 0); // invalid dangling + } void tst_Format(String expd, String fmt, Object... ary) {Tfds.Eq(expd, String_.Format(fmt, ary));} + @Test public void Split() { + tst_Split("ab", " ", "ab"); // no match -> return array with original input + tst_Split("ab cd", " ", "ab", "cd"); // separator.length = 1 + tst_Split("ab+!cd", "+!", "ab", "cd"); // separator.length = 2 + tst_Split("ab+!cd+!ef", "+!", "ab", "cd", "ef"); // terms = 3 + tst_Split("ab+!cd+!", "+!", "ab", "cd", ""); // closing separator + tst_Split("+!ab", "+!", "", "ab"); // opening separator + tst_Split("ab+cd+!ef", "+!", "ab+cd", "ef"); // ignore partial matches + tst_Split("ab+!cd+", "+!", "ab", "cd+"); // ignore partial matches; end of String + + // boundary + tst_Split("ab", "", "ab"); // separator.length = 0 -> return array with input as only member + tst_Split("", " ", ""); // empty input -> return array with empty input + + // acceptance + tst_Split("this\r\nis\na\rtest\r\n.", "\r\n", "this", "is\na\rtest", "."); + } void tst_Split(String text, String separator, String... expd) {Tfds.Eq_ary(expd, String_.Split(text, separator));} + @Test public void ConcatWith_any() { + tst_ConcatWith_any("a|b", "|", "a", "b"); // do not append final delimiter + tst_ConcatWith_any("a||c", "|", "a", null, "c"); // null + tst_ConcatWith_any("a|b", "|", Object_.Ary("a", "b")); // pass array as arg + } void tst_ConcatWith_any(String expd, String delimiter, Object... array) {Tfds.Eq(expd, String_.ConcatWith_any(delimiter, array));} + @Test public void Compare_byteAry() { + tst_Compare_byteAry("a", "a", CompareAble_.Same); + tst_Compare_byteAry("a", "b", CompareAble_.Less); + tst_Compare_byteAry("b", "a", CompareAble_.More); + tst_Compare_byteAry("ab", "ac", CompareAble_.Less); + tst_Compare_byteAry("ac", "ab", CompareAble_.More); + tst_Compare_byteAry("a", "ab", CompareAble_.Less); + tst_Compare_byteAry("ab", "a", CompareAble_.More); + tst_Compare_byteAry("101", "1-0-1", CompareAble_.More); // NOTE: regular String_.Compare returns Less in .NET, More in Java + tst_Compare_byteAry("1-0-1", "101 (album)", CompareAble_.Less); + } void tst_Compare_byteAry(String lhs, String rhs, int expd) {Tfds.Eq(expd, String_.Compare_byteAry(lhs, rhs));} + @Test public void FindBwd() { // WORKAROUND.CS:String.LastIndexOf returns -1 for multi-chars; + tst_FindRev("abc", "a", 0, 0); + tst_FindRev("abc", "ab", 0, 0); // 2 chars + tst_FindRev("abc", "abc", 0, 0); // 3 chars + tst_FindRev("ab", "abc", 0, -1); // out of index error + tst_FindRev("ababab", "ab", 2, 2); // make sure cs implementation doesn't pick up next + } void tst_FindRev(String s, String find, int pos, int expd) {Tfds.Eq(expd, String_.FindBwd(s, find, pos));} + @Test public void Extract_after_bwd() { + Extract_after_bwd_tst("a/b", "/", "b"); + Extract_after_bwd_tst("a/", "/", ""); + Extract_after_bwd_tst("a", "/", ""); + } void Extract_after_bwd_tst(String src, String dlm, String expd) {Tfds.Eq(expd, String_.Extract_after_bwd(src, dlm));} +} diff --git a/100_core/src_110_primitive/gplx/String_obj_ref.java b/100_core/src_110_primitive/gplx/String_obj_ref.java new file mode 100644 index 000000000..9553ab981 --- /dev/null +++ b/100_core/src_110_primitive/gplx/String_obj_ref.java @@ -0,0 +1,30 @@ +/* +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; +public class String_obj_ref { + public String Val() {return val;} public String_obj_ref Val_(String v) {val = v; return this;} private String val; + public String_obj_ref Val_null_() {return Val_(null);} + @Override public String toString() {return val;} + public static String_obj_ref empty_() {return new_("");} + public static String_obj_ref null_() {return new_(null);} + public static String_obj_ref new_(String val) { + String_obj_ref rv = new String_obj_ref(); + rv.val = val; + return rv; + } String_obj_ref() {} +} diff --git a/100_core/src_110_primitive/gplx/String_obj_val.java b/100_core/src_110_primitive/gplx/String_obj_val.java new file mode 100644 index 000000000..80ad4d27e --- /dev/null +++ b/100_core/src_110_primitive/gplx/String_obj_val.java @@ -0,0 +1,31 @@ +/* +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; +public class String_obj_val implements CompareAble { + public String Val() {return val;} private String val; + @Override public String toString() {return val;} + public int compareTo(Object obj) { + String_obj_val comp = (String_obj_val)obj; + return String_.Compare_strict(val, comp.val); + } + public static String_obj_val new_(String val) { + String_obj_val rv = new String_obj_val(); + rv.val = val; + return rv; + } String_obj_val() {} +} diff --git a/100_core/src_110_primitive/gplx/String_ring.java b/100_core/src_110_primitive/gplx/String_ring.java new file mode 100644 index 000000000..91c5aaf37 --- /dev/null +++ b/100_core/src_110_primitive/gplx/String_ring.java @@ -0,0 +1,61 @@ +/* +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; +public class String_ring { + String[] ary = String_.Ary_empty; + public int Len() {return len;} int len; + public String_ring Max_(int v) { + if (v != max) { + ary = new String[v]; + max = v; + } + return this; + } int max; + public void Clear() { + for (int i = 0; i < max; i++) { + ary[i] = null; + } + len = nxt = 0; + } + int nxt; + public void Push(String v) { + int idx = nxt++; + if (idx == max) { + idx = 0; + } + if (nxt == max) { + nxt = 0; + } + ary[idx] = v; + if (len < max) + ++len; + } + public String[] Xto_str_ary() { + String[] rv = new String[len]; + int ary_i = nxt - 1; + for (int rv_i = len - 1; rv_i > -1; rv_i--) { + if (ary_i == -1) { + ary_i = max - 1; + } + rv[rv_i] = ary[ary_i]; + --ary_i; + } + return rv; + } +// public String Get_at(int i) {return } +} diff --git a/100_core/src_110_primitive/gplx/String_ring_tst.java b/100_core/src_110_primitive/gplx/String_ring_tst.java new file mode 100644 index 000000000..5d52cd00e --- /dev/null +++ b/100_core/src_110_primitive/gplx/String_ring_tst.java @@ -0,0 +1,46 @@ +/* +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; +import org.junit.*; +public class String_ring_tst { + String_ring_fxt fxt = new String_ring_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Basic() { + fxt.Clear().Max_(3).Push_many("a") .Expd("a"); + fxt.Clear().Max_(3).Push_many("a", "b") .Expd("a", "b"); + fxt.Clear().Max_(3).Push_many("a", "b", "c") .Expd("a", "b", "c"); + fxt.Clear().Max_(3).Push_many("a", "b", "c", "d") .Expd("b", "c", "d"); + fxt.Clear().Max_(3).Push_many("a", "b", "c", "d", "e") .Expd("c", "d", "e"); + fxt.Clear().Max_(3).Push_many("a", "b", "c", "d", "e", "f") .Expd("d", "e", "f"); + } +} +class String_ring_fxt { + String_ring ring = new String_ring(); + public String_ring_fxt Clear() {ring.Clear(); return this;} + public String_ring_fxt Max_(int v) {ring.Max_(v); return this;} + public String_ring_fxt Push_many(String... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) + ring.Push(ary[i]); + return this; + } + public String_ring_fxt Expd(String... expd) { + Tfds.Eq_ary_str(expd, ring.Xto_str_ary()); + return this; + } +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_ary_dim2.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_ary_dim2.java new file mode 100644 index 000000000..162684012 --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_ary_dim2.java @@ -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 . +*/ +package gplx.brys; import gplx.*; +public class Bry_fmtr_arg_ary_dim2 implements Bry_fmtr_arg { + public Bry_fmtr_arg_ary_dim2 Data_(byte[][] v) {ary_dim2 = v; return this;} + public Bry_fmtr_arg_ary_dim2 Ary_dim2_(byte[][] v) {this.ary_dim2 = v; return this;} + public void XferAry(Bry_bfr trg, int idx) { + for (byte[] ary : ary_dim2) + trg.Add(ary); + } + public Bry_fmtr_arg_ary_dim2() {} byte[][] ary_dim2 = new byte[0][]; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bfr.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bfr.java new file mode 100644 index 000000000..1e5126984 --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bfr.java @@ -0,0 +1,23 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_bfr implements Bry_fmtr_arg { + public Bry_fmtr_arg_bfr Data_(Bry_bfr v) {bfr = v; return this;} + public void XferAry(Bry_bfr trg, int idx) {trg.Add_bfr_and_clear(bfr);} + public Bry_fmtr_arg_bfr(Bry_bfr bfr) {this.bfr = bfr;} Bry_bfr bfr; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bfr_retain.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bfr_retain.java new file mode 100644 index 000000000..e81a8e83e --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bfr_retain.java @@ -0,0 +1,23 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_bfr_retain implements Bry_fmtr_arg { + public Bry_fmtr_arg_bfr_retain Data_(Bry_bfr v) {bfr = v; return this;} + public void XferAry(Bry_bfr trg, int idx) {trg.Add_bfr(bfr);} + public Bry_fmtr_arg_bfr_retain(Bry_bfr bfr) {this.bfr = bfr;} Bry_bfr bfr; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bry.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bry.java new file mode 100644 index 000000000..e3e6e077e --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_bry.java @@ -0,0 +1,23 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_bry implements Bry_fmtr_arg { + public Bry_fmtr_arg_bry Data_(byte[] v) {ary = v; return this;} + public void XferAry(Bry_bfr trg, int idx) {trg.Add(ary);} + public Bry_fmtr_arg_bry(byte[] v) {this.ary = v;} byte[] ary; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_byt.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_byt.java new file mode 100644 index 000000000..0b5124d70 --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_byt.java @@ -0,0 +1,23 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_byt implements Bry_fmtr_arg { + public Bry_fmtr_arg_byt Data_(byte v) {byt = v; return this;} + public void XferAry(Bry_bfr trg, int idx) {trg.Add_byte(byt);} + public Bry_fmtr_arg_byt(byte byt) {this.byt = byt;} private byte byt; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_decimal_int.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_decimal_int.java new file mode 100644 index 000000000..bf38773ce --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_decimal_int.java @@ -0,0 +1,25 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_decimal_int implements Bry_fmtr_arg { + public int Val() {return val;} public Bry_fmtr_arg_decimal_int Val_(int v) {val = v; return this;} int val; + public Bry_fmtr_arg_decimal_int Places_(int v) {places = v; multiple = (int)Math_.Pow(10, v); return this;} int multiple = 1000, places = 3; + public void XferAry(Bry_bfr bfr, int idx) { + bfr.Add_int_variable(val / multiple).Add_byte(Byte_ascii.Dot).Add_int_fixed(val % multiple, places); + } +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_fmtr.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_fmtr.java new file mode 100644 index 000000000..d5ff31db0 --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_fmtr.java @@ -0,0 +1,25 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_fmtr implements Bry_fmtr_arg { + public Bry_fmtr_arg_fmtr Data_(Bry_fmtr v) {fmtr = v; return this;} + public void XferAry(Bry_bfr trg, int idx) { + fmtr.Bld_bfr(trg, arg_ary); + } + public Bry_fmtr_arg_fmtr(Bry_fmtr fmtr, Bry_fmtr_arg... arg_ary) {this.fmtr = fmtr; this.arg_ary = arg_ary;} Bry_fmtr fmtr; Bry_fmtr_arg[] arg_ary; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_int.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_int.java new file mode 100644 index 000000000..9c8bcc597 --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_int.java @@ -0,0 +1,23 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_int implements Bry_fmtr_arg { + public Bry_fmtr_arg_int Data_(int v) {val = Int_.cast_(v); val_digits = Int_.DigitCount(val); return this;} + public void XferAry(Bry_bfr trg, int idx) {trg.Add_int_fixed(val, val_digits);} + public Bry_fmtr_arg_int(int v) {this.val = v; this.val_digits = Int_.DigitCount(v);} int val, val_digits; +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_time.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_time.java new file mode 100644 index 000000000..53f0a614b --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_time.java @@ -0,0 +1,55 @@ +/* +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.brys; import gplx.*; +public class Bry_fmtr_arg_time implements Bry_fmtr_arg { + public Bry_fmtr_arg_time() { + units_len = units.length; + } + public long Seconds() {return seconds;} public Bry_fmtr_arg_time Seconds_(long v) {seconds = v; return this;} long seconds; + byte[][] segs = new byte[][] + { Bry_.new_ascii_("d") + , Bry_.new_ascii_("h") + , Bry_.new_ascii_("m") + , Bry_.new_ascii_("s") + }; + int[] units = new int[] {86400, 3600, 60, 1}; + int units_len; + byte[] spr = new byte[] {Byte_ascii.Space}; + public void XferAry(Bry_bfr bfr, int idx) { + if (seconds == 0) { // handle 0 separately (since it will always be < than units[*] + bfr.Add_int_fixed(0, 2).Add(segs[units_len - 1]); + return; + } + long val = seconds; + boolean dirty = false; + for (int i = 0; i < units_len; i++) { + long unit = units[i]; + long seg = 0; + if (val >= unit) { // unit has value; EX: 87000 > 86400, so unit is 1 day + seg = val / unit; + val = val - (seg * unit); + } + if (seg > 0 || dirty) { // dirty check allows for 0 in middle units (EX: 1h 0m 1s) + if (dirty) bfr.Add(spr); + if (seg < 10) bfr.Add_byte(Byte_ascii.Num_0); // 0 pad + bfr.Add_long_variable(seg).Add(segs[i]); + dirty = true; + } + } + } +} diff --git a/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_time_tst.java b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_time_tst.java new file mode 100644 index 000000000..54c43e980 --- /dev/null +++ b/100_core/src_110_primitive/gplx/brys/Bry_fmtr_arg_time_tst.java @@ -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 . +*/ +package gplx.brys; import gplx.*; +import org.junit.*; +public class Bry_fmtr_arg_time_tst { + @Test public void Basic() { + Time_fmtr_arg_fxt fxt = new Time_fmtr_arg_fxt().Clear(); + fxt.XferAry( 1, "01s"); // seconds + fxt.XferAry( 62, "01m 02s"); // minutes + fxt.XferAry( 3723, "01h 02m 03s"); // hours + fxt.XferAry( 93784, "01d 02h 03m 04s"); // days + fxt.XferAry( 0, "00s"); // handle 0 seconds + fxt.XferAry( 3601, "01h 00m 01s"); // handle 0 in middle unit + } +} +class Time_fmtr_arg_fxt { + public Time_fmtr_arg_fxt Clear() { + if (arg == null) arg = new Bry_fmtr_arg_time(); + return this; + } Bry_fmtr_arg_time arg; + public void XferAry(int seconds, String expd) { + Bry_bfr bfr = Bry_bfr.reset_(255); + arg.Seconds_(seconds); + arg.XferAry(bfr, 0); + Tfds.Eq(expd, bfr.XtoStr()); + } +} diff --git a/100_core/src_120_basicDataType/gplx/DateAdp.java b/100_core/src_120_basicDataType/gplx/DateAdp.java new file mode 100644 index 000000000..07dcdfd75 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DateAdp.java @@ -0,0 +1,152 @@ +/* +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; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.TimeZone; +import java.text.SimpleDateFormat; +public class DateAdp implements CompareAble, GfoInvkAble { + public int compareTo(Object obj) {DateAdp comp = (DateAdp)obj; return under.compareTo(comp.under);} + @Override public String toString() {return XtoStr_gplx_long();} + public String XtoStr_gplx() {return XtoStr_fmt("yyyyMMdd_HHmmss.fff");} + public String XtoStr_gplx_long() {return XtoStr_fmt("yyyy-MM-dd HH:mm:ss.fff");} + public String XtoStr_fmt_HHmmss() {return XtoStr_fmt("HH:mm:ss");} + public String XtoStr_fmt_HHmm() {return XtoStr_fmt("HH:mm");} + public String XtoStr_fmt_yyyy_MM_dd() {return XtoStr_fmt("yyyy-MM-dd");} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_XtoStr_fmt)) return XtoStr_fmt("yyyy-MM-dd HH:mm:ss"); + else if (ctx.Match(k, Invk_AddDays)) { + int days = m.ReadInt("days"); + if (ctx.Deny()) return this; + return this.Add_day(days); + } + else return GfoInvkAble_.Rv_unhandled; + } public static final String Invk_XtoStr_fmt = "XtoStr_fmt", Invk_AddDays = "Add_day"; + public int Segment(int segmentIdx) { + switch (segmentIdx) { + case DateAdp_.SegIdx_year: return this.Year(); + case DateAdp_.SegIdx_month: return this.Month(); + case DateAdp_.SegIdx_day: return this.Day(); + case DateAdp_.SegIdx_hour: return this.Hour(); + case DateAdp_.SegIdx_minute: return this.Minute(); + case DateAdp_.SegIdx_second: return this.Second(); + case DateAdp_.SegIdx_frac: return this.Frac(); + case DateAdp_.SegIdx_dayOfWeek: return this.DayOfWeek(); + case DateAdp_.SegIdx_weekOfYear: return this.WeekOfYear(); + case DateAdp_.SegIdx_dayOfYear: return this.DayOfYear(); + default: throw Err_.unhandled(segmentIdx); + } + } + public int[] XtoSegAry() { + int[] rv = new int[7]; + rv[DateAdp_.SegIdx_year] = this.Year(); + rv[DateAdp_.SegIdx_month] = this.Month(); + rv[DateAdp_.SegIdx_day] = this.Day(); + rv[DateAdp_.SegIdx_hour] = this.Hour(); + rv[DateAdp_.SegIdx_minute] = this.Minute(); + rv[DateAdp_.SegIdx_second] = this.Second(); + rv[DateAdp_.SegIdx_frac] = this.Frac(); + return rv; + } + public String XtoStr_fmt_yyyyMMdd_HHmmss() {return XtoStr_fmt("yyyyMMdd_HHmmss");} + public String XtoStr_fmt_yyyyMMdd_HHmmss_fff() {return XtoStr_fmt("yyyyMMdd_HHmmss.fff");} + public String XtoStr_fmt_yyyyMMdd_HHmm() {return XtoStr_fmt("yyyyMMdd_HHmm");} + public String XtoStr_fmt_yyyy_MM_dd_HH_mm() {return XtoStr_fmt("yyyy-MM-dd HH:mm");} + public String XtoStr_fmt_yyyy_MM_dd_HH_mm_ss() {return XtoStr_fmt("yyyy-MM-dd HH:mm:ss");} + public String XtoStr_fmt_iso_8561() {return XtoStr_fmt("yyyy-MM-dd HH:mm:ss");} + public static int Timezone_offset_test = Int_.MinValue; + public Calendar UnderDateTime() {return under;} Calendar under; + public int Year() {return under.get(Calendar.YEAR);} + public int Month() {return under.get(Calendar.MONTH) + Month_base0adj;} + public int Day() {return under.get(Calendar.DAY_OF_MONTH);} + public int Hour() {return under.get(Calendar.HOUR_OF_DAY);} + public int Minute() {return under.get(Calendar.MINUTE);} + public int Second() {return under.get(Calendar.SECOND);} + public int DayOfWeek() {return under.get(Calendar.DAY_OF_WEEK) - 1;} // -1 : Base0; NOTE: dotnet/php is also Sunday=0 + public int DayOfYear() {return under.get(Calendar.DAY_OF_YEAR);} + public int Timezone_offset() { + return Timezone_offset_test == Int_.MinValue // Timezone_offset_test not over-ridden + ? 0 + // ? under.getTimeZone().getOffset(this.Timestamp_unix()) / 1000 // divide by 1000 to convert from ms to seconds + : Timezone_offset_test + ; + } + public DateAdp XtoUtc() { + java.util.Date date = under.getTime(); + java.util.TimeZone tz = under.getTimeZone(); + long msFromEpochGmt = date.getTime(); + int offsetFromUTC = tz.getOffset(msFromEpochGmt); + Calendar gmtCal = Calendar.getInstance(); + gmtCal.setTimeInMillis(msFromEpochGmt + -offsetFromUTC); + return new DateAdp(gmtCal); + } + public DateAdp XtoLocal() { + java.util.Date date = under.getTime(); + java.util.TimeZone tz = under.getTimeZone(); + long msFromEpochGmt = date.getTime(); + int offsetFromUTC = tz.getOffset(msFromEpochGmt); + Calendar gmtCal = Calendar.getInstance(); + gmtCal.setTimeInMillis(msFromEpochGmt + offsetFromUTC); + return new DateAdp(gmtCal); + } + public long Timestamp_unix() { + long offsetFromUTC = (under.getTimeZone().getOffset(0)); + boolean dst = TimeZone.getDefault().inDaylightTime(under.getTime()); + long dst_adj = dst ? 3600000 : 0; + return (under.getTimeInMillis() + offsetFromUTC + dst_adj) / 1000; + } + public int WeekOfYear() {return under.get(Calendar.WEEK_OF_YEAR);} + public int Frac() {return under.get(Calendar.MILLISECOND);} + public DateAdp Add_frac(int val) {return CloneAndAdd(Calendar.MILLISECOND, val);} + public DateAdp Add_second(int val) {return CloneAndAdd(Calendar.SECOND, val);} + public DateAdp Add_minute(int val) {return CloneAndAdd(Calendar.MINUTE, val);} + public DateAdp Add_hour(int val) {return CloneAndAdd(Calendar.HOUR, val);} + public DateAdp Add_day(int val) {return CloneAndAdd(Calendar.DAY_OF_MONTH, val);} + public DateAdp Add_month(int val) {return CloneAndAdd(Calendar.MONTH, val);} + public DateAdp Add_year(int val) {return CloneAndAdd(Calendar.YEAR, val);} + DateAdp CloneAndAdd(int field, int val) { + Calendar clone = (Calendar)under.clone(); + clone.add(field, val); + return new DateAdp(clone); + } + public String XtoStr_fmt(String fmt) { + fmt = fmt.replace("f", "S"); + SimpleDateFormat sdf = new SimpleDateFormat(fmt); + return sdf.format(under.getTime()); + } + public String XtoStr_tz() { + SimpleDateFormat sdf = new SimpleDateFormat("Z"); + String time_zone = sdf.format(under.getTime()); + return String_.Mid(time_zone, 0, 3) + ":" + String_.Mid(time_zone, 3, String_.Len(time_zone)); + } + public boolean Eq(DateAdp v) {DateAdp comp = v; return Object_.Eq(under.getTimeInMillis(), comp.under.getTimeInMillis());} + public int Diff_days(DateAdp prev) { + long diff = this.under.getTimeInMillis() - prev.under.getTimeInMillis(); + return (int)(diff / (1000 * 60 * 60 * 24)); + } + public TimeSpanAdp Diff(DateAdp earlier) { + long diff = this.under.getTimeInMillis() - earlier.under.getTimeInMillis(); + return TimeSpanAdp_.fracs_(diff); + } + protected DateAdp(Calendar under) {this.under = under;} + protected DateAdp(int year, int month, int day, int hour, int minute, int second, int frac) { + this.under = new GregorianCalendar(year, month - Month_base0adj, day, hour, minute, second); + under.set(Calendar.MILLISECOND, frac); + } + public static final int Month_base0adj = 1; + } diff --git a/100_core/src_120_basicDataType/gplx/DateAdp_.java b/100_core/src_120_basicDataType/gplx/DateAdp_.java new file mode 100644 index 000000000..7f38261f5 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DateAdp_.java @@ -0,0 +1,113 @@ +/* +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; +import java.sql.Timestamp; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; +public class DateAdp_ implements GfoInvkAble { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Now)) return Now(); + else return GfoInvkAble_.Rv_unhandled; + } public static final String Invk_Now = "Now"; + public static final DateAdp MinValue = new DateAdp( 1, 1, 1, 0, 0, 0, 0); + public static final DateAdp MaxValue = new DateAdp(9999, 12, 31, 23, 59, 59, 999); + public static DateAdp Now() {return Tfds.Now_enabled() ? Tfds.Now() : new DateAdp(new GregorianCalendar());} + public static DateAdp new_(int year, int month, int day, int hour, int minute, int second, int frac) {return new DateAdp(year, month, day, hour, minute, second, frac);} + public static DateAdp seg_(int[] ary) { + int ary_len = ary.length; + int y = ary_len > 0 ? ary[0] : 1; + int M = ary_len > 1 ? ary[1] : 1; + int d = ary_len > 2 ? ary[2] : 1; + int h = ary_len > 3 ? ary[3] : 0; + int m = ary_len > 4 ? ary[4] : 0; + int s = ary_len > 5 ? ary[5] : 0; + int f = ary_len > 6 ? ary[6] : 0; + return new DateAdp(y, M, d, h, m, s, f); + } + public static DateAdp cast_(Object arg) {try {return (DateAdp)arg;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, DateAdp.class, arg);}} + public static DateAdp parse_iso8561(String raw) { // NOTE: for now, same as parse_gplx + int[] ary = date_parser.Parse_iso8651_like(raw); + if (ary[1] < 1 || ary[1] > 12) return DateAdp_.MinValue; // guard against invalid month + if (ary[2] < 1 || ary[2] > 31) return DateAdp_.MinValue; + return new DateAdp(ary[0], ary[1], ary[2], ary[3], ary[4], ary[5], ary[6]); + } + public static DateAdp parse_gplx(String raw) { + int[] ary = date_parser.Parse_iso8651_like(raw); + if (ary[1] < 1 || ary[1] > 12) return DateAdp_.MinValue; // guard against invalid month + if (ary[2] < 1 || ary[2] > 31) return DateAdp_.MinValue; + return new DateAdp(ary[0], ary[1], ary[2], ary[3], ary[4], ary[5], ary[6]); + } static DateAdp_parser date_parser = DateAdp_parser.new_(); + public static DateAdp dateTime_(GregorianCalendar v) {return new DateAdp(v);} + public static DateAdp dateTime_obj_(Object v) {return new DateAdp((GregorianCalendar)v);} + public static final DateAdp_ Gfs = new DateAdp_(); + + public static int DaysInMonth(DateAdp date) { + int rv = DaysInMonth_ary[date.Month() - Int_.Base1]; + if (rv == 28 && IsLeapYear(date.Year())) rv = 29; + return rv; + } static int [] DaysInMonth_ary = {31,28,31,30,31,30,31,31,30,31,30,31}; + public static boolean IsLeapYear(int year) { + if (year % 4 != 0) return false; + else if (year % 400 == 0) return true; + else if (year % 100 == 0) return false; + else return true; + } + public static DateAdp unixtime_utc_seconds_(long v) {return unixtime_utc_ms_(v * 1000);} + public static DateAdp db_(Object v) { + Timestamp ts = (Timestamp)v; + Calendar gc = Calendar.getInstance(); + gc.setTimeInMillis(ts.getTime()); + return new DateAdp(gc); + } + public static DateAdp parse_(String raw) { + SimpleDateFormat sdf = new SimpleDateFormat(); + Date d = null; + try {d = sdf.parse(raw);} + catch (ParseException e) {throw Err_.new_key_("parse", "failed to parse to DateAdp").Add("raw", raw);} + GregorianCalendar cal = (GregorianCalendar)Calendar.getInstance(); + cal.setTime(d); + return dateTime_(cal); + } + public static DateAdp parse_fmt(String raw, String fmt) { + fmt = fmt.replace('t', 'a'); // AM/PM + fmt = fmt.replace('f', 'S'); // milliseconds + SimpleDateFormat sdf = new SimpleDateFormat(fmt, Locale.US); + Date d = null; + try {d = sdf.parse(raw);} + catch (ParseException e) {throw Err_.new_key_("parse", "failed to parse to DateAdp").Add("raw", raw).Add("fmt", fmt);} + GregorianCalendar cal = (GregorianCalendar)Calendar.getInstance(); + cal.setTime(d); + return dateTime_(cal); + } + public static DateAdp unixtime_utc_ms_(long v) {return unixtime_lcl_ms_(v).XtoUtc();} + public static DateAdp unixtime_lcl_ms_(long v) { + GregorianCalendar c = new GregorianCalendar(); + c.setTimeInMillis(v); + return new DateAdp(c); + } + public static final int SegIdx_year = 0, SegIdx_month = 1, SegIdx_day = 2, SegIdx_hour = 3, SegIdx_minute = 4, SegIdx_second = 5, SegIdx_frac = 6, SegIdx_dayOfWeek = 7, SegIdx_weekOfYear = 8, SegIdx_dayOfYear = 9, SegIdx__max = 10; + public static final String Fmt_iso8561_date_time = "yyyy-MM-dd HH:mm:ss"; + public static String Xto_str_fmt_or(DateAdp v, String fmt, String or) { + return v == null ? or : v.XtoStr_fmt(fmt); + } +} diff --git a/100_core/src_120_basicDataType/gplx/DateAdp__tst.java b/100_core/src_120_basicDataType/gplx/DateAdp__tst.java new file mode 100644 index 000000000..450292045 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DateAdp__tst.java @@ -0,0 +1,68 @@ +/* +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; +import org.junit.*; +public class DateAdp__tst { + @Test public void Parse_gplx() { + tst_Parse_gplx("99991231_235959.999", "99991231_235959.999"); + tst_Parse_gplx("20090430_213200.123", "20090430_213200.123"); + tst_Parse_gplx("20090430_213200" , "20090430_213200.000"); + tst_Parse_gplx("20090430" , "20090430_000000.000"); + } + @Test public void Parse_separators() { + tst_Parse_gplx("2009-04-30 21:32:00.123", "20090430_213200.123"); + tst_Parse_gplx("2009-04-30 21:32:00" , "20090430_213200.000"); + tst_Parse_gplx("2009-04-30" , "20090430_000000.000"); + } + @Test public void DayOfWeek() { + tst_DayOfWeek("2012-01-18", 3); //3=Wed + } void tst_DayOfWeek(String raw, int expd) {Tfds.Eq(expd, DateAdp_.parse_gplx(raw).DayOfWeek());} + @Test public void WeekOfYear() { + tst_WeekOfYear("2006-02-01", 5); // 1-1:Sun;2-1:Wed + tst_WeekOfYear("2007-02-01", 5); // 1-1:Mon;2-1:Thu + tst_WeekOfYear("2008-02-01", 5); // 1-1:Tue;2-1:Fri + tst_WeekOfYear("2009-02-01", 6); // 1-1:Thu;2-1:Sun + tst_WeekOfYear("2010-02-01", 6); // 1-1:Fri;2-1:Mon + tst_WeekOfYear("2011-02-01", 6); // 1-1:Sat;2-1:Tue + } void tst_WeekOfYear(String raw, int expd) {Tfds.Eq(expd, DateAdp_.parse_gplx(raw).WeekOfYear());} + @Test public void DayOfYear() { + tst_DayOfYear("2012-01-01", 1); + tst_DayOfYear("2012-02-29", 60); + tst_DayOfYear("2012-12-31", 366); + } void tst_DayOfYear(String raw, int expd) {Tfds.Eq(expd, DateAdp_.parse_gplx(raw).DayOfYear());} + @Test public void Timestamp_unix() { + tst_Timestamp_unix("1970-01-01 00:00:00", 0); + tst_Timestamp_unix("2012-01-01 00:00:00", 1325376000); + } void tst_Timestamp_unix(String raw, long expd) {Tfds.Eq(expd, DateAdp_.parse_gplx(raw).Timestamp_unix());} + @Test public void DaysInMonth() { + tst_DaysInMonth("2012-01-01", 31); + tst_DaysInMonth("2012-02-01", 29); + tst_DaysInMonth("2012-04-01", 30); + tst_DaysInMonth("2011-02-01", 28); + } void tst_DaysInMonth(String raw, int expd) {Tfds.Eq(expd, DateAdp_.DaysInMonth(DateAdp_.parse_gplx(raw)));} + @Test public void XtoUtc() { + tst_XtoUtc("2012-01-01 00:00", "2012-01-01 05:00"); //4=Wed + } void tst_XtoUtc(String raw, String expd) {Tfds.Eq(expd, DateAdp_.parse_gplx(raw).XtoUtc().XtoStr_fmt_yyyy_MM_dd_HH_mm());} + + void tst_Parse_gplx(String raw, String expd) { + DateAdp date = DateAdp_.parse_gplx(raw); + String actl = date.XtoStr_gplx(); + Tfds.Eq(expd, actl); + } + DateAdp_parser bldr = DateAdp_parser.new_(); +} diff --git a/100_core/src_120_basicDataType/gplx/DateAdp_parser.java b/100_core/src_120_basicDataType/gplx/DateAdp_parser.java new file mode 100644 index 000000000..be109e330 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DateAdp_parser.java @@ -0,0 +1,120 @@ +/* +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; +public class DateAdp_parser { + public int[] Parse_iso8651_like(String raw_str) {Parse_iso8651_like(tmp_rv, raw_str); return tmp_rv;} int[] tmp_rv = new int[7]; + public void Parse_iso8651_like(int[] rv, String raw_str) { + byte[] raw_bry = Bry_.new_utf8_(raw_str); + Parse_iso8651_like(rv, raw_bry, 0, raw_bry.length); + } + public void Parse_iso8651_like(int[] rv, byte[] src, int bgn, int end) { + /* 1981-04-05T14:30:30.000-05:00 ISO 8601: http://en.wikipedia.org/wiki/ISO_8601 + 1981.04.05 14.30.30.000-05.00 ISO 8601 loose + 99991231_235959.999 gplx + yyyy-MM-ddTHH:mm:ss.fffzzzz Format String */ + int rv_len = rv.length; + for (int i = 0; i < rv_len; i++) + rv[i] = 0; // .Clear + int pos = bgn, rv_idx = 0, int_len = 0, max_len = max_lens[rv_idx]; + while (true) { + int int_val = -1; + byte b = pos < end ? src[pos] : Byte_ascii.Nil; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + int_val = b - Byte_ascii.Num_0; // convert ascii to val; EX: 49 -> 1 + int_len = int_bldr.Add(int_val); + break; + } + if ( (int_val == -1 && int_len > 0) // char is not number && number exists (ignore consecutive delimiters: EX: "1981 01") + || int_len == max_len) { // max_len reached; necessary for gplxFormat + rv[rv_idx++] = int_bldr.BldAndClear(); + if (rv_idx == 7) break; // past frac; break; + int_len = 0; + max_len = max_lens[rv_idx]; + } + if (pos == end) break; + ++pos; + } + } + int[] max_lens = new int[] {4/*y*/,2/*M*/,2/*d*/,2/*H*/,2/*m*/,2/*s*/,3/*S*/,0}; + IntBldr int_bldr = IntBldr.new_(4); + public static DateAdp_parser new_() {return new DateAdp_parser();} DateAdp_parser() {} +} +class IntBldr { + public int Add(char c) { + if (idx > digitsLen - 1) throw Err_.missing_idx_(idx, digitsLen); + digits[idx++] = XbyChar(c); + return idx; + } + public int Add(int i) { + if (idx > digitsLen - 1) throw Err_.missing_idx_(idx, digitsLen); + digits[idx++] = i; + return idx; + } + public int Parse(String raw) { + ParseStr(raw); + try {return Bld();} + catch (Exception exc) {throw Err_.parse_type_exc_(exc, int.class, raw);} + } + public boolean ParseTry(String raw) { + ParseStr(raw); + for (int i = 0; i < idx; i++) + if (digits[i] < 0) return false; + return true; + } + public int Bld() { + int rv = 0, exponent = 1; + for (int i = idx - 1; i > -1; i--) { + int digit = digits[i]; + if (digit < 0) throw Err_.new_("invalid char").Add("char", (char)-digits[i]).Add("ascii", -digits[i]); + rv += digit * exponent; + exponent *= 10; + } + return sign * rv; + } + public int BldAndClear() { + int rv = Bld(); + this.Clear(); + return rv; + } + public void Clear() {idx = 0; sign = 1;} + void ParseStr(String raw) { + this.Clear(); + int rawLength = String_.Len(raw); + for (int i = 0; i < rawLength; i++) { + char c = String_.CharAt(raw, i); + if (i == 0 && c == '-') + sign = -1; + else + Add(c); + } + } + int XbyChar(char c) { + if (c == '0') return 0; else if (c == '1') return 1; else if (c == '2') return 2; else if (c == '3') return 3; else if (c == '4') return 4; + else if (c == '5') return 5; else if (c == '6') return 6; else if (c == '7') return 7; else if (c == '8') return 8; else if (c == '9') return 9; + else return -(int)c; // error handling: don't throw error; store ascii value in digit and throw if needed + } + int[] digits; int idx, digitsLen; int sign = 1; + public static IntBldr new_(int digitsMax) { + IntBldr rv = new IntBldr(); + rv.digits = new int[digitsMax]; + rv.digitsLen = digitsMax; + return rv; + } +} diff --git a/100_core/src_120_basicDataType/gplx/DateAdp_parser_tst.java b/100_core/src_120_basicDataType/gplx/DateAdp_parser_tst.java new file mode 100644 index 000000000..a85f2dd31 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DateAdp_parser_tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class DateAdp_parser_tst { + @Before public void init() {} DateAdp_parser_fxt fxt = new DateAdp_parser_fxt(); + @Test public void Parse_gplx() { + fxt.Test_Parse_iso8651_like("2000-01-02T03:04:05.006-05:00" , 2000, 1, 2, 3, 4, 5, 6); + fxt.Test_Parse_iso8651_like("2000-01-02" , 2000, 1, 2, 0, 0, 0, 0); + } +} +class DateAdp_parser_fxt { + DateAdp_parser parser = DateAdp_parser.new_(); int[] actl = new int[7]; + public void Test_Parse_iso8651_like(String s, int... expd) { + byte[] bry = Bry_.new_ascii_(s); + parser.Parse_iso8651_like(actl, bry, 0, bry.length); + Tfds.Eq_ary(expd, actl, s); + } +} diff --git a/100_core/src_120_basicDataType/gplx/DecimalAdp.java b/100_core/src_120_basicDataType/gplx/DecimalAdp.java new file mode 100644 index 000000000..ff9574c06 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DecimalAdp.java @@ -0,0 +1,64 @@ +/* +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; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.DecimalFormat; +public class DecimalAdp implements CompareAble { + public int compareTo(Object obj) {DecimalAdp comp = (DecimalAdp)obj; return under.compareTo(comp.under);} + + protected DecimalAdp(BigDecimal v) {this.under = v;} BigDecimal under; + protected DecimalAdp(int v) {this.under = new BigDecimal(v);} + public String XtoStr() { + BigDecimal tmp = under; + if (tmp.scale() > 14) tmp = tmp.setScale(14, RoundingMode.DOWN); // NOTE: setting to 14 to match PHP/C# values more closely; RoundingMode.Down for same reason; see E, Pi tests + return tmp .stripTrailingZeros() // NOTE: stripTrailingZeros for exp tests; EX: 120.0 -> 120; 0.01200000000000 -> .012 + .toPlainString(); // NOTE: toPlainString b/c stripTrailingZeros now converts 120 to 1.2E+2 (and any other value that is a multiple of 10) + } + public String XtoStr(String fmt) {return new DecimalFormat(fmt).format(under);} + @Override public String toString() {return under.toString();} + public boolean Eq(DecimalAdp v) {return v.under.doubleValue() == under.doubleValue();} + public BigDecimal XtoDecimal() {return under;} + public long XtoLong_Mult1000() {return under.movePointRight(3).longValue();} + public int Fraction1000() {return (int)(under.movePointRight(3).floatValue() % 1000);} + public double XtoDouble() {return under.doubleValue();} + public int XtoInt() {return (int)under.doubleValue();} + public long XtoLong() {return (long)under.doubleValue();} + public DecimalAdp Op_add(DecimalAdp v) {return new DecimalAdp(under.add(v.under, DecimalAdp_.Gplx_rounding_context));} + public DecimalAdp Op_subtract(DecimalAdp v) {return new DecimalAdp(under.subtract(v.under, DecimalAdp_.Gplx_rounding_context));} + public DecimalAdp Op_mult(DecimalAdp v) {return new DecimalAdp(under.multiply(v.under));} + public DecimalAdp Op_mult(double v) {return new DecimalAdp(under.multiply(new BigDecimal(v, DecimalAdp_.Gplx_rounding_context)));} + public DecimalAdp Op_mult(long v) {return new DecimalAdp(under.multiply(new BigDecimal(v)));} + public DecimalAdp Op_divide(DecimalAdp v) {return new DecimalAdp(under.divide(v.under, DecimalAdp_.Gplx_rounding_context));} + public DecimalAdp Op_mod(DecimalAdp v) {return new DecimalAdp(under.remainder(v.under, DecimalAdp_.Gplx_rounding_context));} + public DecimalAdp Op_sqrt() {return new DecimalAdp(new BigDecimal(Math_.Sqrt(under.doubleValue())));} + public DecimalAdp Op_abs() {return new DecimalAdp(under.abs(DecimalAdp_.Gplx_rounding_context));} + public DecimalAdp Op_pow(int v) {return new DecimalAdp(under.pow(v, DecimalAdp_.Gplx_rounding_context));} + public DecimalAdp Op_truncate_decimal() {return new DecimalAdp(under.intValue());} + public DecimalAdp Op_round(int v) {return new DecimalAdp(under.setScale(v, RoundingMode.HALF_UP));} + public boolean Comp_gte(DecimalAdp v) {return under.doubleValue() >= v.under.doubleValue();} + public boolean Comp_gte(int v) {return under.doubleValue() >= v;} + public boolean Comp_lte(DecimalAdp v) {return under.doubleValue() <= v.under.doubleValue();} + public boolean Comp_lte(int v) {return under.doubleValue() <= v;} + public boolean Comp_gt(DecimalAdp v) {return under.doubleValue() > v.under.doubleValue();} + public boolean Comp_gt(int v) {return under.doubleValue() > v;} + public boolean Comp_lt(DecimalAdp v) {return under.doubleValue() < v.under.doubleValue();} + public boolean Comp_lt(int v) {return under.doubleValue() < v;} + public boolean Eq(int v) {return under.doubleValue() == v;} + } diff --git a/100_core/src_120_basicDataType/gplx/DecimalAdp_.java b/100_core/src_120_basicDataType/gplx/DecimalAdp_.java new file mode 100644 index 000000000..64dbe7c3b --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DecimalAdp_.java @@ -0,0 +1,56 @@ +/* +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; +import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; public class DecimalAdp_ { + public static DecimalAdp as_(Object obj) {return obj instanceof DecimalAdp ? (DecimalAdp)obj : null;} + public static final DecimalAdp Zero = new DecimalAdp(0); + public static final DecimalAdp One = new DecimalAdp(1); + public static final DecimalAdp Neg1 = new DecimalAdp(-1); + public static final DecimalAdp Const_e = DecimalAdp_.double_(Math_.E); + public static final DecimalAdp Const_pi = DecimalAdp_.double_(Math_.Pi); + public static DecimalAdp base1000_(long v) {return divide_(v, 1000);} + public static DecimalAdp parts_1000_(long num, int frc) {return divide_((num * (1000)) + frc, 1000);} + public static DecimalAdp parts_(long num, int frc) { + // int log10 = frc == 0 ? 0 : (Math_.Log10(frc) + 1); + // int pow10 = (int)Math_.Pow(10, log10); + int pow10 = XtoPow10(frc); + return divide_((num * (pow10)) + frc, pow10); + } + public static DecimalAdp cast_(Object obj) {return (DecimalAdp)obj;} + static int XtoPow10(int v) { + if (v > -1 && v < 10) return 10; + else if (v > 9 && v < 100) return 100; + else if (v > 99 && v < 1000) return 1000; + else if (v > 999 && v < 10000) return 10000; + else if (v > 9999 && v < 100000) return 100000; + else if (v > 99999 && v < 1000000) return 1000000; + else if (v > 999999 && v < 10000000) return 10000000; + else if (v > 9999999 && v < 100000000) return 100000000; + else if (v > 99999999 && v < 1000000000) return 1000000000; + else throw Err_.new_("value must be between 0 and 1 billion").Add("v", v); + } + public static String CalcPctStr(long dividend, long divisor, String fmt) { + if (divisor == 0) return "%ERR"; + return DecimalAdp_.float_(Float_.Div(dividend, divisor) * 100).XtoStr(fmt) + "%"; + } + public static DecimalAdp divide_safe_(long lhs, long rhs) {return rhs == 0 ? Zero : divide_(lhs, rhs);} + public static DecimalAdp divide_(long lhs, long rhs) { return new DecimalAdp(new BigDecimal(lhs).divide(new BigDecimal(rhs), Gplx_rounding_context)); } public static DecimalAdp int_(int v) {return new DecimalAdp(new BigDecimal(v));} public static DecimalAdp long_(long v) {return new DecimalAdp(new BigDecimal(v));} + public static DecimalAdp float_(float v) {return new DecimalAdp(new BigDecimal(v));} public static DecimalAdp double_(double v) {return new DecimalAdp(new BigDecimal(v));} + public static DecimalAdp double_thru_str_(double v) {return new DecimalAdp(BigDecimal.valueOf(v));} + public static DecimalAdp db_(Object v) {return new DecimalAdp((BigDecimal)v);} public static DecimalAdp parse_(String raw) {return new DecimalAdp(new BigDecimal(raw));} public static DecimalAdp pow_10_(int v) {return new DecimalAdp(new BigDecimal(1).scaleByPowerOfTen(v));} + public static final MathContext RoundDownContext = new MathContext(0, RoundingMode.DOWN); static final MathContext Gplx_rounding_context = new MathContext(28, RoundingMode.HALF_UP); } diff --git a/100_core/src_120_basicDataType/gplx/DecimalAdp__tst.java b/100_core/src_120_basicDataType/gplx/DecimalAdp__tst.java new file mode 100644 index 000000000..6e762c442 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/DecimalAdp__tst.java @@ -0,0 +1,62 @@ +/* +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; +import org.junit.*; +public class DecimalAdp__tst { + @Test public void divide_() { + tst_divide_(1, 1000, "0.001"); + tst_divide_(1, 3, "0.33333333333333"); + tst_divide_(1, 7, "0.14285714285714"); + } void tst_divide_(int lhs, int rhs, String expd) {Tfds.Eq(expd, DecimalAdp_.divide_(lhs, rhs).XtoStr());} + @Test public void base1000_() { + tst_base1000_(1000, "1"); + tst_base1000_(1234, "1.234"); + tst_base1000_(123, "0.123"); + } void tst_base1000_(int val, String expd) {Tfds.Eq(expd, DecimalAdp_.base1000_(val).XtoStr());} + @Test public void parts_() { + tst_parts_(1, 0, "1"); + tst_parts_(1, 2, "1.2"); + tst_parts_(1, 23, "1.23"); + tst_parts_(123, 4567, "123.4567"); + } void tst_parts_(int num, int fracs, String expd) {Tfds.Eq(expd, DecimalAdp_.parts_(num, fracs).XtoStr());} + @Test public void parse_() { + tst_parse_("1", "1"); + tst_parse_("1.2", "1.2"); + tst_parse_("0.1", "0.1"); + } void tst_parse_(String raw, String expd) {Tfds.Eq(expd, DecimalAdp_.parse_(raw).XtoStr());} + @Test public void Truncate_decimal() { + tst_Truncate_decimal("1", "1"); + tst_Truncate_decimal("1.1", "1"); + tst_Truncate_decimal("1.9", "1"); + } void tst_Truncate_decimal(String raw, String expd) {Tfds.Eq(DecimalAdp_.parse_(expd).XtoStr(), DecimalAdp_.parse_(raw).Op_truncate_decimal().XtoStr());} + @Test public void Fraction1000() { + tst_Fraction1000(1, 1000, 1); // 0.001 + tst_Fraction1000(1, 3, 333); // 0.33333333 + tst_Fraction1000(1234, 1000, 234); // 1.234 + tst_Fraction1000(12345, 10000, 234); // 1.2345 + } void tst_Fraction1000(int lhs, int rhs, int expd) {Tfds.Eq(expd, DecimalAdp_.divide_(lhs, rhs).Fraction1000());} + @Test public void Lt() { + tst_Lt(1,123, 2, true); + tst_Lt(1,99999999, 2, true); + } void tst_Lt(int lhsNum, int lhsFrc, int rhs, boolean expd) {Tfds.Eq(expd, DecimalAdp_.parts_(lhsNum, lhsFrc).Comp_lt(rhs));} + @Test public void XtoStr_fmt() { + tst_XtoStr_fmt(1, 2, "0.0", "0.5"); + tst_XtoStr_fmt(1, 3, "0.0", "0.3"); + tst_XtoStr_fmt(10000, 7, "0,000.000", "1,428.571"); + } void tst_XtoStr_fmt(int l, int r, String fmt, String expd) {Tfds.Eq(expd, DecimalAdp_.divide_(l, r).XtoStr(fmt));} +} diff --git a/100_core/src_120_basicDataType/gplx/EnmMgr.java b/100_core/src_120_basicDataType/gplx/EnmMgr.java new file mode 100644 index 000000000..60ae1dec1 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/EnmMgr.java @@ -0,0 +1,67 @@ +/* +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; +public class EnmMgr { + public String BitRngSpr() {return bitRngSpr;} public EnmMgr BitRngSpr_(String val) {bitRngSpr = val; return this;} private String bitRngSpr = "+"; + public String Prefix() {return prefix;} public EnmMgr Prefix_(String val) {prefix = val; return this;} private String prefix; + public int BitRngBgn() {return bitRngBgn;} public EnmMgr BitRngBgn_(int val) {bitRngBgn = val; return this;} int bitRngBgn = 1; + public int BitRngEnd() {return bitRngEnd;} public EnmMgr BitRngEnd_(int val) {bitRngEnd = val; return this;} int bitRngEnd = Int_.MaxValue; + public void RegObj(int val, String raw, Object o) { + rawRegy.Add(raw, val); + valRegy.Add(val, raw); + objRegy.Add(val, o); + } + public Object Get(int val) {return objRegy.Fetch(val);} + public int GetVal(String raw) { + String[] ary = String_.Split(raw, bitRngSpr); + int rv = 0; + for (int i = 0; i < ary.length; i++) { + String term = String_.Trim(ary[i]); // ex: key.ctrl + key.a + if (prefix != null) term = String_.Replace(term, prefix, ""); + int cur = Int_.cast_(rawRegy.FetchOrFail(term)); + rv |= cur; + } + return rv; + } + public String GetStr(int v) { + String_bldr sb = String_bldr_.new_(); + int cur = v, curModifier = bitRngBgn; + while (true) { + if (cur == 0 + || curModifier > bitRngEnd // loop until all Modifers have been shifted out + ) break; + if ((cur & curModifier) == curModifier) { // cur has Modifier + AppendRaw(sb, curModifier); + cur ^= curModifier; // shift Modifier out + } + curModifier *= 2; // move to next Modifier; relies on Shift, Ctrl, Alt enum values + } + if (cur > 0 // cur is non-Modifier; NOTE: check needed for args that are just a Modifier; + || sb.Count() == 0) // cur is IptKey.None; cur == 0, but sb.length will also be 0 + AppendRaw(sb, cur); + return sb.XtoStr(); + } + void AppendRaw(String_bldr sb, int key) { + String raw = (String)valRegy.Fetch(key); + if (sb.Count() > 0) sb.Add(bitRngSpr); + if (prefix != null) sb.Add(prefix); + sb.Add(raw); + } + HashAdp rawRegy = HashAdp_.new_(), valRegy = HashAdp_.new_(), objRegy = HashAdp_.new_(); + public static EnmMgr new_() {return new EnmMgr();} EnmMgr() {} +} diff --git a/100_core/src_120_basicDataType/gplx/Enm_.java b/100_core/src_120_basicDataType/gplx/Enm_.java new file mode 100644 index 000000000..3531f032a --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Enm_.java @@ -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 . +*/ +package gplx; +public class Enm_ { + public static int XtoInt(Object enm) {return Ordinal_lang(enm);} + public static boolean HasInt(int val, int find) {return find == (val & find);} + public static int AddInt(int lhs, int rhs) {return lhs | rhs;} + public static int FlipInt(boolean enable, int val, int find) { + boolean has = find == (val & find); + return (has ^ enable) ? val ^ find : val; + } + static int Ordinal_lang(Object v) {return ((Enum)v).ordinal();} +} diff --git a/100_core/src_120_basicDataType/gplx/Io_url.java b/100_core/src_120_basicDataType/gplx/Io_url.java new file mode 100644 index 000000000..08d3e46b2 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Io_url.java @@ -0,0 +1,96 @@ +/* +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; +import gplx.ios.*; /*IoUrlInfo*/ +public class Io_url implements CompareAble, EqAble, ParseAble, GfoInvkAble { //_20101005 URL:doc/Io_url.txt + public IoUrlInfo Info() {return info;} IoUrlInfo info; + public String Raw() {return raw;} final String raw; + public byte[] RawBry() {return Bry_.new_utf8_(raw);} +// public byte[] Http_file_bry() { +// try {return Bry_.new_utf8_(String_.Concat(http_file_str, java.net.URLEncoder.encode(raw, "UTF-8")));} +// catch (Exception e) {throw Err_.err_(e, "Http_file_bry");} +// } + public String To_http_file_str() {return Http_file_str + Http_file_str_encoder.Encode_str(raw);} + public byte[] To_http_file_bry() {return Bry_.Add(Http_file_bry, Http_file_str_encoder.Encode_bry(raw));} + public static Url_encoder_interface Http_file_str_encoder; + + public static final String Http_file_str = "file:///"; + public static final int Http_file_len = String_.Len(Http_file_str); + public static final byte[] Http_file_bry = Bry_.new_ascii_(Http_file_str); + public boolean Type_dir() {return info.IsDir(raw);} public boolean Type_fil() {return !info.IsDir(raw);} + public Io_url OwnerDir() {return Io_url_.new_inf_(info.OwnerDir(raw), info);} + public Io_url OwnerRoot() {return Io_url_.new_inf_(info.OwnerRoot(raw), info);} + public String NameAndExt() {return info.NameAndExt(raw);} + public String NameAndExt_noDirSpr() {return this.Type_dir() ? this.NameOnly() : this.NameAndExt();} + public String NameOnly() {return info.NameOnly(raw);} + public String Ext() {return info.Ext(raw);} + public String Xto_api() {return info.Xto_api(raw);} + public String XtoCaseNormalized() {return String_.CaseNormalize(info.CaseSensitive(), raw);} + public Io_url GenSubDir(String subDirName) {return Io_url_.new_inf_(String_.Concat(raw, subDirName, info.DirSpr()), info);} + public Io_url GenSubDir_nest(String... ary) {return GenSub(false, ary);} + public Io_url GenSubFil(String val) {return Io_url_.new_inf_(raw + val, info);} + public Io_url GenSubFil_ary(String... ary) {return Io_url_.new_inf_(raw + String_.Concat(ary), info);} + public Io_url GenSubFil_nest(String... ary) {return GenSub(true, ary);} + public Io_url GenNewNameAndExt(String val) {return this.OwnerDir().GenSubFil(val);} + public Io_url GenNewNameOnly(String val) {return this.OwnerDir().GenSubFil(val + this.Ext());} + public Io_url GenNewExt(String val) {return this.OwnerDir().GenSubFil(this.NameOnly() + val);} + public String Gen_sub_path_for_os(String val) { + if (Op_sys.Cur().Tid_is_wnt()) val = String_.Replace(val, Op_sys.Lnx.Fsys_dir_spr_str(), Op_sys.Wnt.Fsys_dir_spr_str()); + return raw + val; + } + public String GenRelUrl_orEmpty(Io_url dir) { + String dirRaw = dir.Raw(); + return String_.HasAtBgn(raw, dirRaw) + ? String_.DelBgn(raw, String_.Len(dirRaw)) + : String_.Empty; + } + public ListAdp XtoNames() { + ListAdp list = ListAdp_.new_(); + Io_url cur = this; + while (!cur.EqNull()) { + list.Add(cur.NameAndExt_noDirSpr()); + cur = cur.OwnerDir(); + } + list.Reverse(); + return list; + } + public Io_url GenParallel(Io_url oldRoot, Io_url newRoot) {return newRoot.GenSubFil_ary(GenRelUrl_orEmpty(oldRoot));} + public boolean Eq(Object obj) {if (obj == null) return false; return String_.Eq(raw, ((Io_url)obj).raw);} + public boolean EqNull() {return this.Eq(Io_url_.Null);} + Io_url GenSub(boolean isFil, String[] ary) { + String_bldr sb = String_bldr_.new_().Add(raw); + int len = Array_.Len(ary); + for (int i = 0; i < len; i++) { + sb.Add(ary[i]); + if (isFil && i == len - 1) break; // do not add closing backslash if last term + sb.Add(info.DirSpr()); + } + return Io_url_.new_inf_(sb.XtoStr(), info); + } + public Object ParseAsObj(String raw) {return Io_url_.new_any_(raw);} + @Override public String toString() {return raw;} + public int compareTo(Object obj) {return CompareAble_.Compare_obj(raw, ((Io_url)obj).raw);} + @Override public boolean equals(Object obj) {return String_.Eq(raw, Io_url_.as_(obj).raw);} + @Override public int hashCode() {return raw.hashCode();} + @gplx.Internal protected Io_url(String raw, IoUrlInfo info) {this.raw = raw; this.info = info;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_to_http_file)) return To_http_file_str(); + else if (ctx.Match(k, Invk_gen_sub_path_for_os)) return Gen_sub_path_for_os(m.ReadStr("v")); + else return GfoInvkAble_.Rv_unhandled; + } static final String Invk_to_http_file = "to_http_file", Invk_gen_sub_path_for_os = "gen_sub_path_for_os"; +} diff --git a/100_core/src_120_basicDataType/gplx/Io_url_.java b/100_core/src_120_basicDataType/gplx/Io_url_.java new file mode 100644 index 000000000..c0f46a8b0 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Io_url_.java @@ -0,0 +1,92 @@ +/* +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; +import gplx.ios.*; /*IoUrlInfo_*/ +public class Io_url_ { + public static final Io_url Null = new Io_url("", IoUrlInfo_.Nil); + public static final Io_url NullPtr = null; + public static final Io_url Parser = new Io_url("", IoUrlInfo_.Nil); + public static Io_url as_(Object obj) {return obj instanceof Io_url ? (Io_url)obj : null;} + public static Io_url cast_(Object obj) {try {return (Io_url)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, Io_url.class, obj);}} + public static Io_url Usr() { + if (usr_dir == null) { + usr_dir = Op_sys.Cur().Tid_is_lnx() + ? Io_url_.new_inf_(String_.Format("/home/{0}/", Env_.UserName()), IoUrlInfo_.Lnx) + : Io_url_.new_inf_("C:\\", IoUrlInfo_.Wnt); + } + return usr_dir; + } static Io_url usr_dir; + public static Io_url Usr_Gplx() {return Usr().GenSubDir("gplx");} + public static Io_url mem_dir_(String raw) { + raw = EndsWith_or_add(raw, Op_sys.Lnx.Fsys_dir_spr_str()); + return new Io_url(raw, IoUrlInfoRegy._.Match(raw)); + } + public static Io_url mem_fil_(String raw) {return new_inf_(raw, IoUrlInfoRegy._.Match(raw));} + public static Io_url wnt_fil_(String raw) {return new_inf_(raw, IoUrlInfo_.Wnt);} + public static Io_url wnt_dir_(String raw) {return new_inf_(EndsWith_or_add(raw, Op_sys.Wnt.Fsys_dir_spr_str()), IoUrlInfo_.Wnt);} + public static Io_url lnx_fil_(String raw) {return new_inf_(raw, IoUrlInfo_.Lnx);} + public static Io_url lnx_dir_(String raw) {return new_inf_(EndsWith_or_add(raw, Op_sys.Lnx.Fsys_dir_spr_str()), IoUrlInfo_.Lnx);} + public static Io_url new_fil_(String raw) {return new_any_(raw);} + public static Io_url new_dir_(String raw) {return new_any_(raw);} // NOTE: for now, same as new_fil; stack overflow when doing new_dir + public static Io_url new_any_(String raw) {return new_inf_(raw, IoUrlInfoRegy._.Match(raw));} + public static Io_url new_inf_(String raw, IoUrlInfo info) {return String_.Eq(raw, "") ? Io_url_.Null : new Io_url(raw, info);} + public static Io_url http_any_(String src, boolean wnt) { + return new_any_(parse_http_file(src, wnt)); + } + private static String parse_http_file(String v, boolean wnt) { + byte[] v_bry = Bry_.new_utf8_(v); + int v_len = v_bry.length; + if (Bry_.HasAtBgn(v_bry, Io_url.Http_file_bry, 0, v_len)) { + byte[] rv = new byte[v_len - Io_url.Http_file_len]; + for (int i = 0; i < rv.length; i++) { + byte b = v_bry[i + Io_url.Http_file_len]; + if (wnt && b == Byte_ascii.Slash) b = Byte_ascii.Backslash; + rv[i] = b; + } + return String_.new_utf8_(rv); + } + return v; + } + + public static Io_url store_orFail_(SrlMgr mgr, String key, Io_url v) { + String s = mgr.SrlStrOr(key, v.Raw()); + return (mgr.Type_rdr()) ? Io_url_.new_any_(s) : v; + } + public static Io_url store_orSelf_(SrlMgr mgr, String key, Io_url v) { + String s = mgr.SrlStrOr(key, v.Raw()); + return (mgr.Type_rdr()) ? Io_url_.new_any_(s) : v; + } + public static Io_url rdrOr_(DataRdr rdr, String key, Io_url or) { + String val = rdr.ReadStrOr(key, null); if (val == null) return or; // NOTE: val == null also checks for rdr == DataRdr_.Null + return Io_url_.new_any_(val); + } + static String EndsWith_or_add(String raw, String endsWith) { + if (String_.HasAtEnd(raw, endsWith)) return raw; + return raw += endsWith; + } + public static Io_url Rel_dir(String s) {return IsAbs(s) ? Io_url_.new_dir_(s) : Env_.AppUrl().OwnerDir().GenSubDir(s);} + public static Io_url Rel_fil(String s) {return IsAbs(s) ? Io_url_.new_fil_(s) : Env_.AppUrl().OwnerDir().GenSubFil(s);} + static boolean IsAbs(String s) { + return String_.HasAtBgn(s, Op_sys.Lnx.Fsys_dir_spr_str()) + || (String_.Len(s) > 2 + && ( (String_.CharAt(s, 1) == ':' && String_.CharAt(s, 2) == '\\') + || (String_.CharAt(s, 1) == '\\' && String_.CharAt(s, 2) == '\\') + ) + ); + } +} diff --git a/100_core/src_120_basicDataType/gplx/KeyVal.java b/100_core/src_120_basicDataType/gplx/KeyVal.java new file mode 100644 index 000000000..642b71039 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/KeyVal.java @@ -0,0 +1,31 @@ +/* +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; +public class KeyVal implements XtoStrAble { + @gplx.Internal protected KeyVal(byte key_tid, Object k, Object v) {this.key_tid = key_tid; key = k; val = v;} + public String Key() {return Object_.XtoStr_OrNull(key);} + public byte Key_tid() {return key_tid;} private byte key_tid; + public Object Key_as_obj() {return key;} private Object key; + public KeyVal Key_(Object v) {this.key = v; return this;} + public Object Val() {return val;} public KeyVal Val_(Object v) {val = v; return this;} private Object val; + public String Val_to_str_or_empty() {return Object_.XtoStr_OrEmpty(val);} + public String Val_to_str_or_null() {return Object_.XtoStr_OrNull(val);} + public byte[] Val_to_bry() {return Bry_.new_utf8_(Object_.XtoStr_OrNull(val));} + @Override public String toString() {return XtoStr();} + public String XtoStr() {return Key() + "=" + Object_.XtoStr_OrNullStr(val);} +} diff --git a/100_core/src_120_basicDataType/gplx/KeyValHash.java b/100_core/src_120_basicDataType/gplx/KeyValHash.java new file mode 100644 index 000000000..6e4f8aa4c --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/KeyValHash.java @@ -0,0 +1,60 @@ +/* +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; +public class KeyValHash { + private OrderedHash hash = OrderedHash_.new_(); + public int Count() {return hash.Count();} + public KeyValHash Clear() {hash.Clear(); return this;} + public boolean Has(String key) {return hash.Has(key);} + public KeyVal FetchAt(int i) {return (KeyVal)hash.FetchAt(i);} + public Object FetchValOr(String key, Object or) {KeyVal rv = FetchOrNull(key); return rv == null ? or : rv.Val();} + public Object FetchValOrNull(String key) {return FetchValOr(key, null);} + public Object FetchValOrFail(String key) {return KeyVal_.as_(hash.FetchOrFail(key)).Val();} + public KeyValHash Add(KeyVal kv) {hash.Add(kv.Key(), kv); return this;} + public KeyValHash Add(String key, Object val) {hash.Add(key, KeyVal_.new_(key, val)); return this;} + public KeyValHash AddReplace(String key, Object val) {hash.AddReplace(key, KeyVal_.new_(key, val)); return this;} + public void Del(String key) {hash.Del(key);} + public KeyVal[] XtoAry() { + KeyVal[] rv = new KeyVal[this.Count()]; + for (int i = 0; i < rv.length; i++) + rv[i] = this.FetchAt(i); + return rv; + } + public static KeyValHash new_() {return new KeyValHash();} protected KeyValHash() {} + public static KeyValHash new_by_ary(KeyVal[] ary) { + int ary_len = ary.length; + KeyValHash rv = new KeyValHash(); + for (int i = 0; i < ary_len; i++) + rv.Add(ary[i]); + return rv; + } + public KeyVal FetchOrNull(String key) {return KeyVal_.as_(hash.Fetch(key));} + public static KeyValHash strAry_(String[] ary) {// needed for consoleLine + int aryLen = Array_.Len(ary); if (aryLen % 2 != 0) throw Err_.new_("array length must be divisible by 2").Add("aryLen", aryLen).Add("ary", String_.Concat_lines_crlf(ary)); + KeyValHash rv = new KeyValHash(); + String key = null; + for (int i = 0; i < aryLen; i++) { + if (i % 2 == 0) + key = ary[i]; + else + rv.Add(key, ary[i]); + } + return rv; + } + public static final KeyValHash Empty = new KeyValHash(); +} \ No newline at end of file diff --git a/100_core/src_120_basicDataType/gplx/KeyValHash_tst.java b/100_core/src_120_basicDataType/gplx/KeyValHash_tst.java new file mode 100644 index 000000000..0290c7646 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/KeyValHash_tst.java @@ -0,0 +1,36 @@ +/* +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; +import org.junit.*; +public class KeyValHash_tst { + @Test public void AryVals() { + tst_AryVals(ary_()); + tst_AryVals(ary_("key1", "1"), kv_("key1", "1")); + tst_AryVals(ary_("key1", "1", "key2", "2"), kv_("key1", "1"), kv_("key2", "2")); + } + @Test public void Fail_lengthMustBeEven() { + try { + tst_AryVals(ary_("key1"), kv_("key1", "1")); + Tfds.Fail_expdError(); + } + catch (Exception e) {Err_.Noop(e);} + } + void tst_AryVals(String[] ary, KeyVal... expd) {Tfds.Eq_ary_str(expd, KeyValHash.strAry_(ary).XtoAry());} + KeyVal kv_(String key, Object val) {return KeyVal_.new_(key, val);} + String[] ary_(String... ary) {return ary;} +} diff --git a/100_core/src_120_basicDataType/gplx/KeyValList.java b/100_core/src_120_basicDataType/gplx/KeyValList.java new file mode 100644 index 000000000..e6545c1ef --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/KeyValList.java @@ -0,0 +1,35 @@ +/* +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; +public class KeyValList {//20101217 + public int Count() {return list.Count();} ListAdp list = ListAdp_.new_(); + public void Clear() {list.Clear();} + public KeyVal GetAt(int i) {return (KeyVal)list.FetchAt(i);} + public KeyValList Add(String key, Object val) {list.Add(KeyVal_.new_(key, val)); return this;} + public KeyVal[] XtoAry() {return (KeyVal[])list.XtoAry(KeyVal.class);} + public String XtoStr() { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < list.Count(); i++) { + KeyVal kv = (KeyVal)list.FetchAt(i); + sb.Add_spr_unless_first(kv.Key(), " ", i); + sb.Add("=").Add(kv.Val_to_str_or_empty()); + } + return sb.XtoStr(); + } + public static KeyValList args_(String key, Object val) {return new KeyValList().Add(key, val);} +} diff --git a/100_core/src_120_basicDataType/gplx/KeyVal_.java b/100_core/src_120_basicDataType/gplx/KeyVal_.java new file mode 100644 index 000000000..182a33f14 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/KeyVal_.java @@ -0,0 +1,71 @@ +/* +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; +public class KeyVal_ { + public static final KeyVal[] Ary_empty = new KeyVal[0]; + public static KeyVal[] Ary(KeyVal... ary) {return ary;} + public static KeyVal[] Ary_cast_(Object o) { + try {return (KeyVal[])o;} + catch (Exception e) {throw Err_.cast_(e, KeyVal.class, o);} + } + public static KeyVal[] Ary_insert(KeyVal[] orig, boolean insert_at_end, KeyVal... vals) { + int orig_len = orig.length, vals_len = vals.length; + int rv_len = orig_len + vals_len; + KeyVal[] rv = new KeyVal[rv_len]; + int vals_bgn = 0 , vals_end = vals_len; + int orig_bgn = vals_len , orig_end = rv_len; + if (insert_at_end) { + orig_bgn = 0 ; orig_end = orig_len; + vals_bgn = orig_len ; vals_end = rv_len; + } + for (int i = orig_bgn; i < orig_end; i++) + rv[i] = orig[i - orig_bgn]; + for (int i = vals_bgn; i < vals_end; i++) + rv[i] = vals[i - vals_bgn]; + return rv; + } + public static String Ary_x_to_str(KeyVal... ary) { + String_bldr sb = String_bldr_.new_(); + int len = ary.length; + for (int i = 0; i < len; i++) { + KeyVal itm = ary[i]; + sb.Add(itm.Key()).Add("="); + Object itm_val = itm.Val(); + if (ClassAdp_.Eq_typeSafe(itm_val, KeyVal[].class)) + sb.Add(Ary_x_to_str((KeyVal[])itm_val)); + else + sb.Add(Object_.XtoStr_OrNullStr(itm_val)); + sb.Add_char_nl(); + } + return sb.XtoStr(); + } + public static Object Ary_get_by_key_or_null(KeyVal[] ary, String key) { + int len = ary.length; + for (int i = 0; i < len; i++) { + KeyVal kv = ary[i]; + if (String_.Eq(kv.Key(), key)) return kv.Val(); + } + return null; + } + public static KeyVal as_(Object obj) {return obj instanceof KeyVal ? (KeyVal)obj : null;} + public static KeyVal new_(String key) {return new KeyVal(KeyVal_.Key_tid_str, key, key);} + public static KeyVal new_(String key, Object val) {return new KeyVal(KeyVal_.Key_tid_str, key, val);} + public static KeyVal int_(int key, Object val) {return new KeyVal(KeyVal_.Key_tid_int, key, val);} + public static KeyVal obj_(Object key, Object val) {return new KeyVal(KeyVal_.Key_tid_obj, key, val);} + public static final byte Key_tid_obj = 0, Key_tid_str = 1, Key_tid_int = 2; +} diff --git a/100_core/src_120_basicDataType/gplx/Math_.java b/100_core/src_120_basicDataType/gplx/Math_.java new file mode 100644 index 000000000..1d42eb32c --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Math_.java @@ -0,0 +1,72 @@ +/* +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; +public class Math_ { + public static double Pow(double val, double exponent) {return java.lang.Math.pow(val, exponent);} + public static double Pi = java.lang.Math.PI; + public static double E = java.lang.Math.E; + public static double Ceil(double v) {return java.lang.Math.ceil(v);} + public static double Floor(double v) {return java.lang.Math.floor(v);} + public static double Round(double v, int places) { + return java.math.BigDecimal.valueOf(v).setScale(places, java.math.BigDecimal.ROUND_HALF_UP).doubleValue(); + } + public static int Trunc(double v) {return (int)v;} + public static double Exp(double v) {return java.lang.Math.exp(v);} + public static double Log(double v) {return java.lang.Math.log(v);} + public static double Sin(double v) {return java.lang.Math.sin(v);} + public static double Cos(double v) {return java.lang.Math.cos(v);} + public static double Tan(double v) {return java.lang.Math.tan(v);} + public static double Asin(double v) {return java.lang.Math.asin(v);} + public static double Acos(double v) {return java.lang.Math.acos(v);} + public static double Atan(double v) {return java.lang.Math.atan(v);} + public static double Sqrt(double v) {return java.lang.Math.sqrt(v);} + public static int Abs(int val) {return val > 0 ? val : val * -1;} + public static long Abs(long val) {return val > 0 ? val : val * -1;} + public static float Abs(float val) {return val > 0 ? val : val * -1;} + public static double Abs_double(double val) {return val > 0 ? val : val * -1;} + public static int Log10(int val) { + if (val <= 0) return Int_.MinValue; + int rv = -1, baseVal = 10; + while (val != 0) { + val = (val / baseVal); + rv++; + } + return rv; + } + public static int Div_safe_as_int(int val, int divisor) {return divisor == 0 ? 0 : val / divisor;} + public static long Div_safe_as_long(long val, long divisor) {return divisor == 0 ? 0 : val / divisor;} + public static double Div_safe_as_double(double val, double divisor) {return divisor == 0 ? 0 : val / divisor;} + public static int Min(int val0, int val1) {return val0 < val1 ? val0 : val1;} + public static int Max(int val0, int val1) {return val0 > val1 ? val0 : val1;} + public static int[] Base2Ary(int v, int max) { + int[] idxs = new int[32]; + int cur = v, mult = max, idx = 0; + while (mult > 0) { + int tmp = cur / mult; + if (tmp >= 1) { + idxs[idx++] = mult; + cur -= mult; + } + mult /= 2; + } + int[] rv = new int[idx]; + for (int i = 0; i < idx; i++) + rv[i] = idxs[idx - i - 1]; + return rv; + } +} \ No newline at end of file diff --git a/100_core/src_120_basicDataType/gplx/Math__tst.java b/100_core/src_120_basicDataType/gplx/Math__tst.java new file mode 100644 index 000000000..983cf8015 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Math__tst.java @@ -0,0 +1,61 @@ +/* +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; +import org.junit.*; +public class Math__tst { + @Test public void Abs() { + tst_Abs(1, 1); + tst_Abs(-1, 1); + tst_Abs(0, 0); + } void tst_Abs(int val, int expd) {Tfds.Eq(expd, Math_.Abs(val));} + @Test public void Log10() { + tst_Log10(0, Int_.MinValue); + tst_Log10(9, 0); + tst_Log10(10, 1); + tst_Log10(99, 1); + tst_Log10(100, 2); + } void tst_Log10(int val, int expd) {Tfds.Eq(expd, Math_.Log10(val));} + @Test public void Min() { + tst_Min(0, 1, 0); + tst_Min(1, 0, 0); + tst_Min(0, 0, 0); + } void tst_Min(int val0, int val1, int expd) {Tfds.Eq(expd, Math_.Min(val0, val1));} + @Test public void Pow() { + tst_Pow(2, 0, 1); + tst_Pow(2, 1, 2); + tst_Pow(2, 2, 4); + } void tst_Pow(int val, int exponent, double expd) {Tfds.Eq(expd, Math_.Pow(val, exponent));} + @Test public void Mult() { + tst_Mult(100, .01f, 1); + } void tst_Mult(int val, float multiplier, int expd) {Tfds.Eq(expd, Int_.Mult(val, multiplier));} + @Test public void Base2Ary() { + tst_Base2Ary( 1, 256, 1); + tst_Base2Ary( 2, 256, 2); + tst_Base2Ary( 3, 256, 1, 2); + tst_Base2Ary( 4, 256, 4); + tst_Base2Ary( 5, 256, 1, 4); + tst_Base2Ary( 6, 256, 2, 4); + tst_Base2Ary(511, 256, 1, 2, 4, 8, 16, 32, 64, 128, 256); + } void tst_Base2Ary(int v, int max, int... expd) {Tfds.Eq_ary(expd, Math_.Base2Ary(v, max));} + @Test public void Round() { + tst_Round(1.5 , 0, 2); + tst_Round(2.5 , 0, 3); + tst_Round(2.123 , 2, 2.12); + tst_Round(21.1 , -1, 20); + } void tst_Round(double v, int places, double expd) {Tfds.Eq(expd, Math_.Round(v, places));} +} diff --git a/100_core/src_120_basicDataType/gplx/ObjAry.java b/100_core/src_120_basicDataType/gplx/ObjAry.java new file mode 100644 index 000000000..e90849fd3 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/ObjAry.java @@ -0,0 +1,36 @@ +/* +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; +public class ObjAry { + public Object[] Ary() {return ary;} Object[] ary; + public Object Get(int i) {return ary[i];} + public Object Get0() {return ary[0];} + public Object Get1() {return ary[1];} + public static ObjAry pair_(Object val0, Object val1) { + ObjAry rv = new ObjAry(); + rv.ary = new Object[2]; + rv.ary[0] = val0; + rv.ary[1] = val1; + return rv; + } ObjAry() {} + public static ObjAry many_(Object... ary) { + ObjAry rv = new ObjAry(); + rv.ary = ary; + return rv; + } +} diff --git a/100_core/src_120_basicDataType/gplx/RandomAdp.java b/100_core/src_120_basicDataType/gplx/RandomAdp.java new file mode 100644 index 000000000..604c90dd5 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/RandomAdp.java @@ -0,0 +1,24 @@ +/* +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; +import java.util.*; +public class RandomAdp { + public int Next(int max) {return under.nextInt(max);} + public RandomAdp(Random v) {under = v;} + Random under; +} diff --git a/100_core/src_120_basicDataType/gplx/RandomAdp_.java b/100_core/src_120_basicDataType/gplx/RandomAdp_.java new file mode 100644 index 000000000..ee45c701b --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/RandomAdp_.java @@ -0,0 +1,30 @@ +/* +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; +import java.util.*; +public class RandomAdp_ implements GfoInvkAble { + public static RandomAdp new_() { + Random random = new Random(System.currentTimeMillis()); + return new RandomAdp(random); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Next)) return RandomAdp_.new_().Next(m.ReadInt("max")); + else return GfoInvkAble_.Rv_unhandled; + } static final String Invk_Next = "Next"; + public static final RandomAdp_ Gfs = new RandomAdp_(); +} diff --git a/100_core/src_120_basicDataType/gplx/TimeSpanAdp.java b/100_core/src_120_basicDataType/gplx/TimeSpanAdp.java new file mode 100644 index 000000000..498b24bf4 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/TimeSpanAdp.java @@ -0,0 +1,84 @@ +/* +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; +public class TimeSpanAdp implements CompareAble, EqAble { + public long Fracs() {return fracs;} long fracs; public int FracsAsInt() {return (int)fracs;} + public DecimalAdp TotalSecs() { + return DecimalAdp_.divide_(fracs, TimeSpanAdp_.Divisors[TimeSpanAdp_.Idx_Sec]); + } + public DecimalAdp Total_days() { + return DecimalAdp_.divide_(fracs, TimeSpanAdp_.Divisors[TimeSpanAdp_.Idx_Hour] * 24); + } + public int[] Units() {return TimeSpanAdp_.Split_long(fracs, TimeSpanAdp_.Divisors);} + public int Units_fracs() { + int[] ary = TimeSpanAdp_.Split_long(fracs, TimeSpanAdp_.Divisors); + return ary[TimeSpanAdp_.Idx_Frac]; + } + public TimeSpanAdp Add(TimeSpanAdp val) {return new TimeSpanAdp(fracs + val.fracs);} + public TimeSpanAdp Add_fracs(long val) {return new TimeSpanAdp(fracs + val);} + public TimeSpanAdp Add_unit(int idx, int val) { + int[] units = TimeSpanAdp_.Split_long(fracs, TimeSpanAdp_.Divisors); + units[idx] += val; + int sign = fracs >= 0 ? 1 : -1; + long rv = sign * TimeSpanAdp_.Merge_long(units, TimeSpanAdp_.Divisors); + return TimeSpanAdp_.fracs_(rv); + } + public TimeSpanAdp Subtract(TimeSpanAdp val) {return new TimeSpanAdp(fracs - val.fracs);} + + public int compareTo(Object obj) {TimeSpanAdp comp = TimeSpanAdp_.cast_(obj); return CompareAble_.Compare_obj(fracs, comp.fracs);} + public boolean Eq(Object o) { + TimeSpanAdp comp = TimeSpanAdp_.cast_(o); if (comp == null) return false; + return fracs == comp.fracs; + } + @Override public String toString() {return XtoStr(TimeSpanAdp_.Fmt_Default);} + @Override public boolean equals(Object obj) {TimeSpanAdp comp = TimeSpanAdp_.cast_(obj); return Object_.Eq(fracs, comp.fracs);} + @Override public int hashCode() {return super.hashCode();} + + public String XtoStr() {return TimeSpanAdp_.XtoStr(fracs, TimeSpanAdp_.Fmt_Default);} + public String XtoStr(String format) { + return TimeSpanAdp_.XtoStr(fracs, format); + } + public String XtoStrUiAbbrv() { + if (fracs == 0) return "0" + UnitAbbrv(0); + int[] units = Units(); + boolean started = false; + String_bldr sb = String_bldr_.new_(); + for (int i = units.length - 1; i > -1; i--) { + int unit = units[i]; + if (!started) { + if (unit == 0) + continue; + else + started = true; + } + if (sb.Count() != 0) sb.Add(" "); + sb.Add_obj(unit).Add(UnitAbbrv(i)); + } + return sb.XtoStr(); + } + String UnitAbbrv(int i) { + switch (i) { + case 0: return "f"; + case 1: return "s"; + case 2: return "m"; + case 3: return "h"; + default: return "unknown:<" + Int_.XtoStr(i) + ">"; + } + } + @gplx.Internal protected TimeSpanAdp(long fracs) {this.fracs = fracs;} +} diff --git a/100_core/src_120_basicDataType/gplx/TimeSpanAdp_.java b/100_core/src_120_basicDataType/gplx/TimeSpanAdp_.java new file mode 100644 index 000000000..61d03fa79 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/TimeSpanAdp_.java @@ -0,0 +1,161 @@ +/* +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; +public class TimeSpanAdp_ { + public static final TimeSpanAdp Zero = new TimeSpanAdp(0); + public static final TimeSpanAdp Null = new TimeSpanAdp(-1); + public static TimeSpanAdp fracs_(long val) {return new TimeSpanAdp(val);} + public static TimeSpanAdp seconds_(double seconds) { + long fracs = (long)(seconds * Divisors[Idx_Sec]); + return new TimeSpanAdp(fracs); + } + public static TimeSpanAdp decimal_(DecimalAdp seconds) { + return new TimeSpanAdp(seconds.XtoLong_Mult1000()); + } + public static TimeSpanAdp units_(int frc, int sec, int min, int hour) { + int[] units = new int[] {frc, sec, min, hour}; + long fracs = Merge_long(units, TimeSpanAdp_.Divisors); + return TimeSpanAdp_.fracs_(fracs); + } + public static TimeSpanAdp from_(long bgn) {return TimeSpanAdp_.fracs_(Env_.TickCount() - bgn);} + public static final long parse_null = Long_.MinValue; + public static TimeSpanAdp parse_(String raw) { + byte[] bry = Bry_.new_utf8_(raw); + long fracs = parse_to_fracs(bry, 0, bry.length, false); + return fracs == parse_null ? null : TimeSpanAdp_.fracs_(fracs); + } + public static long parse_to_fracs(byte[] raw, int bgn, int end, boolean fail_if_ws) { + int sign = 1, val_f = 0, val_s = 0, val_m = 0, val_h = 0, colon_pos = 0, unit_val = 0, unit_multiple = 1; + for (int i = end - 1; i >= bgn; i--) { // start from end; fracs should be lowest unit + byte b = raw[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + int unit_digit = Byte_ascii.X_to_digit(b); + unit_val = (unit_multiple == 1) ? unit_digit : unit_val + (unit_digit * unit_multiple); + switch (colon_pos) { + case 0: val_s = unit_val; break; + case 1: val_m = unit_val; break; + case 2: val_h = unit_val; break; + default: return parse_null; // only hour:minute:second supported for ':' separator; ':' count should be <= 2 + } + unit_multiple *= 10; + break; + case Byte_ascii.Dot: + double factor = (double)1000 / (double)unit_multiple; // factor is necessary to handle non-standard decimals; ex: .1 -> 100; .00199 -> .001 + val_f = (int)((double)val_s * factor); // move val_s unit_val to val_f; logic is indirect, b/c of differing inputs: "123" means 123 seconds; ".123" means 123 fractionals + val_s = 0; + unit_multiple = 1; + break; + case Byte_ascii.Colon: + colon_pos++; + unit_multiple = 1; + break; + case Byte_ascii.Dash: + if (i == 0 && unit_val > 0) // only if first char && unit_val > 0 + sign = -1; + break; + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + if (fail_if_ws) return parse_null; + break; + default: + return parse_null; // invalid char; return null; + } + } + return sign * (val_f + (val_s * Divisors[1]) + (val_m * Divisors[2]) + (val_h * Divisors[3])); + } + @gplx.Internal protected static String XtoStr(long frc, String fmt) { + String_bldr sb = String_bldr_.new_(); + int[] units = Split_long(frc, Divisors); + + if (String_.Eq(fmt, TimeSpanAdp_.Fmt_Short)) { + for (int i = Idx_Hour; i > -1; i--) { + int val = units[i]; + if (val == 0 && i == Idx_Hour) continue; // skip hour if 0; ex: 01:02, instead of 00:01:02 + if (i == Idx_Frac) continue; // skip frac b/c fmt is short + if (sb.Count() > 0) // sb already has unit; add delimiter + sb.Add(Sprs[i]); + if (val < 10) // zeroPad + sb.Add("0"); + sb.Add(Int_.XtoStr(val)); + } + return sb.XtoStrAndClear(); + } + boolean fmt_fracs = !String_.Eq(fmt, TimeSpanAdp_.Fmt_NoFractionals); + boolean fmt_padZeros = String_.Eq(fmt, TimeSpanAdp_.Fmt_PadZeros); + if (frc == 0) return fmt_padZeros ? "00:00:00.000" : "0"; + + int[] padZerosAry = ZeroPadding; + boolean first = true; + String dlm = ""; + int zeros = 0; + if (frc < 0) sb.Add("-"); // negative sign + for (int i = Idx_Hour; i > -1; i--) { // NOTE: "> Idx_Frac" b/c frc will be handled below + int val = units[i]; + if (i == Idx_Frac // only write fracs... + && !(val == 0 && fmt_padZeros) // ... if val == 0 && fmt is PadZeros + && !(val != 0 && fmt_fracs) // ... or val != 0 && fmt is PadZeros or Default + ) continue; + if (first && val == 0 && !fmt_padZeros) continue; // if first and val == 0, don't full pad (avoid "00:") + zeros = first && !fmt_padZeros ? 1 : padZerosAry[i]; // if first, don't zero pad (avoid "01") + dlm = first ? "" : Sprs[i]; // if first, don't use dlm (avoid ":01") + sb.Add(dlm); + sb.Add(Int_.XtoStr_PadBgn(val, zeros)); + first = false; + } + return sb.XtoStr(); + } + @gplx.Internal protected static int[] Split_long(long fracs, int[] divisors) { + int divLength = Array_.Len(divisors); + int[] rv = new int[divLength]; + long cur = Math_.Abs(fracs); + for (int i = divLength - 1; i > -1; i--) { + int divisor = divisors[i]; + long factor = cur / divisor; + rv[i] = (int)factor; + cur -= (factor * divisor); + } + return rv; + } + @gplx.Internal protected static long Merge_long(int[] vals, int[] divisors) { + long rv = 0; int valLength = Array_.Len(vals); + for (int i = 0; i < valLength; i++) { + rv += vals[i] * divisors[i]; + } + return rv; + } + public static final String Fmt_PadZeros = "00:00:00.000"; // u,h00:m00:s00.f000 + public static final String Fmt_Short = "short"; // u,h##:m#0:s00; + public static final String Fmt_Default = "0.000"; // v,#.000 + public static final String Fmt_NoFractionals = "0"; // v,# + @gplx.Internal protected static final int[] Divisors = { + 1, //1 fracs + 1000, //1,000 fracs in a second + 60000, //60,000 fracs in a minute (60 seconds * 1,000) + 3600000, //3,600,000 fracs in an hour (60 minutes * 60,000) + }; + public static final String MajorDelimiter = ":"; + @gplx.Internal protected static final int Idx_Frac = 0; + @gplx.Internal protected static final int Idx_Sec = 1; + @gplx.Internal protected static final int Idx_Min = 2; + @gplx.Internal protected static final int Idx_Hour = 3; + static int[] ZeroPadding = {3, 2, 2, 2,}; + static String[] Sprs = {".", MajorDelimiter, MajorDelimiter, "",}; + public static TimeSpanAdp cast_(Object arg) {try {return (TimeSpanAdp)arg;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, TimeSpanAdp.class, arg);}} + public static final double Ratio_f_to_s = 1000; +} diff --git a/100_core/src_120_basicDataType/gplx/TimeSpanAdp__parse_tst.java b/100_core/src_120_basicDataType/gplx/TimeSpanAdp__parse_tst.java new file mode 100644 index 000000000..586fb6d5d --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/TimeSpanAdp__parse_tst.java @@ -0,0 +1,54 @@ +/* +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; +import org.junit.*; +public class TimeSpanAdp__parse_tst { + @Test public void Zero() { + tst_Parse("0", 0); + } + @Test public void Milliseconds() { + tst_Parse("0.987", 987); + tst_Parse("0.00199", 1); // do not parse as 199 + tst_Parse("0.1", 100); // do not parse as 1 + } + @Test public void Seconds() { + tst_Parse("1.987", 1987); + } + @Test public void Minutes() { + tst_Parse("1:02.987", 62987); + } + @Test public void MinuteSecondOnly() { + tst_Parse("1:02", 62000); + } + @Test public void Hour() { + tst_Parse("1:02:03.987", 3723987); + } + @Test public void Negative() { + tst_Parse("-1:02:03.987", -3723987); + } + @Test public void Loopholes() { + tst_Parse("001:02", 62000); // multiple leading zeroes + tst_Parse("1.2.3.4", 1200); // ignore all decimals except first + tst_Parse("60:60.9999", 3660999); // value does not need to be bounded to limits (except fracs, which is always < 1000) + tst_Parse(" 01 : 02 : 03 . 987", 3723987); // whitespace + } + void tst_Parse(String text, long expd) { + TimeSpanAdp val = TimeSpanAdp_.parse_(text); + Tfds.Eq(expd, val.Fracs()); + } +} diff --git a/100_core/src_120_basicDataType/gplx/TimeSpanAdp_basic_tst.java b/100_core/src_120_basicDataType/gplx/TimeSpanAdp_basic_tst.java new file mode 100644 index 000000000..763dc0f95 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/TimeSpanAdp_basic_tst.java @@ -0,0 +1,87 @@ +/* +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; +import org.junit.*; +public class TimeSpanAdp_basic_tst { + @Test public void seconds_() { + TimeSpanAdp expd = TimeSpanAdp_.fracs_(123987); + TimeSpanAdp actl = TimeSpanAdp_.seconds_(123.987); + Tfds.Eq(expd, actl); + } + @Test public void TotalSecs() { + TimeSpanAdp val = TimeSpanAdp_.fracs_(1987); + Tfds.Eq_decimal(DecimalAdp_.parts_(1, 987), val.TotalSecs()); + } + @Test public void Units() { + tst_Units("01:02:03.987", 1, 2, 3, 987); + tst_Units("01:00:03", 1, 0, 3, 0); + tst_Units("01:00:00.987", 1, 0, 0, 987); + tst_Units("02:00.987", 0, 2, 0, 987); + } + @Test public void Add() { + TimeSpanAdp val = TimeSpanAdp_.fracs_(3); + TimeSpanAdp arg = TimeSpanAdp_.fracs_(2); + TimeSpanAdp expd = TimeSpanAdp_.fracs_(5); + TimeSpanAdp actl = val.Add(arg); + Tfds.Eq(expd, actl); + } + @Test public void Subtract() { + TimeSpanAdp val = TimeSpanAdp_.fracs_(3); + TimeSpanAdp arg = TimeSpanAdp_.fracs_(2); + TimeSpanAdp expd = TimeSpanAdp_.fracs_(1); + TimeSpanAdp actl = val.Subtract(arg); + Tfds.Eq(expd, actl); + } + @Test public void Add_unit_identity() { + tst_AddUnit("00:00:01.000", 0, 0, "00:00:01.000"); + } + @Test public void Add_unit_basic() { + tst_AddUnit("01:59:58.987", 0, 1013, "02:00:00.000"); + tst_AddUnit("01:59:58.987", 1, 2, "02:00:00.987"); + tst_AddUnit("01:59:58.987", 2, 1, "02:00:58.987"); + tst_AddUnit("01:59:58.987", 3, 1, "02:59:58.987"); + } + @Test public void Add_unit_negative() { + tst_AddUnit("01:00:00.00", 0, -1, "00:59:59.999"); + tst_AddUnit("01:00:00.00", 1, -1, "00:59:59.000"); + tst_AddUnit("01:00:00.00", 2, -1, "00:59:00.000"); + tst_AddUnit("01:00:00.00", 3, -1, "00:00:00.000"); + } + @Test public void XtoStrUiAbbrv() { + tst_XtoStrUiAbbrv("01:02:03.004", "1h 2m 3s 4f"); + tst_XtoStrUiAbbrv("00:00:03.004", "3s 4f"); + tst_XtoStrUiAbbrv("00:00:03.000", "3s 0f"); + tst_XtoStrUiAbbrv("11:22:33.444", "11h 22m 33s 444f"); + tst_XtoStrUiAbbrv("00:00:00.000", "0f"); + } void tst_XtoStrUiAbbrv(String raw, String expd) {Tfds.Eq(expd, TimeSpanAdp_.parse_(raw).XtoStrUiAbbrv());} + void tst_AddUnit(String valRaw, int unitIdx, int delta, String expdRaw) { + TimeSpanAdp val = TimeSpanAdp_.parse_(valRaw); + TimeSpanAdp actl = val.Add_unit(unitIdx, delta); + Tfds.Eq(TimeSpanAdp_.parse_(expdRaw), actl); + } + void tst_Units(String text, int... expd) { + TimeSpanAdp val = TimeSpanAdp_.parse_(text); + int hour = 0, min = 0, sec = 0, frac = 0; + int[] ary = val.Units(); + hour = ary[TimeSpanAdp_.Idx_Hour]; min = ary[TimeSpanAdp_.Idx_Min]; sec = ary[TimeSpanAdp_.Idx_Sec]; frac = ary[TimeSpanAdp_.Idx_Frac]; + Tfds.Eq(expd[0], hour); + Tfds.Eq(expd[1], min); + Tfds.Eq(expd[2], sec); + Tfds.Eq(expd[3], frac); + } +} diff --git a/100_core/src_120_basicDataType/gplx/TimeSpanAdp_xtoStr_tst.java b/100_core/src_120_basicDataType/gplx/TimeSpanAdp_xtoStr_tst.java new file mode 100644 index 000000000..7c5be5acc --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/TimeSpanAdp_xtoStr_tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class TimeSpanAdp_xtoStr_tst { + @Test public void Zero() { + tst_Default(0, "0"); + } + @Test public void MinuteSeconds() { + tst_Default(77000, "1:17"); + } + @Test public void ZeroSuppression() { + tst_Default(660000, "11:00"); //fractional 0 and leading 0s are suppressed; i.e.: not 00:11:00.000 + } + @Test public void HourTest() { + tst_Default(3723987, "1:02:03.987"); + } + @Test public void NegSeconds() { + tst_Default(-2000, "-2"); + } + @Test public void NegMins() { + tst_Default(-60000, "-1:00"); + } + @Test public void NegHours() { + tst_Default(-3723981, "-1:02:03.981"); + } + @Test public void ZeroPadding() { + tst_ZeroPadding("0", "00:00:00.000"); + tst_ZeroPadding("1:02:03.123", "01:02:03.123"); + tst_ZeroPadding("1", "00:00:01.000"); + tst_ZeroPadding(".987", "00:00:00.987"); + tst_ZeroPadding("2:01.456", "00:02:01.456"); + } + void tst_Default(long fractionals, String expd) { + TimeSpanAdp ts = TimeSpanAdp_.fracs_(fractionals); + String actl = ts.XtoStr(TimeSpanAdp_.Fmt_Default); + Tfds.Eq(expd, actl); + } + void tst_ZeroPadding(String val, String expd) { + TimeSpanAdp timeSpan = TimeSpanAdp_.parse_(val); + String actl = timeSpan.XtoStr(TimeSpanAdp_.Fmt_PadZeros); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src_120_basicDataType/gplx/Url_encoder_interface.java b/100_core/src_120_basicDataType/gplx/Url_encoder_interface.java new file mode 100644 index 000000000..10e4013b0 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Url_encoder_interface.java @@ -0,0 +1,22 @@ +/* +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; +public interface Url_encoder_interface { + String Encode_str(String v); + byte[] Encode_bry(String v); +} diff --git a/100_core/src_120_basicDataType/gplx/UuidAdp.java b/100_core/src_120_basicDataType/gplx/UuidAdp.java new file mode 100644 index 000000000..3e53f76a7 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/UuidAdp.java @@ -0,0 +1,22 @@ +/* +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; +public class UuidAdp { + public String XtoStr() {return guid.toString();} + public UuidAdp(java.util.UUID guid) {this.guid = guid;} java.util.UUID guid; +} \ No newline at end of file diff --git a/100_core/src_120_basicDataType/gplx/UuidAdp_.java b/100_core/src_120_basicDataType/gplx/UuidAdp_.java new file mode 100644 index 000000000..b26766cc7 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/UuidAdp_.java @@ -0,0 +1,23 @@ +/* +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; +public class UuidAdp_ { + public static final UuidAdp Null = parse_("00000000-0000-0000-0000-000000000000"); + public static UuidAdp random_() {return new UuidAdp(java.util.UUID.randomUUID());} + public static UuidAdp parse_(String s) {return new UuidAdp(java.util.UUID.fromString(s));} +} \ No newline at end of file diff --git a/100_core/src_120_basicDataType/gplx/UuidAdp__tst.java b/100_core/src_120_basicDataType/gplx/UuidAdp__tst.java new file mode 100644 index 000000000..9c50bf4dd --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/UuidAdp__tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class UuidAdp__tst { + @Test public void parse_() { + tst_parse_("467ffb41-cdfe-402f-b22b-be855425784b"); + } + void tst_parse_(String s) { + UuidAdp uuid = UuidAdp_.parse_(s); + Tfds.Eq(uuid.XtoStr(), s); + } +} diff --git a/100_core/src_120_basicDataType/gplx/Yn.java b/100_core/src_120_basicDataType/gplx/Yn.java new file mode 100644 index 000000000..92f908b23 --- /dev/null +++ b/100_core/src_120_basicDataType/gplx/Yn.java @@ -0,0 +1,54 @@ +/* +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; +public class Yn { + public static final String Y = "y", N = "n"; + public static boolean parse_or_n_(String v) {return parse_or_(v, false);} + public static int parse_as_int(String v) { + if (v == null) return Bool_.__int; + else if (String_.Eq(v, "y")) return Bool_.Y_int; + else if (String_.Eq(v, "n")) return Bool_.N_int; + else return Bool_.__int; + } + public static boolean parse_or_(String v, boolean or) { + int v_int = parse_as_int(v); + switch (v_int) { + case Bool_.N_int: return false; + case Bool_.Y_int: return true; + case Bool_.__int: return or; + default: throw Err_mgr._.unhandled_(v_int); + } + } + public static boolean parse_(String v) { + int v_int = parse_as_int(v); + if (v_int == Bool_.__int) Err_mgr._.unhandled_(v); + return v_int == Bool_.Y_int; + } + public static String X_to_str(boolean v) {return v ? "y" : "n";} + public static boolean store_bool_or(SrlMgr mgr, String key, boolean or) { + String v = mgr.SrlStrOr(key, ""); + return mgr.Type_rdr() ? parse_or_(v, or) : or; + } + public static boolean coerce_(Object o) {String s = String_.as_(o); return s != null ? parse_or_(s, false) : Bool_.cast_(o);} + public static boolean readOrFalse_(DataRdr rdr, String key) {return read_(rdr, key, false);} + public static boolean readOrTrue_(DataRdr rdr, String key) {return read_(rdr, key, true);} + static boolean read_(DataRdr rdr, String key, boolean or) { + String v = rdr.ReadStrOr(key, null); + return parse_or_(v, or); + } +} \ No newline at end of file diff --git a/100_core/src_130_crt/gplx/criterias/Criteria.java b/100_core/src_130_crt/gplx/criterias/Criteria.java new file mode 100644 index 000000000..bcf9ecec0 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria.java @@ -0,0 +1,22 @@ +/* +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.criterias; import gplx.*; +public interface Criteria extends XtoStrAble { + byte Crt_tid(); + boolean Matches(Object obj); +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_.java b/100_core/src_130_crt/gplx/criterias/Criteria_.java new file mode 100644 index 000000000..225de8d80 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_.java @@ -0,0 +1,54 @@ +/* +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.criterias; import gplx.*; +import gplx.criterias.*; +import gplx.texts.*; /*RegxPatn_cls_like*/ +public class Criteria_ { + public static final Criteria All = new Criteria_const(true); + public static final Criteria None = new Criteria_const(false); + public static Criteria Not(Criteria arg) {return new Criteria_not(arg);} + public static Criteria And(Criteria lhs, Criteria rhs) {return new Criteria_and(lhs, rhs);} + public static Criteria And_many(Criteria... ary) { + int len = Array_.Len(ary); if (len == 0) throw Err_.new_("cannot AND 0 criterias;"); + Criteria rv = ary[0]; + for (int i = 1; i < len; i++) + rv = And(rv, ary[i]); + return rv; + } + public static Criteria Or(Criteria lhs, Criteria rhs) {return new Criteria_or(lhs, rhs);} + public static Criteria Or_many(Criteria... ary) { + int len = Array_.Len(ary); if (len == 0) throw Err_.new_("cannot OR 0 criterias;"); + Criteria rv = ary[0]; + for (int i = 1; i < len; i++) + rv = Or(rv, ary[i]); + return rv; + } + public static Criteria eq_(Object arg) {return new Criteria_eq(false, arg);} + public static Criteria eqn_(Object arg) {return new Criteria_eq(true, arg);} + public static Criteria in_(Object... array) {return new Criteria_in(false, array);} + public static Criteria inn_(Object... array) {return new Criteria_in(true, array);} + public static Criteria lt_(Comparable val) {return new Criteria_comp(CompareAble_.Less, val);} + public static Criteria lte_(Comparable val) {return new Criteria_comp(CompareAble_.LessOrSame, val);} + public static Criteria mt_(Comparable val) {return new Criteria_comp(CompareAble_.More, val);} + public static Criteria mte_(Comparable val) {return new Criteria_comp(CompareAble_.MoreOrSame, val);} + public static Criteria between_(Comparable lhs, Comparable rhs) {return new Criteria_between(false, lhs, rhs);} + public static Criteria between_(boolean negated, Comparable lhs, Comparable rhs) {return new Criteria_between(negated, lhs, rhs);} + public static Criteria like_(String pattern) {return new Criteria_like(false, RegxPatn_cls_like_.parse_(pattern, RegxPatn_cls_like.EscapeDefault));} + public static Criteria liken_(String pattern) {return new Criteria_like(true, RegxPatn_cls_like_.parse_(pattern, RegxPatn_cls_like.EscapeDefault));} + public static final byte Tid_custom = 0, Tid_const = 1, Tid_not = 2, Tid_and = 3, Tid_or = 4, Tid_eq = 5, Tid_between = 6, Tid_in = 7, Tid_like = 8, Tid_comp = 9, Tid_wrapper = 10, Tid_iomatch = 11, Tid_db_obj_ary = 12; +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_between.java b/100_core/src_130_crt/gplx/criterias/Criteria_between.java new file mode 100644 index 000000000..5e57642c7 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_between.java @@ -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 . +*/ +package gplx.criterias; import gplx.*; +public class Criteria_between implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_between;} + public boolean Negated() {return negate;} private boolean negate; + public Comparable Lhs() {return lhs;} Comparable lhs; + public Comparable Rhs() {return rhs;} Comparable rhs; + public String XtoStr() {return String_.Concat_any("BETWEEN ", lhs, " AND ", rhs);} + public boolean Matches(Object compObj) { + Comparable comp = CompareAble_.as_(compObj); + int lhsResult = CompareAble_.CompareComparables(lhs, comp); + int rhsResult = CompareAble_.CompareComparables(rhs, comp); + boolean rv = (lhsResult * rhsResult) != 1; + return negate ? !rv : rv; + } + public Criteria_between(boolean negate, Comparable lhs, Comparable rhs) {this.negate = negate; this.lhs = lhs; this.rhs = rhs;} + public static Criteria_between as_(Object obj) {return obj instanceof Criteria_between ? (Criteria_between)obj : null;} +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_bool_base.java b/100_core/src_130_crt/gplx/criterias/Criteria_bool_base.java new file mode 100644 index 000000000..c77b0c346 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_bool_base.java @@ -0,0 +1,51 @@ +/* +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.criterias; import gplx.*; +public abstract class Criteria_bool_base implements Criteria { + public abstract byte Crt_tid(); + public abstract boolean Matches(Object curVal); + public String OpLiteral() {return opLiteral;} private String opLiteral; + public Criteria Lhs() {return lhs;} Criteria lhs; + public Criteria Rhs() {return rhs;} Criteria rhs; + public String XtoStr() {return String_.Concat(lhs.XtoStr(), " ", this.opLiteral, " ", rhs.XtoStr());} + @gplx.Internal protected void ctor_Criteria_base(String opLiteral, Criteria lhs, Criteria rhs) {this.opLiteral = opLiteral; this.lhs = lhs; this.rhs = rhs;} + public static Criteria_bool_base as_(Object obj) {return obj instanceof Criteria_bool_base ? (Criteria_bool_base)obj : null;} +} +class Criteria_and extends Criteria_bool_base { + @Override public byte Crt_tid() {return Criteria_.Tid_not;} + @Override public boolean Matches(Object curVal) {return this.Lhs().Matches(curVal) && this.Rhs().Matches(curVal);} + public Criteria_and(Criteria lhs, Criteria rhs) {this.ctor_Criteria_base("AND", lhs, rhs);} +} +class Criteria_or extends Criteria_bool_base { + @Override public byte Crt_tid() {return Criteria_.Tid_or;} + @Override public boolean Matches(Object curVal) {return this.Lhs().Matches(curVal) || this.Rhs().Matches(curVal);} + public Criteria_or(Criteria lhs, Criteria rhs) {this.ctor_Criteria_base("OR", lhs, rhs);} +} +class Criteria_const implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_const;} + public boolean Matches(Object comp) {return val;} private boolean val; + public String XtoStr() {return String_.Concat(" IS ", Bool_.XtoStr_lower(val));} + public Criteria_const(boolean val) {this.val = val;} +} +class Criteria_not implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_not;} + public boolean Matches(Object obj) {return !criteria.Matches(obj);} + public String XtoStr() {return String_.Concat_any(" NOT ", criteria.XtoStr());} + Criteria criteria; + public Criteria_not(Criteria v) {this.criteria = v;} +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_comp.java b/100_core/src_130_crt/gplx/criterias/Criteria_comp.java new file mode 100644 index 000000000..cc0dcdeae --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_comp.java @@ -0,0 +1,36 @@ +/* +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.criterias; import gplx.*; +public class Criteria_comp implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_comp;} + public Comparable Value() {return val;} final Comparable val; + public boolean Matches(Object compObj) { + Comparable comp = CompareAble_.as_(compObj); + return CompareAble_.Is(compMode, comp, val); + } + public String XtoStr() {return String_.Concat_any(XtoSymbol(), " ", val);} + public String XtoSymbol() { + String compSym = compMode < CompareAble_.Same ? "<" : ">"; + String eqSym = compMode % 2 == CompareAble_.Same ? "=" : ""; + return compSym + eqSym; + } + + int compMode; + @gplx.Internal protected Criteria_comp(int compMode, Comparable val) {this.compMode = compMode; this.val = val;} + public static Criteria_comp as_(Object obj) {return obj instanceof Criteria_comp ? (Criteria_comp)obj : null;} +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_eq.java b/100_core/src_130_crt/gplx/criterias/Criteria_eq.java new file mode 100644 index 000000000..4c1d191da --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_eq.java @@ -0,0 +1,33 @@ +/* +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.criterias; import gplx.*; +public class Criteria_eq implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_eq;} + public boolean Negated() {return negated;} private boolean negated; + public Object Value() {return val;} Object val; + public boolean Matches(Object comp) { + Class valType = ClassAdp_.ClassOf_obj(val); + if (!ClassAdp_.Eq_typeSafe(comp, valType)) throw Err_.type_mismatch_(valType, comp); + boolean rv = Object_.Eq(val, comp); + return negated ? !rv : rv; + } + public String XtoStr() {return String_.Concat_any("= ", val);} + + @gplx.Internal protected Criteria_eq(boolean negated, Object val) {this.negated = negated; this.val = val;} + public static Criteria_eq as_(Object obj) {return obj instanceof Criteria_eq ? (Criteria_eq)obj : null;} +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_in.java b/100_core/src_130_crt/gplx/criterias/Criteria_in.java new file mode 100644 index 000000000..a3642d80f --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_in.java @@ -0,0 +1,43 @@ +/* +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.criterias; import gplx.*; +public class Criteria_in implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_in;} + public boolean Negated() {return negated;} private boolean negated; + public Object[] Values() {return ary;} Object[] ary; Class aryType; int aryLen; + public boolean Matches(Object comp) { + if (aryLen == 0) return false; // empty array never matches + if (!ClassAdp_.Eq_typeSafe(comp, aryType)) throw Err_.type_mismatch_(aryType, comp); + boolean rv = false; + for (int i = 0; i < aryLen; i++) { + Object val = ary[i]; + if (Object_.Eq(val, comp)) { + rv = true; + break; + } + } + return negated ? !rv : rv; + } + public String XtoStr() {return String_.Concat_any("IN ", String_.Concat_any(ary));} + public Criteria_in(boolean negated, Object[] vals) { + this.negated = negated; this.ary = vals; + aryLen = Array_.Len(ary); + aryType = aryLen == 0 ? Object.class : ClassAdp_.ClassOf_obj(ary[0]); + } + public static Criteria_in as_(Object obj) {return obj instanceof Criteria_in ? (Criteria_in)obj : null;} +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_ioItm_tst.java b/100_core/src_130_crt/gplx/criterias/Criteria_ioItm_tst.java new file mode 100644 index 000000000..d0e9e37e6 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_ioItm_tst.java @@ -0,0 +1,50 @@ +/* +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.criterias; import gplx.*; +import org.junit.*; +import gplx.ios.*; +public class Criteria_ioItm_tst { + IoItmFil fil; Criteria crt; IoItm_fxt fx = IoItm_fxt.new_(); + @Test public void IoType() { + crt = crt_(IoItm_base_.Prop_Type, Criteria_.eq_(IoItmFil.Type_Fil)); + tst_Match(true, crt, fx.fil_wnt_("C:\\fil.txt")); + tst_Match(false, crt, fx.dir_wnt_("C:\\dir")); + } + @Test public void Ext() { + crt = crt_(IoItm_base_.Prop_Ext, Criteria_.eq_(".txt")); + tst_Match(true, crt, fx.fil_wnt_("C:\\fil.txt")); + tst_Match(false, crt, fx.fil_wnt_("C:\\fil.xml"), fx.fil_wnt_("C:\\fil.txt1"), fx.fil_wnt_("C:\\fil1.txt.xml"), fx.dir_wnt_("C:\\.txt")); + } + @Test public void Modified() { + fil = fx.fil_wnt_("C:\\fil.txt"); + crt = crt_(IoItmFil_.Prop_Modified, Criteria_.mte_(DateAdp_.parse_gplx("2001-01-01"))); + tst_Match(true, crt, fil.ModifiedTime_(DateAdp_.parse_gplx("2001-01-02")), fil.ModifiedTime_(DateAdp_.parse_gplx("2001-01-01"))); + tst_Match(false, crt, fil.ModifiedTime_(DateAdp_.parse_gplx("2000-12-31"))); + } + @Test public void IoMatch() { + Criteria crt = Criteria_ioMatch.parse_(true, "*.txt", false); + CriteriaFxt fx_crt = new CriteriaFxt(); + fx_crt.tst_Matches(crt, Io_url_.new_any_("file.txt")); + fx_crt.tst_MatchesNot(crt, Io_url_.new_any_("file.xml")); + } + Criteria crt_(String fld, Criteria crt) {return Criteria_wrapper.new_(fld, crt);} + void tst_Match(boolean expt, Criteria fieldCrt, IoItm_base... ary) { + for (IoItm_base itm : ary) + Tfds.Eq(expt, fieldCrt.Matches(itm)); + } +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_ioMatch.java b/100_core/src_130_crt/gplx/criterias/Criteria_ioMatch.java new file mode 100644 index 000000000..7cca5d4de --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_ioMatch.java @@ -0,0 +1,35 @@ +/* +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.criterias; import gplx.*; +import gplx.texts.*; +public class Criteria_ioMatch implements Criteria { //url IOMATCH '*.xml|*.txt' + public byte Crt_tid() {return Criteria_.Tid_iomatch;} + public static final String TokenName = "IOMATCH"; + public boolean Negated() {return !match;} private final boolean match; + public RegxPatn_cls_ioMatch Pattern() {return pattern;} private final RegxPatn_cls_ioMatch pattern; + public boolean Matches(Object compObj) { + Io_url comp = (Io_url)compObj; + boolean rv = pattern.Matches(comp.XtoCaseNormalized()); + return match ? rv : !rv; + } + public String XtoStr() {return String_.Concat_any("IOMATCH ", pattern);} + public Criteria_ioMatch(boolean match, RegxPatn_cls_ioMatch pattern) {this.match = match; this.pattern = pattern;} + + public static Criteria_ioMatch as_(Object obj) {return obj instanceof Criteria_ioMatch ? (Criteria_ioMatch)obj : null;} + public static Criteria_ioMatch parse_(boolean match, String raw, boolean caseSensitive) {return new Criteria_ioMatch(match, RegxPatn_cls_ioMatch_.parse_(raw, caseSensitive));} +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_like.java b/100_core/src_130_crt/gplx/criterias/Criteria_like.java new file mode 100644 index 000000000..9853a19b8 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_like.java @@ -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 . +*/ +package gplx.criterias; import gplx.*; +import gplx.texts.*; /*RegxPatn_cls_like*/ +public class Criteria_like implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_like;} + public boolean Negated() {return negated;} private boolean negated; + public RegxPatn_cls_like Pattern() {return pattern;} RegxPatn_cls_like pattern; + public boolean Matches(Object compObj) { + String comp = String_.as_(compObj); if (comp == null) throw Err_.type_mismatch_(String.class, compObj); + boolean rv = pattern.Matches(comp); + return negated ? !rv : rv; + } + public String XtoStr() {return String_.Concat_any("LIKE ", pattern);} + @gplx.Internal protected Criteria_like(boolean negated, RegxPatn_cls_like pattern) { + this.negated = negated; this.pattern = pattern; + } + public static Criteria_like as_(Object obj) {return obj instanceof Criteria_like ? (Criteria_like)obj : null;} +} \ No newline at end of file diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_tst.java b/100_core/src_130_crt/gplx/criterias/Criteria_tst.java new file mode 100644 index 000000000..91d746e4e --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_tst.java @@ -0,0 +1,92 @@ +/* +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.criterias; import gplx.*; +import org.junit.*; +public class Criteria_tst { + @Test public void Equal() { + Criteria crt = Criteria_.eq_(true); + fx.tst_Matches(crt, true); + fx.tst_MatchesNot(crt, false); + fx.tst_MatchesFail(crt, "true"); + + fx.tst_Matches(Criteria_.eq_(1), 1); + fx.tst_Matches(Criteria_.eq_("equal"), "equal"); + fx.tst_Matches(Criteria_.eq_(date), date); + } + @Test public void Not() { + Criteria crt = Criteria_.eqn_(true); + fx.tst_Matches(crt, false); + fx.tst_MatchesNot(crt, true); + fx.tst_MatchesFail(crt, "false"); + + fx.tst_Matches(Criteria_.eqn_(1), -1); + fx.tst_Matches(Criteria_.eqn_("equal"), "not equal"); + fx.tst_Matches(Criteria_.eqn_(date), date.Add_minute(1)); + } + @Test public void MoreThan() { + Criteria crt = Criteria_.mt_(0); + fx.tst_Matches(crt, 1, 2); + fx.tst_MatchesNot(crt, 0, -1); + fx.tst_MatchesFail(crt, "1"); + + fx.tst_Matches(Criteria_.mt_(0), 1); + fx.tst_Matches(Criteria_.mt_("a"), "b"); + fx.tst_Matches(Criteria_.mt_(date), date.Add_minute(1)); + fx.tst_Matches(Criteria_.mt_(false), true); // MISC: thus truth is greater than falsehood + } + @Test public void MoreThanEq() { + Criteria crt = Criteria_.mte_(0); + fx.tst_Matches(crt, 0); + } + @Test public void Less() { + Criteria crt = Criteria_.lt_(0); + fx.tst_Matches(crt, -1, -2); + fx.tst_MatchesNot(crt, 0, 1); + fx.tst_MatchesFail(crt, "-1"); + } + @Test public void LessEq() { + Criteria crt = Criteria_.lte_(0); + fx.tst_Matches(crt, 0); + } + @Test public void Between() { + Criteria crt = Criteria_.between_(-1, 1); + fx.tst_Matches(crt, 0, 1, -1); + fx.tst_MatchesNot(crt, -2, 2); + fx.tst_MatchesFail(crt, "0"); + + fx.tst_Matches(Criteria_.between_(1, -1), 0); // reverse range + fx.tst_Matches(Criteria_.between_("a", "c"), "b"); + } + @Test public void In() { + Criteria crt = Criteria_.in_(0, 1, 2); + fx.tst_Matches(crt, 0, 1, 2); + fx.tst_MatchesNot(crt, 3, -1); + fx.tst_MatchesFail(crt, "0"); + } + CriteriaFxt fx = new CriteriaFxt(); + DateAdp date = DateAdp_.parse_gplx("2001-01-01"); +} +class CriteriaFxt { + public void tst_Matches(Criteria crt, Object... ary) {for (Object val : ary) Tfds.Eq(true, crt.Matches(val));} + public void tst_MatchesNot(Criteria crt, Object... ary) {for (Object val : ary) Tfds.Eq(false, crt.Matches(val));} + public void tst_MatchesFail(Criteria crt, Object val) { + try {crt.Matches(val);} + catch(Exception exc) {Err_.Noop(exc); return;} + Tfds.Fail_expdError(); + } +} diff --git a/100_core/src_130_crt/gplx/criterias/Criteria_wrapper.java b/100_core/src_130_crt/gplx/criterias/Criteria_wrapper.java new file mode 100644 index 000000000..f5e5d3af0 --- /dev/null +++ b/100_core/src_130_crt/gplx/criterias/Criteria_wrapper.java @@ -0,0 +1,35 @@ +/* +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.criterias; import gplx.*; +public class Criteria_wrapper implements Criteria { + public byte Crt_tid() {return Criteria_.Tid_wrapper;} + public static Criteria_wrapper as_(Object obj) {return obj instanceof Criteria_wrapper ? (Criteria_wrapper)obj : null;} + public String Name_of_GfoFldCrt() {return fieldName;} private String fieldName; + public Criteria Crt_of_GfoFldCrt() {return crt;} Criteria crt; + public boolean Matches(Object invkObj) { + GfoInvkAble invk = (GfoInvkAble)invkObj; + Object comp = GfoInvkAble_.InvkCmd(invk, fieldName); + return crt.Matches(comp); + } + public String XtoStr() {return String_.Concat(fieldName, " ", crt.XtoStr());} + public static Criteria_wrapper new_(String fieldName, Criteria criteria) { + Criteria_wrapper rv = new Criteria_wrapper(); + rv.fieldName = fieldName; rv.crt = criteria; + return rv; + } +} diff --git a/100_core/src_140_list/gplx/HashAdp.java b/100_core/src_140_list/gplx/HashAdp.java new file mode 100644 index 000000000..e50f0d478 --- /dev/null +++ b/100_core/src_140_list/gplx/HashAdp.java @@ -0,0 +1,31 @@ +/* +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; +public interface HashAdp extends gplx.lists.EnumerAble { + int Count(); + boolean Has(Object key); + Object Fetch(Object key); + Object FetchOrFail(Object key); + Object FetchOrNew(Object key, NewAble prototype); + void Add(Object key, Object val); + void AddKeyVal(Object val); + void AddReplace(Object key, Object val); + boolean Add_if_new(Object key, Object val); + void Del(Object key); + void Clear(); +} diff --git a/100_core/src_140_list/gplx/HashAdp_.java b/100_core/src_140_list/gplx/HashAdp_.java new file mode 100644 index 000000000..1271d088a --- /dev/null +++ b/100_core/src_140_list/gplx/HashAdp_.java @@ -0,0 +1,45 @@ +/* +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; +public class HashAdp_ { + public static HashAdp new_() {return new HashAdp_obj();} + public static HashAdp new_bry_() {return new HashAdp_bry();} + public static final HashAdp Null = new HashAdp_null(); +} +class HashAdp_obj extends gplx.lists.HashAdp_base implements HashAdp {}//_20110428 +class HashAdp_null implements HashAdp { + public int Count() {return 0;} + public boolean Has(Object key) {return false;} + public Object Fetch(Object key) {return null;} + public Object FetchOrFail(Object key) {throw Err_.missing_key_(Object_.XtoStr_OrNullStr(key));} + public Object FetchOrNew(Object key, NewAble proto) {throw Err_.new_("could not add to null hash");} + public void Add(Object key, Object val) {} + public void AddKeyVal(Object val) {} + public void AddReplace(Object key, Object val) {} + public boolean Add_if_new(Object key, Object val) {return false;} + public void Del(Object key) {} + public void Clear() {} + public java.util.Iterator iterator() {return gplx.lists.Iterator_null._;} +} +class HashAdp_bry extends gplx.lists.HashAdp_base implements HashAdp { + Bry_obj_ref key_ref = Bry_obj_ref.null_(); + @Override protected void Add_base(Object key, Object val) {super.Add_base(Bry_obj_ref.new_((byte[])key), val);} + @Override protected void Del_base(Object key) {super.Del_base(key_ref.Val_((byte[])key));} + @Override protected boolean Has_base(Object key) {return super.Has_base(key_ref.Val_((byte[])key));} + @Override protected Object Fetch_base(Object key) {return super.Fetch_base(key_ref.Val_((byte[])key));} +} diff --git a/100_core/src_140_list/gplx/Hash_adp_bry.java b/100_core/src_140_list/gplx/Hash_adp_bry.java new file mode 100644 index 000000000..2a3f027f8 --- /dev/null +++ b/100_core/src_140_list/gplx/Hash_adp_bry.java @@ -0,0 +1,101 @@ +/* +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; +public class Hash_adp_bry extends gplx.lists.HashAdp_base implements HashAdp { + Hash_adp_bry(boolean case_match) { + this.case_match = case_match; + key_ref = new Hash_adp_bry_ref(case_match, null, -1, -1); + } private final boolean case_match; private Hash_adp_bry_ref key_ref; + public Object Get_by_bry(byte[] src) {return super.Fetch_base(key_ref.Src_all_(src));} + public Object Get_by_mid(byte[] src, int bgn, int end) {return super.Fetch_base(key_ref.Src_all_set_(src, bgn, end));} + public Hash_adp_bry Add_bry_bry(byte[] key) {this.Add_base(key, key); return this;} + public Hash_adp_bry Add_str_byte(String key, byte val) {this.Add_base(Bry_.new_utf8_(key), Byte_obj_val.new_(val)); return this;} + public Hash_adp_bry Add_str_obj(String key, Object val) {this.Add_base(Bry_.new_utf8_(key), val); return this;} + public Hash_adp_bry Add_bry_byte(byte[] key, byte val) {this.Add_base(key, Byte_obj_val.new_(val)); return this;} + public Hash_adp_bry Add_bry_obj(byte[] key, Object val) {this.Add_base(key, val); return this;} + public Hash_adp_bry Add_many_str(String... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) { + String itm = ary[i]; + byte[] bry = Bry_.new_utf8_(itm); + Add_bry_bry(bry); + } + return this; + } + @Override protected void Add_base(Object key, Object val) { + byte[] key_bry = (byte[])key; + super.Add_base(new Hash_adp_bry_ref(case_match, key_bry, 0, key_bry.length), val); + } + @Override protected void Del_base(Object key) {super.Del_base(key_ref.Src_all_((byte[])key));} + @Override protected boolean Has_base(Object key) {return super.Has_base(key_ref.Src_all_((byte[])key));} + @Override protected Object Fetch_base(Object key) {return super.Fetch_base(key_ref.Src_all_((byte[])key));} + public static Hash_adp_bry cs_() {return new Hash_adp_bry(true);} + public static Hash_adp_bry ci_() {return new Hash_adp_bry(false);} + public static Hash_adp_bry ci_ascii_() {return new Hash_adp_bry(false);} +} +class Hash_adp_bry_ref { + public Hash_adp_bry_ref(boolean case_match, byte[] src, int src_bgn, int src_end) {this.case_match = case_match; this.src = src; this.src_bgn = src_bgn; this.src_end = src_end;} + final boolean case_match; + public byte[] Src() {return src;} private byte[] src; + public Hash_adp_bry_ref Src_all_(byte[] v) { + this.src = v; + this.src_bgn = 0; + this.src_end = v.length; + return this; + } + public Hash_adp_bry_ref Src_all_set_(byte[] v, int src_bgn, int src_end) { + this.src = v; + this.src_bgn = src_bgn; + this.src_end = src_end; + return this; + } + public int Src_bgn() {return src_bgn;} int src_bgn; + public int Src_end() {return src_end;} int src_end; + @Override public int hashCode() { + int rv = 0; + for (int i = src_bgn; i < src_end; i++) { + int b_int = src[i] & 0xFF; // JAVA: patch + if (!case_match && b_int > 64 && b_int < 91) // 64=before A; 91=after Z; NOTE: lowering upper-case on PERF assumption that there will be more lower-case letters than upper-case + b_int += 32; + rv = (31 * rv) + b_int; + } + return rv; + } + @Override public boolean equals(Object obj) { + if (obj == null) return false; + Hash_adp_bry_ref comp = (Hash_adp_bry_ref)obj; + byte[] comp_src = comp.Src(); int comp_bgn = comp.Src_bgn(), comp_end = comp.Src_end(); + int comp_len = comp_end - comp_bgn, src_len = src_end - src_bgn; + if (comp_len != src_len) return false; + for (int i = 0; i < comp_len; i++) { + int src_pos = src_bgn + i; + if (src_pos >= src_end) return false; // ran out of src; exit; EX: src=ab; find=abc + if (case_match) { + if (src[src_pos] != comp_src[i + comp_bgn]) return false; + } + else { + byte src_byte = src[src_pos]; + if (src_byte > 64 && src_byte < 91) src_byte += 32; + byte comp_byte = comp_src[i + comp_bgn]; + if (comp_byte > 64 && comp_byte < 91) comp_byte += 32; + if (src_byte != comp_byte) return false; + } + } + return true; + } +} diff --git a/100_core/src_140_list/gplx/Hash_adp_bry_tst.java b/100_core/src_140_list/gplx/Hash_adp_bry_tst.java new file mode 100644 index 000000000..cba4590b0 --- /dev/null +++ b/100_core/src_140_list/gplx/Hash_adp_bry_tst.java @@ -0,0 +1,69 @@ +/* +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; +import org.junit.*; +public class Hash_adp_bry_tst { + Hash_adp_bry_fxt fxt = new Hash_adp_bry_fxt(); + @Before public void setup() {fxt.Clear();} + @Test public void Add_bry() { + fxt .New_cs() + .Add("a0").Add("b0").Add("c0") + .Get_bry_tst("a0").Get_bry_tst("b0").Get_bry_tst("c0").Get_bry_tst("A0", null) + ; + } + @Test public void Get_mid() { + fxt .New_cs() + .Add("a0").Add("b0").Add("c0") + .Get_mid_tst("xyza0xyz", 3, 5, "a0") + .Get_mid_tst("xyza0xyz", 3, 4, null) + ; + } + @Test public void Case_insensitive() { + fxt .New_ci() + .Add("a0").Add("B0").Add("c0") + .Get_bry_tst("a0", "a0") + .Get_bry_tst("A0", "a0") + .Get_bry_tst("b0", "B0") + .Get_bry_tst("B0", "B0") + .Get_mid_tst("xyza0xyz", 3, 5, "a0") + .Get_mid_tst("xyzA0xyz", 3, 5, "a0") + .Count_tst(3) + ; + } +} +class Hash_adp_bry_fxt { + Hash_adp_bry hash; + public void Clear() {} + public Hash_adp_bry_fxt New_cs() {hash = Hash_adp_bry.cs_(); return this;} + public Hash_adp_bry_fxt New_ci() {hash = Hash_adp_bry.ci_(); return this;} + public Hash_adp_bry_fxt Add(String key) {byte[] key_bry = Bry_.new_utf8_(key); hash.Add(key_bry, key_bry); return this;} + public Hash_adp_bry_fxt Count_tst(int expd) {Tfds.Eq(expd, hash.Count()); return this;} + public Hash_adp_bry_fxt Get_bry_tst(String key) {return Get_bry_tst(key, key);} + public Hash_adp_bry_fxt Get_bry_tst(String key, String expd) { + byte[] key_bry = Bry_.new_utf8_(key); + byte[] actl_bry = (byte[])hash.Get_by_bry(key_bry); + Tfds.Eq(expd, String_.new_utf8_(actl_bry)); + return this; + } + public Hash_adp_bry_fxt Get_mid_tst(String key, int bgn, int end, String expd) { + byte[] key_bry = Bry_.new_utf8_(key); + byte[] actl_bry = (byte[])hash.Get_by_mid(key_bry, bgn, end); + Tfds.Eq(expd, String_.new_utf8_(actl_bry)); + return this; + } +} diff --git a/100_core/src_140_list/gplx/ListAdp.java b/100_core/src_140_list/gplx/ListAdp.java new file mode 100644 index 000000000..9aa83b31c --- /dev/null +++ b/100_core/src_140_list/gplx/ListAdp.java @@ -0,0 +1,50 @@ +/* +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; +import gplx.lists.*; /*EnumerAble,ComparerAble*/ +public interface ListAdp extends EnumerAble { + boolean Has_none(); + int Count(); + Object FetchAt(int i); + Object FetchAtOr(int i, Object or); + Object FetchAtLast(); + Object PopLast(); + void Add(Object o); + void AddAt(int i, Object o); + ListAdp AddMany(Object... ary); + void Del(Object o); + void DelAt(int i); + void Del_range(int bgn, int end); + void Clear(); + void Clear_max(int max); + boolean RangeCheck(int v); + void ResizeBounds(int i); + int IndexOf(Object o); + int LastIndex(); + void MoveTo(int src, int trg); + void SetAt(int i, Object o); + String XtoStr(); + String[] XtoStrAry(); + Object XtoAry(Class memberType); + Object XtoAryAndClear(Class memberType); + + void Reverse(); + void Sort(); + void SortBy(ComparerAble comparer); + void Shuffle(); +} diff --git a/100_core/src_140_list/gplx/ListAdp_.java b/100_core/src_140_list/gplx/ListAdp_.java new file mode 100644 index 000000000..d78437776 --- /dev/null +++ b/100_core/src_140_list/gplx/ListAdp_.java @@ -0,0 +1,86 @@ +/* +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; +import gplx.lists.*; /*EnumerAble,ComparerAble*/ +public class ListAdp_ { + public static ListAdp as_(Object obj) {return obj instanceof ListAdp ? (ListAdp)obj : null;} + public static ListAdp new_() {return new ListAdp_obj();} + public static ListAdp size_(int v) {return new ListAdp_obj(v);} + public static ListAdp many_(Object... ary) {return new ListAdp_obj().AddMany(ary);} + public static final ListAdp Null = new ListAdp_null(); + public static void DelAt_last(ListAdp list) {list.DelAt(list.Count() - 1);} + public static Object Pop(ListAdp list) { + int lastIdx = list.Count() - 1; + Object rv = list.FetchAt(lastIdx); + list.DelAt(lastIdx); + return rv; + } + public static Object Pop_first(ListAdp list) { // NOTE: dirty way of implementing FIFO queue; should not be used with lists with many members + Object rv = list.FetchAt(0); + list.DelAt(0); + return rv; + } + public static void DisposeAll(ListAdp list) { + for (int i = 0; i < list.Count(); i++) + ((RlsAble)list.FetchAt(i)).Rls(); + } + public static ListAdp new_ary_(Object ary) { + int ary_len = Array_.Len(ary); + ListAdp rv = size_(ary_len); + for (int i = 0; i < ary_len; i++) + rv.Add(Array_.Get(ary, i)); + return rv; + } + public static final int Capacity_initial = 8; + public static final int NotFound = -1, Base1 = 1, LastIdxOffset = 1, CountToPos = 1; +} +class ListAdp_obj extends ListAdp_base implements ListAdp { + public ListAdp_obj() {super();} + public ListAdp_obj(int v) {super(v);} +} +class ListAdp_null implements ListAdp { + public boolean Has_none() {return true;} + public int Count() {return 0;} + public Object FetchAt(int i) {return null;} + public Object FetchAtLast() {return null;} + public Object FetchAtOr(int i, Object or) {return null;} + public Object PopLast() {return null;} + public void Add(Object o) {} + public void AddAt(int i, Object o) {} + public ListAdp AddMany(Object... ary) {return this;} + public void Del(Object o) {} + public void DelAt(int i) {} + public void Del_range(int bgn, int end) {} + public void Clear() {} + public void Clear_max(int max) {} + public int LastIndex() {return -1;} + public int IndexOf(Object o) {return ListAdp_.NotFound;} + public void MoveTo(int elemPos, int newPos) {} + public boolean RangeCheck(int v) {return false;} + public void ResizeBounds(int i) {} + public Object XtoAry(Class memberType) {return Object_.Ary_empty;} + public Object XtoAryAndClear(Class memberType) {return Object_.Ary_empty;} + public String XtoStr() {return "< NULL LIST >";} + public String[] XtoStrAry() {return new String[0];} + public java.util.Iterator iterator() {return Iterator_null._;} + public void Reverse() {} + public void SetAt(int i, Object o) {} + public void Sort() {} + public void SortBy(ComparerAble comparer) {} + public void Shuffle() {} +} diff --git a/100_core/src_140_list/gplx/ListAdp_Sorter.java b/100_core/src_140_list/gplx/ListAdp_Sorter.java new file mode 100644 index 000000000..5d06018e5 --- /dev/null +++ b/100_core/src_140_list/gplx/ListAdp_Sorter.java @@ -0,0 +1,73 @@ +/* +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; +import gplx.lists.*; +public class ListAdp_Sorter { + public void Sort(Object[] orig, int origLen) {Sort(orig, origLen, true, null);} + public void Sort(Object[] orig, int origLen, boolean asc, ComparerAble comparer) { + this.comparer = comparer; + Object[] temp = new Object[origLen]; + MergeSort(asc, orig, temp, 0, origLen - 1); + this.comparer = null; + } + void MergeSort(boolean asc, Object[] orig,Object[] temp, int lhs, int rhs) { + if (lhs < rhs) { + int mid = (lhs + rhs) / 2; + MergeSort(asc, orig, temp, lhs, mid); + MergeSort(asc, orig, temp, mid + 1, rhs); + Combine(asc, orig, temp, lhs, mid + 1, rhs); + } + } + ComparerAble comparer = null; + void Combine(boolean asc, Object[] orig, Object[] temp, int lhsPos, int rhsPos, int rhsEnd) { + int lhsEnd = rhsPos - 1; + int tmpPos = lhsPos; + int aryLen = rhsEnd - lhsPos + 1; + + while (lhsPos <= lhsEnd && rhsPos <= rhsEnd) { + int compareVal = 0; + if (comparer != null) + compareVal = ComparerAble_.Compare(comparer, orig[lhsPos], orig[rhsPos]); + else { + Comparable lhsComp = (Comparable)orig[lhsPos]; + compareVal = lhsComp == null ? CompareAble_.Less : lhsComp.compareTo(orig[rhsPos]); + } + if (!asc) compareVal *= -1; + if (compareVal <= CompareAble_.Same) // NOTE: (a) must be < 0; JAVA's String.compareTo returns -number based on position; (b) must be <= else sorting sorted list will change order; EX: sorting (a,1;a,2) on fld0 will switch to (a,2;a,1) + temp[tmpPos++] = orig[lhsPos++]; + else + temp[tmpPos++] = orig[rhsPos++]; + } + + while (lhsPos <= lhsEnd) // Copy rest of first half + temp[tmpPos++] = orig[lhsPos++]; + while (rhsPos <= rhsEnd) // Copy rest of right half + temp[tmpPos++] = orig[rhsPos++]; + for (int i = 0; i < aryLen; i++, rhsEnd--) + orig[rhsEnd] = temp[rhsEnd]; + } + + public static ListAdp_Sorter new_() {return new ListAdp_Sorter();} ListAdp_Sorter() {} +} +class Iterator_objAry implements java.util.Iterator { + public boolean hasNext() {return ++pos < len;} + public Object next() {return ary[pos];} + public void remove() {pos = -1;} + Object[] ary; int pos = -1; int len = 0; + public Iterator_objAry(Object[] v, int count) {ary = v; len = count;} +} \ No newline at end of file diff --git a/100_core/src_140_list/gplx/ListAdp_Sorter_tst.java b/100_core/src_140_list/gplx/ListAdp_Sorter_tst.java new file mode 100644 index 000000000..9bdbf80e7 --- /dev/null +++ b/100_core/src_140_list/gplx/ListAdp_Sorter_tst.java @@ -0,0 +1,37 @@ +/* +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; +import org.junit.*; +public class ListAdp_Sorter_tst { + @Test public void Basic() { + Object[] src = new Object[] {0,8,1,7,2,6,3,5,4}; + ListAdp_Sorter.new_().Sort(src, src.length); + Tfds.Eq_ary(src, Sequential(0, 8)); + } + @Test public void Basic2() { + Object[] src = new Object[] {"0","8","1","7","2","6","3","5","4"}; + ListAdp_Sorter.new_().Sort(src, src.length); + Tfds.Eq_ary(src, new Object[] {"0","1","2","3","4","5","6","7","8"}); + } + Object[] Sequential(int bgn, int end) { + Object[] rv = new Object[end - bgn + 1]; + for (int i = 0; i < Array_.Len(rv); i++) + rv[i] = i + bgn; + return rv; + } +} diff --git a/100_core/src_140_list/gplx/ListAdp_base.java b/100_core/src_140_list/gplx/ListAdp_base.java new file mode 100644 index 000000000..c382f4089 --- /dev/null +++ b/100_core/src_140_list/gplx/ListAdp_base.java @@ -0,0 +1,184 @@ +/* +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; +import gplx.lists.*; +public abstract class ListAdp_base implements ListAdp, GfoInvkAble { + public Object XtoAryAndClear(Class memberType) {Object rv = XtoAry(memberType); this.Clear(); return rv;} + public Object XtoAry(Class memberType) { + Object rv = Array_.Create(memberType, count); + for (int i = 0; i < count; i++) + Array_.Set(rv, i, list[i]); + return rv; + } + public java.util.Iterator iterator() { + if (count == 0) + return Iterator_null._; + else + return new Iterator_objAry(list, count); + } + public ListAdp AddMany(Object... ary) {for (Object o : ary) Add_base(o); return this;} + + public boolean Has_none() {return count == 0;} + public int Idx_last() {return Count() - 1;} + public int Count() {return count;} + public int LastIndex() {return count - 1;} + protected Object FetchAt_base(int index) {if (index >= count || index < 0) throw Err_.missing_idx_(index, count); + return list[index]; + } + protected void Add_base(Object o) { + if (count == Array_.LenAry(list)) Resize_expand(); + list[count] = o; + count++; + } + protected int Del_base(Object o) { + int index = IndexOf_base(o); if (index == ListAdp_.NotFound) return ListAdp_.NotFound; + this.DelAt(index); + return index; + } + public void Del_range(int delBgn, int delEnd) { + BoundsChk(delBgn, delEnd, count); + if (delBgn == 0 && delEnd == count - 1) { // entire list deleted; call .Clear, else will have 0 elem array + this.Clear(); + return; + } + int delLen = (delEnd - delBgn) + 1; // EX: 0,2 creates 3 len ary + int newLen = count - delLen; + Object[] newList = new Object[newLen]; + if (delBgn != 0) // copy elements < delBgn; skip if delBgn == 0 + Array_.CopyTo(list, 0, newList, 0, delBgn); + if (delEnd != count -1 ) // copy elements > delEnd; skip if delEnd == lastIdx + Array_.CopyTo(list, delEnd + 1, newList, delBgn, newLen - delBgn); + list = newList; + count = list.length; + } + public Object PopLast() { + int lastIdx = count - 1; + Object rv = FetchAt(lastIdx); + DelAt(lastIdx); + return rv; + } + void BoundsChk(int bgn, int end, int len) { + if ( bgn >= 0 && bgn < len + && end >= 0 && end < len + && bgn <= end + ) return; + throw Err_.new_("bounds check failed").Add("bgn", bgn).Add("end", end).Add("len", len); + } + public boolean RangeCheck(int v) {return v >= 0 && v < count;} + protected int IndexOf_base(Object o) { + for (int i = 0; i < count; i++) + if (Object_.Eq(list[i], o)) return i; + return ListAdp_.NotFound; + } + @gplx.Virtual public void Clear() { + for (int i = 0; i < count; i++) + list[i] = null; + count = 0; +// if (Capacity() != ListAdp_.Capacity_initial) list = new Object[ListAdp_.Capacity_initial]; // COMMENT:2012-06-16 + } + public void Clear_max(int max) {this.Clear(); if (Capacity() > max) list = new Object[max];} + @gplx.Virtual public void DelAt(int index) {if (index >= count || index < 0) throw Err_.missing_idx_(index, count); + Collapse(index); + count--; + } + public void MoveTo(int src, int trg) {if (src >= count || src < 0) throw Err_.missing_idx_(src, count); if (trg >= count || trg < 0) throw Err_.missing_idx_(trg, count); + if (src == trg) return; // position not changed + Object o = list[src]; + int dif = trg > src ? 1 : -1; + for (int i = src; i != trg; i += dif) + list[i] = list[i + dif]; + list[trg] = o; + } + protected void AddAt_base(int pos, Object o) { + if (count + 1 >= Array_.LenAry(list)) Resize_expand(); + for (int i = count; i > pos; i--) + list[i] = list[i - 1]; + list[pos] = o; + count = count + 1; + } + public void ResizeBounds(int i) { + Resize_expand(i); + } + public String XtoStr() { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < count; i++) { + if (i != 0) sb.Add_char_crlf(); + Object val = list[i]; + sb.Add_obj(Object_.XtoStr_OrEmpty(val)); + } + return sb.XtoStr(); + } + public void SetAt(int i, Object o) {list[i] = o;} + public void Sort() {SortBy(null);} + public void SortBy(ComparerAble comparer) {ListAdp_Sorter.new_().Sort(list, count, true, comparer);} + public void Reverse() { + int mid = count / 2; // no need to reverse pivot; ex: for 3 elements, only 1 and 3 need to be exchanged; 2 stays inplace + for (int lhs = 0; lhs < mid; lhs++) { + int rhs = count - lhs - 1; // -1 b/c list[count] is not real element + Object temp = list[lhs]; + list[lhs] = list[rhs]; + list[rhs] = temp; + } + } + @gplx.Virtual public void Shuffle() {// REF: Fisher-Yates shuffle + RandomAdp random = RandomAdp_.new_(); + for (int i = count; i > 1; i--) { + int rndIdx = random.Next(i); + Object tmp = list[rndIdx]; + list[rndIdx] = list[i-1]; + list[i-1] = tmp; + } + } + public String[] XtoStrAry() {return (String[])XtoAry(String.class);} + public Object FetchAt(int i) {return FetchAt_base(i);} + public Object FetchAtLast() {if (count == 0) throw Err_.invalid_op_("cannot call FetchAtLast on empty list"); return FetchAt_base(count - 1);} + public Object FetchAtOr(int i, Object or) {return (i >= count) ? or : FetchAt(i);} + public void Add(Object item) {Add_base(item);} + public void AddAt(int i, Object o) {AddAt_base(i, o);} + public void Del(Object item) {Del_base(item);} + public int IndexOf(Object o) {return IndexOf_base(o);} + + Object[] list; int count; + public ListAdp_base() { + list = new Object[ListAdp_.Capacity_initial]; + } + public ListAdp_base(int capacity) { + list = new Object[capacity]; + } + void Resize_expand() {Resize_expand(count * 2);} + void Resize_expand(int newCount) { + Object[] trg = new Object[newCount]; + for (int i = 0; i < count; i++) { + trg[i] = list[i]; + list[i] = null; + } + list = trg; + } + void Collapse(int index) { + for (int i = index; i < count; i++) { + list[i] = (i == count - 1) ? null : list[i + 1]; + } + } + @gplx.Internal protected int Capacity() {return Array_.LenAry(list);} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_len)) return count; + else if (ctx.Match(k, Invk_get_at)) return FetchAt(m.ReadInt("v")); + else return GfoInvkAble_.Rv_unhandled; +// return this; + } private static final String Invk_len = "len", Invk_get_at = "get_at"; +} diff --git a/100_core/src_140_list/gplx/ListAdp_tst.java b/100_core/src_140_list/gplx/ListAdp_tst.java new file mode 100644 index 000000000..1531c87a6 --- /dev/null +++ b/100_core/src_140_list/gplx/ListAdp_tst.java @@ -0,0 +1,236 @@ +/* +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; +import org.junit.*; +public class ListAdp_tst { + @Before public void setup() { + list = ListAdp_.new_(); + listBase = (ListAdp_base)list; + } ListAdp list; ListAdp_base listBase; + @Test public void Add() { + Tfds.Eq(0, list.Count()); + + list.Add("0"); + Tfds.Eq(1, list.Count()); + } + @Test public void Add_changeCapacity() { + int capacity = ListAdp_.Capacity_initial; + for (int i = 0; i < capacity; i++) + list.Add("0"); + Tfds.Eq(capacity, list.Count()); + Tfds.Eq(capacity, listBase.Capacity()); + + list.Add(capacity); // forces resize + Tfds.Eq(capacity + 1, list.Count()); + Tfds.Eq(capacity * 2, listBase.Capacity()); + } + @Test public void FetchAt() { + list.Add("0"); + + Tfds.Eq("0", list.FetchAt(0)); + } + @Test public void Fetch_many() { + list_AddMany("0", "1"); + + Tfds.Eq("0", list.FetchAt(0)); + Tfds.Eq("1", list.FetchAt(1)); + } + @Test public void FetchAt_fail() { + try {list.FetchAt(0);} + catch (Exception exc) {Err_.Noop(exc); return;} + Tfds.Fail("FetchAt should fail for out of bound index"); + } + @Test public void DelAt() { + list.Add("0"); + Tfds.Eq(1, list.Count()); + + list.DelAt(0); + Tfds.Eq(0, list.Count()); + } + @Test public void DelAt_shiftDown() { + list_AddMany("0", "1"); + Tfds.Eq(list.Count(), 2); + + list.DelAt(0); + Tfds.Eq(1, list.Count()); + Tfds.Eq("1", list.FetchAt(0)); + } + @Test public void DelAt_fail() { + try {list.DelAt(0);} + catch (Exception exc) {Err_.Noop(exc); return;} + Tfds.Fail("DelAt should fail for out of bound index"); + } + @Test public void Del() { + list.Add("0"); + Tfds.Eq(1, list.Count()); + + list.Del("0"); + Tfds.Eq(0, list.Count()); + } + @Test public void Del_matchMember() { + list_AddMany("0", "1"); + Tfds.Eq(2, list.Count()); + + list.Del("1"); + Tfds.Eq(1, list.Count()); + Tfds.Eq("0", list.FetchAt(0)); + } + @Test public void Del_matchFirst() { + list_AddMany("0", "1", "0"); + Tfds.Eq(3, list.Count()); + + list.Del("0"); + tst_Enumerator("1", "0"); + } + @Test public void Has_none() { + Tfds.Eq_true(list.Has_none()); + list.Add("0"); + Tfds.Eq_false(list.Has_none()); + list.DelAt(0); + Tfds.Eq_true(list.Has_none()); + } + @Test public void Enumerator() { + list_AddMany("0", "1", "2"); + tst_Enumerator("0", "1", "2"); + } + @Test public void Enumerator_stateLess() { // run 2x, to confirm no state is being cached + list_AddMany("0", "1", "2"); + tst_Enumerator("0", "1", "2"); + tst_Enumerator("0", "1", "2"); + } + @Test public void Enumerator_recursive() { // confirm separate enumerator objects are used + int pos = 0; + list_AddMany("0", "1", "2"); + for (Object valObj : list) { + String val = (String)valObj; + Tfds.Eq(Int_.XtoStr(pos++), val); + tst_Enumerator("0", "1", "2"); + } + } + @Test public void Clear() { + int capacity = ListAdp_.Capacity_initial; + for (int i = 0; i < capacity + 1; i++) + list.Add("0"); + Tfds.Eq(capacity * 2, listBase.Capacity()); + + list.Clear(); + Tfds.Eq(0, list.Count()); + Tfds.Eq(16, listBase.Capacity()); // check that capacity has increased + } + @Test public void Clear_empty() { // confirm no failure + list.Clear(); + Tfds.Eq(0, list.Count()); + } + @Test public void Reverse() { + list_AddMany("0", "1", "2"); + + list.Reverse(); + tst_Enumerator("2", "1", "0"); + } + @Test public void Reverse_empty() {list.Reverse();} + @Test public void Sort() { + list_AddMany("2", "0", "1"); + + list.Sort(); + tst_Enumerator("0", "1", "2"); + } + @Test public void Sort_empty() {list.Sort();} + @Test public void XtoAry() { + list_AddMany("0", "1"); + String[] ary = (String[])list.XtoAry(String.class); + Tfds.Eq_nullNot(ary); + Tfds.Eq(2, Array_.Len(ary)); + } + @Test public void XtoAry_empty() { + String[] ary = (String[])list.XtoAry(String.class); + Tfds.Eq_nullNot(ary); + Tfds.Eq(0, Array_.Len(ary)); + } + @Test public void XtoStr() { + list_AddMany("0", "1", "2"); + Tfds.Eq("0\r\n1\r\n2", list.XtoStr()); + } + @Test public void XtoStr_empty() { + Tfds.Eq("", list.XtoStr()); + } + @Test public void Shuffle() { + for (int i = 0; i < 25; i++) + list.Add(i); + + list.Shuffle(); + int hasMovedCount = 0; + for (int i = 0; i < list.Count(); i++) { + int val = Int_.cast_(list.FetchAt(i)); + if (val != i) hasMovedCount++; + } + Tfds.Eq_true(hasMovedCount > 0, "all documents have the same index"); // NOTE: may still fail occasionally (1%) + + int count = list.Count(); + for (int i = 0; i < count; i++) + list.Del(i); + Tfds.Eq(0, list.Count(), "shuffled list does not have the same contents as original list"); + } + @Test public void Shuffle_empty() {list.Shuffle();} + @Test public void MoveTo() { + run_ClearAndAdd("0", "1", "2").run_MoveTo(0, 1).tst_Order("1", "0", "2"); + run_ClearAndAdd("0", "1", "2").run_MoveTo(0, 2).tst_Order("1", "2", "0"); + run_ClearAndAdd("0", "1", "2").run_MoveTo(2, 1).tst_Order("0", "2", "1"); + run_ClearAndAdd("0", "1", "2").run_MoveTo(2, 0).tst_Order("2", "0", "1"); + } + @Test public void Del_range() { + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 2, "3"); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 3); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(1, 2, "0", "3"); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(1, 3, "0"); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 3); + run_ClearAndAdd("0", "1", "2", "3").tst_DelRange(0, 0, "1", "2", "3"); + } + void tst_DelRange(int bgn, int end, String... expd) { + list.Del_range(bgn, end); + Tfds.Eq_ary_str(expd, list.XtoStrAry()); + } + ListAdp_tst run_ClearAndAdd(String... ary) { + list.Clear(); + for (int i = 0; i < Array_.Len(ary); i++) { + String val = ary[i]; + list.Add(val); + } + return this; + } + ListAdp_tst run_MoveTo(int elemPos, int newPos) {list.MoveTo(elemPos, newPos); return this;} + ListAdp_tst tst_Order(String... expd) { + String[] actl = (String[])list.XtoAry(String.class); + Tfds.Eq_ary(expd, actl); + return this; + } + void list_AddMany(String... ary) { + for (int i = 0; i < Array_.Len(ary); i++) { + String val = ary[i]; + list.Add(val); + } + } + void tst_Enumerator(String... expd) { + int pos = 0; + int expdLength = Array_.Len(expd); + for (int i = 0; i < expdLength; i++) { + String val = expd[i]; + Tfds.Eq(expd[pos++], val); + } + Tfds.Eq(pos, expdLength); + } +} diff --git a/100_core/src_140_list/gplx/OrderedHash.java b/100_core/src_140_list/gplx/OrderedHash.java new file mode 100644 index 000000000..d57ae8e3e --- /dev/null +++ b/100_core/src_140_list/gplx/OrderedHash.java @@ -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 . +*/ +package gplx; +import gplx.lists.*; /*EnumerAble,ComparerAble*/ +public interface OrderedHash extends HashAdp { + Object FetchAt(int i); + Object FetchAtOr(int i, Object or); + void AddAt(int i, Object o); + int IndexOf(Object item); + void Sort(); + void SortBy(ComparerAble comparer); + void ResizeBounds(int i); + Object XtoAry(Class t); + String XtoStr_ui(); + void MoveTo(int src, int trg); + void Lock(); +} diff --git a/100_core/src_140_list/gplx/OrderedHash_.java b/100_core/src_140_list/gplx/OrderedHash_.java new file mode 100644 index 000000000..a13bea564 --- /dev/null +++ b/100_core/src_140_list/gplx/OrderedHash_.java @@ -0,0 +1,29 @@ +/* +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; +public class OrderedHash_ { + public static OrderedHash new_() {return new OrderedHash_base();} + public static OrderedHash new_bry_() {return new OrderedHash_bry();} +} +class OrderedHash_bry extends OrderedHash_base { + private Bry_obj_ref lkp = Bry_obj_ref.null_(); + @Override protected void Add_base(Object key, Object val) {super.Add_base(Bry_obj_ref.new_((byte[])key), val);} + @Override protected void Del_base(Object key) {super.Del_base(lkp.Val_((byte[])key));} + @Override protected boolean Has_base(Object key) {return super.Has_base(lkp.Val_((byte[])key));} + @Override protected Object Fetch_base(Object key) {return super.Fetch_base(lkp.Val_((byte[])key));} +} diff --git a/100_core/src_140_list/gplx/OrderedHash_base.java b/100_core/src_140_list/gplx/OrderedHash_base.java new file mode 100644 index 000000000..842d8ca1b --- /dev/null +++ b/100_core/src_140_list/gplx/OrderedHash_base.java @@ -0,0 +1,89 @@ +/* +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; +import gplx.lists.*; /*EnumerAble,ComparerAble*/ +public class OrderedHash_base extends HashAdp_base implements OrderedHash, GfoInvkAble { + @Override protected void Add_base(Object key, Object val) { + super.Add_base(key, val); + ordered.Add(val); + AssertCounts(); + } + @Override public void Del(Object key) { + if (!this.Has_base(key)) return; + Object val = this.Fetch_base(key); + this.Del_base(key); + ordered.Del(val); + AssertCounts(); + } + protected Object FetchAt_base(int index) {return ordered.FetchAt(index);} + protected int IndexOf_base(Object obj) {return ordered.IndexOf(obj);} + @Override public void Clear() { + if (locked) Lock_fail(); + super.Clear(); + ordered.Clear(); + } + public Object XtoAry(Class type) {return ordered.XtoAry(type);} + @gplx.Virtual public void Sort() {if (locked) Lock_fail(); ordered.Sort();} // NOTE: uses item's .compareTo + public void SortBy(ComparerAble comparer) {if (locked) Lock_fail(); ordered.SortBy(comparer);} + @Override public java.util.Iterator iterator() {return ordered.iterator();} + public void AddAt(int i, Object key, Object val) { + if (locked) Lock_fail(); + super.Add_base(key, val); + ordered.AddAt(i, val); + AssertCounts(); + } + void AssertCounts() { + if (super.Count() != ordered.Count()) throw Err_.new_("counts do not match").Add("hash", super.Count()).Add("list", ordered.Count()); + } + public void ResizeBounds(int i) {if (locked) Lock_fail(); ordered.ResizeBounds(i);} + public void Lock() {locked = true;} private boolean locked = false; + void Lock_fail() {throw Err_mgr._.fmt_(GRP_KEY, "locked", "collection is locked");} + static final String GRP_KEY = "gplx.lists.ordered_hash"; + public void AddAt(int i, Object o) {if (locked) Lock_fail(); ordered.AddAt(i, o);} + public Object FetchAt(int i) {return FetchAt_base(i);} + public Object FetchAtOr(int i, Object or) {return ordered.FetchAtOr(i, or);} + public int IndexOf(Object obj) {return this.IndexOf_base(obj);} + public void MoveTo(int src, int trg) {if (locked) Lock_fail(); ordered.MoveTo(src, trg);} + public String XtoStr_ui() { + String_bldr sb = String_bldr_.new_(); + int count = ordered.Count(); + int pad = String_.Len(Int_.XtoStr(count)); + for (int i = 0; i < count; i++) { + sb .Add(Int_.XtoStr_PadBgn(i, pad)) + .Add(":").Add(ordered.FetchAt(i).toString()) + .Add(Op_sys.Cur().Nl_str()); + } + return sb.XtoStr(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_SetKeyOnly)) { + String s = m.ReadStr("v"); + if (ctx.Deny()) return this; + this.Add(s, s); + } + else if (ctx.Match(k, Invk_Print)) { + if (ctx.Deny()) return this; + return XtoStr_ui(); + } + else return GfoInvkAble_.Rv_unhandled; + return this; + } static final String Invk_SetKeyOnly = "SetKeyOnly", Invk_Print = "Print"; + final ListAdp ordered = ListAdp_.new_(); + @Override public int Count() {return ordered.Count();} + public OrderedHash_base() {} +} diff --git a/100_core/src_140_list/gplx/OrderedHash_tst.java b/100_core/src_140_list/gplx/OrderedHash_tst.java new file mode 100644 index 000000000..c9f03fa40 --- /dev/null +++ b/100_core/src_140_list/gplx/OrderedHash_tst.java @@ -0,0 +1,39 @@ +/* +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; +import org.junit.*; +public class OrderedHash_tst { + @Before public void setup() { + hash = OrderedHash_.new_(); + } + @Test public void FetchAt() { + hash.Add("key1", "val1"); + Tfds.Eq("val1", hash.FetchAt(0)); + } + @Test public void iterator() { + hash.Add("key2", "val2"); + hash.Add("key1", "val1"); + + ListAdp list = ListAdp_.new_(); + for (Object val : hash) + list.Add(val); + Tfds.Eq("val2", list.FetchAt(0)); + Tfds.Eq("val1", list.FetchAt(1)); + } + OrderedHash hash; +} diff --git a/100_core/src_140_list/gplx/lists/ComparerAble.java b/100_core/src_140_list/gplx/lists/ComparerAble.java new file mode 100644 index 000000000..e3cabb981 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/ComparerAble.java @@ -0,0 +1,20 @@ +/* +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.lists; import gplx.*; +public interface ComparerAble extends java.util.Comparator {} //_20110320 +// public int compare(Object lhsObj, Object rhsObj) {} diff --git a/100_core/src_140_list/gplx/lists/ComparerAble_.java b/100_core/src_140_list/gplx/lists/ComparerAble_.java new file mode 100644 index 000000000..2812d5cb5 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/ComparerAble_.java @@ -0,0 +1,21 @@ +/* +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.lists; import gplx.*; +public class ComparerAble_ { + public static int Compare(ComparerAble comparer, Object lhs, Object rhs) {return comparer.compare(lhs, rhs);} +} diff --git a/100_core/src_140_list/gplx/lists/EnumerAble.java b/100_core/src_140_list/gplx/lists/EnumerAble.java new file mode 100644 index 000000000..bfe79ff92 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/EnumerAble.java @@ -0,0 +1,19 @@ +/* +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.lists; import gplx.*; +public interface EnumerAble extends java.lang.Iterable {}//_20110320 diff --git a/100_core/src_140_list/gplx/lists/HashAdp_base.java b/100_core/src_140_list/gplx/lists/HashAdp_base.java new file mode 100644 index 000000000..11db7a2ab --- /dev/null +++ b/100_core/src_140_list/gplx/lists/HashAdp_base.java @@ -0,0 +1,57 @@ +/* +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.lists; import gplx.*; +public abstract class HashAdp_base implements HashAdp { + public boolean Has(Object key) {return Has_base(key);} + public Object Fetch(Object key) {return Fetch_base(key);} + public Object FetchOrFail(Object key) {return FetchOrFail_base(key);} + public Object FetchOrNew(Object key, NewAble proto) { + Object rv = Fetch_base(key); + if (rv == null) { + rv = proto.NewByKey(key); + Add_base(key, rv); + } + return rv; + } + public void Add(Object key, Object val) {Add_base(key, val);} + public void AddKeyVal(Object val) {Add_base(val, val);} + public void AddReplace(Object key, Object val) { + Object existing = Fetch_base(key); if (existing != null) Del(key); // overwrite if exists + Add(key, val); + } + public boolean Add_if_new(Object key, Object val) { + if (Has(key)) return false; + Add(key, val); + return true; + } + @gplx.Virtual public void Del(Object key) {Del_base(key);} + protected Object FetchOrFail_base(Object key) { + if (key == null) throw Err_.new_("key cannot be null"); + if (!Has_base(key)) throw Err_.new_("key not found").Add("key", key); + return Fetch_base(key); + } + + java.util.Hashtable hash = new java.util.Hashtable(); + @gplx.Virtual public int Count() {return hash.size();} + @gplx.Virtual public void Clear() {hash.clear();} + @gplx.Virtual protected void Add_base(Object key, Object val) {hash.put(key, val);} + @gplx.Virtual protected void Del_base(Object key) {hash.remove(key);} + @gplx.Virtual protected boolean Has_base(Object key) {return hash.containsKey(key);} + @gplx.Virtual protected Object Fetch_base(Object key) {return hash.get(key);} + @gplx.Virtual public java.util.Iterator iterator() {return hash.values().iterator();} +} diff --git a/100_core/src_140_list/gplx/lists/HashAdp_list.java b/100_core/src_140_list/gplx/lists/HashAdp_list.java new file mode 100644 index 000000000..1a4ae1a96 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/HashAdp_list.java @@ -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 . +*/ +package gplx.lists; import gplx.*; +public class HashAdp_list extends HashAdp_base { + @gplx.New public ListAdp Fetch(Object key) {return ListAdp_.as_(Fetch_base(key));} + public ListAdp FetchOrNew(Object key) { + ListAdp rv = Fetch(key); + if (rv == null) { + rv = ListAdp_.new_(); + Add_base(key, rv); + } + return rv; + } + public void AddInList(Object key, Object val) { + ListAdp list = FetchOrNew(key); + list.Add(val); + } + public void DelInList(Object key, Object val) { + ListAdp list = Fetch(key); + if (list == null) return; + list.Del(val); + if (list.Count() == 0) Del(key); + } + public static HashAdp_list new_() {return new HashAdp_list();} HashAdp_list() {} +} diff --git a/100_core/src_140_list/gplx/lists/Iterator_null.java b/100_core/src_140_list/gplx/lists/Iterator_null.java new file mode 100644 index 000000000..4d1af3975 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/Iterator_null.java @@ -0,0 +1,24 @@ +/* +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.lists; import gplx.*; +public class Iterator_null implements java.util.Iterator { + public boolean hasNext() {return false;} + public Object next() {return null;} + public void remove() {} + public static final Iterator_null _ = new Iterator_null(); +} diff --git a/100_core/src_140_list/gplx/lists/StackAdp.java b/100_core/src_140_list/gplx/lists/StackAdp.java new file mode 100644 index 000000000..3c30ebc26 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/StackAdp.java @@ -0,0 +1,26 @@ +/* +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.lists; import gplx.*; +public interface StackAdp extends EnumerAble { + int Count(); + void Clear(); + void Push(Object obj); + Object Pop(); + Object Peek(); + ListAdp XtoList(); +} diff --git a/100_core/src_140_list/gplx/lists/StackAdp_.java b/100_core/src_140_list/gplx/lists/StackAdp_.java new file mode 100644 index 000000000..cfef443e1 --- /dev/null +++ b/100_core/src_140_list/gplx/lists/StackAdp_.java @@ -0,0 +1,41 @@ +/* +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.lists; import gplx.*; +public class StackAdp_ { + public static StackAdp new_() {return new StackAdp_base();} +} +class StackAdp_base implements StackAdp { + public Object Peek() {return Peek_base();} + public Object Pop() {return Pop_base();} + public void Push(Object obj) {Push_base(obj);} + public ListAdp XtoList() { + ListAdp list = ListAdp_.new_(); + for (Object obj : stack) + list.Add(obj); + // NOTE: dotnet traverses last to first; java: first to last + return list; + } + final java.util.Stack stack = new java.util.Stack(); + public StackAdp_base() {} + public int Count() {return stack.size();} + public void Clear() {stack.clear();} + protected void Push_base(Object obj) {stack.push(obj);} + protected Object Pop_base() {return stack.pop();} + protected Object Peek_base() {return stack.peek();} + public java.util.Iterator iterator() {return stack.iterator();} +} diff --git a/100_core/src_140_list/gplx/lists/StackAdp_tst.java b/100_core/src_140_list/gplx/lists/StackAdp_tst.java new file mode 100644 index 000000000..54b253c1b --- /dev/null +++ b/100_core/src_140_list/gplx/lists/StackAdp_tst.java @@ -0,0 +1,33 @@ +/* +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.lists; import gplx.*; +import org.junit.*; +public class StackAdp_tst { + @Test public void XtoList() { + tst_XtoList(1, 2, 3); + } + void tst_XtoList(int... ary) { + StackAdp stack = StackAdp_.new_(); + for (int i : ary) + stack.Push(i); + ListAdp list = stack.XtoList(); + int[] actl = (int[])list.XtoAry(int.class); + for (int i = 0; i < ary.length; i++) + Tfds.Eq(ary[i], actl[i]); + } +} diff --git a/100_core/src_150_text/gplx/intl/Utf16_.java b/100_core/src_150_text/gplx/intl/Utf16_.java new file mode 100644 index 000000000..dc4be43dd --- /dev/null +++ b/100_core/src_150_text/gplx/intl/Utf16_.java @@ -0,0 +1,136 @@ +/* +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.intl; import gplx.*; +public class Utf16_ { + public static int Surrogate_merge(int hi, int lo) { // REF: http://perldoc.perl.org/Encode/Unicode.html + return 0x10000 + (hi - 0xD800) * 0x400 + (lo - 0xDC00); + } + public static void Surrogate_split(int v, Int_obj_ref hi, Int_obj_ref lo) { + hi.Val_((v - 0x10000) / 0x400 + 0xD800); + lo.Val_((v - 0x10000) % 0x400 + 0xDC00); + } + public static int Decode_to_int(byte[] ary, int pos) { + byte b0 = ary[pos]; + if ((b0 & 0x80) == 0) { + return b0; + } + else if ((b0 & 0xE0) == 0xC0) { + return ( b0 & 0x1f) << 6 + | ( ary[pos + 1] & 0x3f) + ; + } + else if ((b0 & 0xF0) == 0xE0) { + return ( b0 & 0x0f) << 12 + | ((ary[pos + 1] & 0x3f) << 6) + | ( ary[pos + 2] & 0x3f) + ; + } + else if ((b0 & 0xF8) == 0xF0) { + return ( b0 & 0x07) << 18 + | ((ary[pos + 1] & 0x3f) << 12) + | ((ary[pos + 2] & 0x3f) << 6) + | ( ary[pos + 3] & 0x3f) + ; + } + else throw Err_.new_fmt_("invalid utf8 byte: byte={0}", b0); + } + public static byte[] Encode_hex_to_bry(String raw) {return Encode_hex_to_bry(Bry_.new_ascii_(raw));} + public static byte[] Encode_hex_to_bry(byte[] raw) { + if (raw == null) return null; + int int_val = gplx.texts.HexDecUtl.parse_or_(raw, Int_.MinValue); + return int_val == Int_.MinValue ? null : Encode_int_to_bry(int_val); + } + public static byte[] Encode_int_to_bry(int c) { + int bry_len = Len_by_int(c); + byte[] bry = new byte[bry_len]; + Encode_int(c, bry, 0); + return bry; + } + public static int Encode_char(int c, char[] c_ary, int c_pos, byte[] b_ary, int b_pos) { + if ((c > -1) + && (c < 128)) { + b_ary[ b_pos] = (byte)c; + return 1; + } + else if (c < 2048) { + b_ary[ b_pos] = (byte)(0xC0 | (c >> 6)); + b_ary[++b_pos] = (byte)(0x80 | (c & 0x3F)); + return 1; + } + else if((c > 55295) // 0xD800 + && (c < 56320)) { // 0xDFFF + if (c_pos >= c_ary.length) throw Err_.new_fmt_("incomplete surrogate pair at end of String; char={0}", c); + char nxt_char = c_ary[c_pos + 1]; + int v = Surrogate_merge(c, nxt_char); + b_ary[b_pos] = (byte)(0xF0 | (v >> 18)); + b_ary[++b_pos] = (byte)(0x80 | (v >> 12) & 0x3F); + b_ary[++b_pos] = (byte)(0x80 | (v >> 6) & 0x3F); + b_ary[++b_pos] = (byte)(0x80 | (v & 0x3F)); + return 2; + } + else { + b_ary[b_pos] = (byte)(0xE0 | (c >> 12)); + b_ary[++b_pos] = (byte)(0x80 | (c >> 6) & 0x3F); + b_ary[++b_pos] = (byte)(0x80 | (c & 0x3F)); + return 1; + } + } + public static int Encode_int(int c, byte[] src, int pos) { + if ((c > -1) + && (c < 128)) { + src[ pos] = (byte)c; + return 1; + } + else if (c < 2048) { + src[ pos] = (byte)(0xC0 | (c >> 6)); + src[++pos] = (byte)(0x80 | (c & 0x3F)); + return 2; + } + else if (c < 65536) { + src[pos] = (byte)(0xE0 | (c >> 12)); + src[++pos] = (byte)(0x80 | (c >> 6) & 0x3F); + src[++pos] = (byte)(0x80 | (c & 0x3F)); + return 3; + } + else if (c < 2097152) { + src[pos] = (byte)(0xF0 | (c >> 18)); + src[++pos] = (byte)(0x80 | (c >> 12) & 0x3F); + src[++pos] = (byte)(0x80 | (c >> 6) & 0x3F); + src[++pos] = (byte)(0x80 | (c & 0x3F)); + return 4; + } + else throw Err_.new_fmt_("UTF-16 int must be between 0 and 2097152; char={0}", c); + } + private static int Len_by_int(int c) { + if ((c > -1) + && (c < 128)) return 1; // 1 << 7 + else if (c < 2048) return 2; // 1 << 11 + else if (c < 65536) return 3; // 1 << 16 + else if (c < 2097152) return 4; + else throw Err_.new_fmt_("UTF-16 int must be between 0 and 2097152; char={0}", c); + } + public static int Len_by_char(int c) { + if ((c > -1) + && (c < 128)) return 1; // 1 << 7 + else if (c < 2048) return 2; // 1 << 11 + else if((c > 55295) // 0xD800 + && (c < 56320)) return 4; // 0xDFFF + else if (c < 65536) return 3; // 1 << 16 + else throw Err_.new_fmt_("UTF-16 int must be between 0 and 65536; char={0}", c); + } +} diff --git a/100_core/src_150_text/gplx/intl/Utf16__tst.java b/100_core/src_150_text/gplx/intl/Utf16__tst.java new file mode 100644 index 000000000..1d7709a20 --- /dev/null +++ b/100_core/src_150_text/gplx/intl/Utf16__tst.java @@ -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 . +*/ +package gplx.intl; import gplx.*; +import org.junit.*; +public class Utf16__tst { + private Utf16__fxt fxt = new Utf16__fxt(); + @Test public void Encode_decode() { + fxt.Test_encode_decode(162, 194, 162); // cent + fxt.Test_encode_decode(8364, 226, 130, 172); // euro + fxt.Test_encode_decode(150370, 240, 164, 173, 162); // example from [[UTF-8]]; should be encoded as two bytes + } + @Test public void Encode_as_bry_by_hex() { + fxt.Test_Encode_hex_to_bry("00", 0); + fxt.Test_Encode_hex_to_bry("41", 65); + fxt.Test_Encode_hex_to_bry("0041", 65); + fxt.Test_Encode_hex_to_bry("00C0", 195, 128); + } + @Test public void Surrogate() { + fxt.Test_surrogate(0x64321, 0xD950, 0xDF21); // example from w:UTF-16 + fxt.Test_surrogate(66643, 55297, 56403); // example from d:Boomerang + } +} +class Utf16__fxt { + private Int_obj_ref hi_ref = Int_obj_ref.neg1_(), lo_ref = Int_obj_ref.neg1_(); + public void Test_encode_decode(int expd_c_int, int... expd_int) { + byte[] expd = Bry_.ints_(expd_int); + byte[] bfr = new byte[10]; + int bfr_len = Utf16_.Encode_int(expd_c_int, bfr, 0); + byte[] actl = Bry_.Mid_by_len(bfr, 0, bfr_len); + Tfds.Eq_ary(expd, actl); + int actl_c_int = Utf16_.Decode_to_int(bfr, 0); + Tfds.Eq(expd_c_int, actl_c_int); + } + public void Test_surrogate(int v, int hi, int lo) { + Tfds.Eq(v, Utf16_.Surrogate_merge((char)hi, (char)lo)); + Utf16_.Surrogate_split(v, hi_ref, lo_ref); + Tfds.Eq(hi, hi_ref.Val()); + Tfds.Eq(lo, lo_ref.Val()); + } + public void Test_Encode_hex_to_bry(String raw, int... expd) { + byte[] actl = Utf16_.Encode_hex_to_bry(raw); + Tfds.Eq_ary(Byte_.Ary_by_ints(expd), actl); + } +} diff --git a/100_core/src_150_text/gplx/intl/Utf8_.java b/100_core/src_150_text/gplx/intl/Utf8_.java new file mode 100644 index 000000000..dfc0acfb2 --- /dev/null +++ b/100_core/src_150_text/gplx/intl/Utf8_.java @@ -0,0 +1,106 @@ +/* +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.intl; import gplx.*; +public class Utf8_ { + public static int Len_of_char_by_1st_byte(byte b) {// SEE:w:UTF-8 + int i = b & 0xff; // PATCH.JAVA:need to convert to unsigned byte + switch (i) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: + case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28: case 29: case 30: case 31: + case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: + case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63: + case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: case 72: case 73: case 74: case 75: case 76: case 77: case 78: case 79: + case 80: case 81: case 82: case 83: case 84: case 85: case 86: case 87: case 88: case 89: case 90: case 91: case 92: case 93: case 94: case 95: + case 96: case 97: case 98: case 99: case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: case 108: case 109: case 110: case 111: + case 112: case 113: case 114: case 115: case 116: case 117: case 118: case 119: case 120: case 121: case 122: case 123: case 124: case 125: case 126: case 127: + case 128: case 129: case 130: case 131: case 132: case 133: case 134: case 135: case 136: case 137: case 138: case 139: case 140: case 141: case 142: case 143: + case 144: case 145: case 146: case 147: case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: case 156: case 157: case 158: case 159: + case 160: case 161: case 162: case 163: case 164: case 165: case 166: case 167: case 168: case 169: case 170: case 171: case 172: case 173: case 174: case 175: + case 176: case 177: case 178: case 179: case 180: case 181: case 182: case 183: case 184: case 185: case 186: case 187: case 188: case 189: case 190: case 191: + return 1; + case 192: case 193: case 194: case 195: case 196: case 197: case 198: case 199: case 200: case 201: case 202: case 203: case 204: case 205: case 206: case 207: + case 208: case 209: case 210: case 211: case 212: case 213: case 214: case 215: case 216: case 217: case 218: case 219: case 220: case 221: case 222: case 223: + return 2; + case 224: case 225: case 226: case 227: case 228: case 229: case 230: case 231: case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239: + return 3; + case 240: case 241: case 242: case 243: case 244: case 245: case 246: case 247: + return 4; + default: throw Err_.new_fmt_("invalid initial utf8 byte; byte={0}", b); + } + } + public static byte[] Get_char_at_pos_as_bry(byte[] bry, int pos) { + int len = Len_of_char_by_1st_byte(bry[pos]); + return Bry_.Mid(bry, pos, pos + len); + } + public static byte[] Increment_char_at_last_pos(byte[] bry) { // EX: abc -> abd; complexity is for multi-byte chars + int bry_len = bry.length; if (bry_len == 0) return bry; + int pos = bry_len - 1; + while (true) { // loop bwds + int cur_char_pos0 = Get_pos0_of_char_bwd(bry, pos); // get byte0 of char + int cur_char_len = (pos - cur_char_pos0) + 1; // calc len of char + int nxt_char = Codepoint_max; + if (cur_char_len == 1) { // len=1; just change 1 byte + nxt_char = Increment_char(bry[cur_char_pos0]); // get next char + if (nxt_char < 128) { // single-byte char; just change pos + bry = Bry_.Copy(bry); // always return new bry; never reuse existing + bry[cur_char_pos0] = (byte)nxt_char; + return bry; + } + } + int cur_char = Utf16_.Decode_to_int(bry, cur_char_pos0); + nxt_char = Increment_char(cur_char); + if (nxt_char != Int_.MinValue) { + byte[] nxt_char_as_bry = Utf16_.Encode_int_to_bry(nxt_char); + bry = Bry_.Add(Bry_.Mid(bry, 0, cur_char_pos0), nxt_char_as_bry); + return bry; + } + pos = cur_char_pos0 - 1; + if (pos < 0) return null; + } + } + public static int Get_pos0_of_char_bwd(byte[] bry, int pos) { // find pos0 of char while moving bwd through bry; see test + int stop = pos - 4; // UTF8 char has max of 4 bytes + if (stop < 0) stop = 0; // if at pos 0 - 3, stop at 0 + for (int i = pos - 1; i >= stop; i--) { // start at pos - 1, and move bwd; NOTE: pos - 1 to skip pos, b/c pos will never definitively yield any char_len info + byte b = bry[i]; + int char_len = Len_of_char_by_1st_byte(b); + switch (char_len) { // if char_len is multi-byte and pos is at correct multi-byte pos (pos - i = # of bytes - 1), then pos0 found; EX: � = {226,130,172}; 172 is skipped; 130 has len of 1 -> continue; 226 has len of 3 and is found at correct pos for 3 byte char -> return + case 2: if (pos - i == 1) return i; break; + case 3: if (pos - i == 2) return i; break; + case 4: if (pos - i == 3) return i; break; + } + } + return pos; // no mult-byte char found; return pos + } + @gplx.Internal protected static int Increment_char(int cur) { + while (cur++ < Codepoint_max) { + if (cur == Codepoint_surrogate_bgn) cur = Codepoint_surrogate_end + 1; // skip over surrogate range + if (!Codepoint_valid(cur)) continue; + return cur; + } + return Int_.MinValue; + } + private static boolean Codepoint_valid(int v) { + return Character.isDefined(v); + } + public static final int + Codepoint_max = 0x10FFFF //see http://unicode.org/glossary/ + , Codepoint_surrogate_bgn = 0xD800 + , Codepoint_surrogate_end = 0xDFFF + ; +} diff --git a/100_core/src_150_text/gplx/intl/Utf8__tst.java b/100_core/src_150_text/gplx/intl/Utf8__tst.java new file mode 100644 index 000000000..4835f3f21 --- /dev/null +++ b/100_core/src_150_text/gplx/intl/Utf8__tst.java @@ -0,0 +1,69 @@ +/* +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.intl; import gplx.*; +import org.junit.*; +public class Utf8__tst { + private Utf8__fxt fxt = new Utf8__fxt(); + @Test public void Get_pos0_of_char_bwd() { + fxt.Test_Get_pos0_of_char_bwd("abcd", 3); // len=1; (note that bry.len = 4) + fxt.Test_Get_pos0_of_char_bwd("a", 0); // len=1; short-String + fxt.Test_Get_pos0_of_char_bwd("abc¢", 3); // len=2; (note that bry.len = 5) + fxt.Test_Get_pos0_of_char_bwd("abc€", 3); // len=3; (note that bry.len = 6) + fxt.Test_Get_pos0_of_char_bwd("abc" + String_.new_utf8_(Byte_.Ary_by_ints(240, 164, 173, 162)), 3); // len=4; (note that bry.len = 7) + } + @Test public void Increment_char_at_last_pos() { + fxt.Test_Increment_char_at_last_pos("a", "b"); + fxt.Test_Increment_char_at_last_pos("abc", "abd"); + fxt.Test_Increment_char_at_last_pos("É", "Ê"); // len=2 + fxt.Test_Increment_char_at_last_pos("€", "₭"); // len=3 + } +// @Test public void Increment_char_at_last_pos_exhaustive_check() { // check all values; commented for perf +// Bry_bfr bfr = Bry_bfr.new_(); +// int bgn = 32; +// while (true) { +// byte[] bgn_bry = Utf16_.Encode_int_to_bry(bgn); +// int end = Utf8_.Increment_char(bgn); +// if (end == Utf8_.Codepoint_max) break; +//// if (bgn > 1024 * 1024) break; +// byte[] end_by_codepoint_next = Utf16_.Encode_int_to_bry(end); +// byte[] end_by_increment_char = Utf8_.Increment_char_at_last_pos(bgn_bry); +// if (!Bry_.Eq(end_by_codepoint_next, end_by_increment_char)) { +// Tfds.Write(bgn); +// } +//// bfr .Add_int_variable(bgn).Add_byte(Byte_ascii.Tab) +//// .Add(bgn_bry).Add_byte(Byte_ascii.Tab) +//// .Add(end_by_codepoint_next).Add_byte(Byte_ascii.Tab) +//// .Add(end_by_increment_char).Add_byte(Byte_ascii.Tab) +//// .Add_byte_nl() +//// ; +// bgn = end; +// bgn_bry = end_by_codepoint_next; +// } +// Tfds.WriteText(bfr.XtoStrAndClear()); +// } +} +class Utf8__fxt { + public void Test_Get_pos0_of_char_bwd(String str, int expd) { + byte[] bry = Bry_.new_utf8_(str); + int pos = bry.length - 1; // always start from last char + Tfds.Eq(expd, Utf8_.Get_pos0_of_char_bwd(bry, pos)); + } + public void Test_Increment_char_at_last_pos(String str, String expd) { + Tfds.Eq(expd, String_.new_utf8_(Utf8_.Increment_char_at_last_pos(Bry_.new_utf8_(str)))); + } +} diff --git a/100_core/src_150_text/gplx/texts/Base32Converter.java b/100_core/src_150_text/gplx/texts/Base32Converter.java new file mode 100644 index 000000000..423d3c12e --- /dev/null +++ b/100_core/src_150_text/gplx/texts/Base32Converter.java @@ -0,0 +1,99 @@ +/* +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.texts; import gplx.*; +public class Base32Converter { + public static String EncodeString(String orig) {return Encode(Bry_.new_utf8_(orig));} + public static String Encode(byte[] raw) { + int i = 0, index = 0, digit = 0; int currByte, nextByte; + int rawLen = Array_.Len(raw); + char[] ary = new char[(rawLen + 7) * 8 / 5]; int aryPos = 0; + while (i < rawLen) { + currByte = (raw[i] >= 0) ? raw[i] : raw[i] + 256; // unsign; java converts char 128+ -> byte -128 + if (index > 3) { /* Is the curPath digit going to span a byte boundary? */ + if ((i+1) < rawLen) + nextByte = (raw[i+1] >= 0) ? raw[i+1] : (raw[i+1] + 256); + else + nextByte = 0; + + digit = currByte & (0xFF >> index); + index = (index + 5) % 8; + digit <<= index; + digit |= nextByte >> (8 - index); + i++; + } + else { + digit = (currByte >> (8 - (index + 5))) & 0x1F; + index = (index + 5) % 8; + if (index == 0) i++; + } + ary[aryPos++] = String_.CharAt(chars, digit); + } + return new String(ary, 0, aryPos); + } + public static String DecodeString(String orig) {return String_.new_utf8_(Decode(orig));} + public static byte[] Decode(String raw) { + int i, index, lookup, offset; byte digit; + int rawLen = String_.Len(raw); + int rvLen = rawLen * 5 / 8; + byte[] rv = new byte[rvLen]; + for (i = 0, index = 0, offset = 0; i < rawLen; i++) { + lookup = String_.CharAt(raw, i) - '0'; + if (lookup < 0 || lookup >= valsLen) continue; /* Skip any char outside the lookup table */ + digit = vals[lookup]; + if (digit == 0x7F) continue; /* If this digit is not in the table, ignore it */ + + if (index <= 3) { + index = (index + 5) % 8; + if (index == 0) { + rv[offset] |= digit; + offset++; + if(offset >= rvLen) break; + } + else + rv[offset] |= (byte)(digit << (8 - index)); + } + else { + index = (index + 5) % 8; + rv[offset] |= (byte)(digit >> index); + offset++; + + if(offset >= rvLen) break; + rv[offset] |= (byte)(digit << (8 - index)); + } + } + return rv; + } + static String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + static int valsLen = 80; + static byte[] vals = { + 0x7F,0x7F,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, // '0', '1', '2', '3', '4', '5', '6', '7' + 0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F, // '8', '9', ':', ';', '<', '=', '>', '?' + 0x7F,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' + 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' + 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' + 0x17,0x18,0x19,0x7F,0x7F,0x7F,0x7F,0x7F, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_' + 0x7F,0x00,0x01,0x02,0x03,0x04,0x05,0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g' + 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' + 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' + 0x17,0x18,0x19,0x7F,0x7F,0x7F,0x7F,0x7F // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL' + }; +} +/* +source: Azraeus excerpt in java + http://www.koders.com/java/fidA4D6F0DF43E6E9A6B518762366AB7232C14E9DB7.aspx +*/ diff --git a/100_core/src_150_text/gplx/texts/Base64Converter.java b/100_core/src_150_text/gplx/texts/Base64Converter.java new file mode 100644 index 000000000..b200c8b4e --- /dev/null +++ b/100_core/src_150_text/gplx/texts/Base64Converter.java @@ -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 . +*/ +package gplx.texts; import gplx.*; +public class Base64Converter { + private final static char[] ALPHABET = String_.XtoCharAry("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + private static int[] toInt = null;//new int[128]; + static void Init() { + toInt = new int[128]; + for(int i=0; i< ALPHABET.length; i++){ + toInt[ALPHABET[i]]= i; + } + } + public static String EncodeString(String orig) {return Encode(Bry_.new_utf8_(orig));} + public static String Encode(byte[] buf){ + if (toInt == null) Init(); + int size = buf.length; + char[] ar = new char[((size + 2) / 3) * 4]; + int a = 0; + int i=0; + while(i < size){ + byte b0 = buf[i++]; + byte b1 = (i < size) ? buf[i++] : (byte)0; + byte b2 = (i < size) ? buf[i++] : (byte)0; + + int mask = 0x3F; + ar[a++] = ALPHABET[(b0 >> 2) & mask]; + ar[a++] = ALPHABET[((b0 << 4) | ((b1 & 0xFF) >> 4)) & mask]; + ar[a++] = ALPHABET[((b1 << 2) | ((b2 & 0xFF) >> 6)) & mask]; + ar[a++] = ALPHABET[b2 & mask]; + } + switch(size % 3){ + case 1: ar[--a] = '='; + ar[--a] = '='; + break; + case 2: ar[--a] = '='; break; + } + return new String(ar); + } + public static String DecodeString(String orig) {return String_.new_utf8_(Decode(orig));} + public static byte[] Decode(String s){ + if (toInt == null) Init(); + int sLen = String_.Len(s); + int delta = String_.HasAtEnd(s, "==") ? 2 : String_.HasAtEnd(s, "=") ? 1 : 0; + byte[] buffer = new byte[sLen *3/4 - delta]; + int mask = 0xFF; + int index = 0; + for(int i=0; i< sLen; i+=4){ + int c0 = toInt[String_.CharAt(s, i)]; + int c1 = toInt[String_.CharAt(s, i + 1)]; + buffer[index++]= (byte)(((c0 << 2) | (c1 >> 4)) & mask); + if(index >= buffer.length){ + return buffer; + } + int c2 = toInt[String_.CharAt(s, i + 2)]; + buffer[index++]= (byte)(((c1 << 4) | (c2 >> 2)) & mask); + if(index >= buffer.length){ + return buffer; + } + int c3 = toInt[String_.CharAt(s, i + 3)]; + buffer[index++]= (byte)(((c2 << 6) | c3) & mask); + } + return buffer; + } +} diff --git a/100_core/src_150_text/gplx/texts/BaseXXConverter_tst.java b/100_core/src_150_text/gplx/texts/BaseXXConverter_tst.java new file mode 100644 index 000000000..c75c3f593 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/BaseXXConverter_tst.java @@ -0,0 +1,58 @@ +/* +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.texts; import gplx.*; +import org.junit.*; +public class BaseXXConverter_tst { + @Test public void Base32() { + tst_Base32("", ""); + tst_Base32("f", "MY"); + tst_Base32("fo", "MZXQ"); + tst_Base32("foo", "MZXW6"); + tst_Base32("foob", "MZXW6YQ"); + tst_Base32("fooba", "MZXW6YTB"); + tst_Base32("foobar", "MZXW6YTBOI"); + tst_Base32("A", "IE"); + tst_Base32("a", "ME"); + tst_Base32("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLIZDGNBVGY3Q"); + } + @Test public void Base64() { + tst_Base64("", ""); + tst_Base64("f", "Zg=="); + tst_Base64("fo", "Zm8="); + tst_Base64("foo", "Zm9v"); + tst_Base64("foob", "Zm9vYg=="); + tst_Base64("fooba", "Zm9vYmE="); + tst_Base64("foobar", "Zm9vYmFy"); +// tst_Base64("A", "IE"); +// tst_Base64("a", "ME"); +// tst_Base64("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", "IFBEGRCFIZDUQSKKJNGE2TSPKBIVEU2UKVLFOWCZLIZDGNBVGY3Q"); + } + void tst_Base32(String orig, String expd) { + String actl = Base32Converter.EncodeString(orig); + Tfds.Eq(expd, actl); + String decode = Base32Converter.DecodeString(actl); + Tfds.Eq(orig, decode); + } + void tst_Base64(String orig, String expd) { + String actl = Base64Converter.EncodeString(orig); + Tfds.Eq(expd, actl); + String decode = Base64Converter.DecodeString(actl); + Tfds.Eq(orig, decode); + } +} +//http://tools.ietf.org/html/rfc4648: test vectors for "foobar" diff --git a/100_core/src_150_text/gplx/texts/CharStream.java b/100_core/src_150_text/gplx/texts/CharStream.java new file mode 100644 index 000000000..932da2b38 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/CharStream.java @@ -0,0 +1,67 @@ +/* +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.texts; import gplx.*; +public class CharStream { + public char[] Ary() {return ary;} char[] ary; + public int Len() {return len;} int len; + public int Pos() {return pos;} int pos = BgnPos; static final int BgnPos = -1; + public boolean AtBgn() {return pos <= BgnPos;} + public boolean AtEnd() {return pos >= len;} + public boolean AtMid() {return pos > BgnPos && pos < len;} + public char Cur() {try {return ary[pos];} catch (Exception exc) {Err_.Noop(exc); throw Err_.missing_idx_(pos, this.Len());}} + public void MoveNext() {pos++;} + public void MoveNextBy(int offset) {pos += offset;} + public void MoveBack() {pos--;} + public void MoveBackBy(int offset) {pos -= offset;} + public void MoveTo(int val) {pos = val;} + public boolean Match(String match) { + int matchLen = String_.Len(match); + for (int i = 0; i < matchLen; i++) { + int cur = pos + i; + if (cur >= len || ary[cur] != String_.CharAt(match, i)) return false; + } + return true; + } + public boolean MatchAndMove(String match) { + int matchLen = String_.Len(match); + boolean rv = Match(match); + if (rv) MoveNextBy(matchLen); + return rv; + } + public boolean MatchAndMove(char match) { + boolean rv = ary[pos] == match; + if (rv) pos++; + return rv; + } + public String XtoStr() {return Char_.XtoStr(ary, 0, len);} + public String XtoStrAtCur(int length) { + length = (pos + length > len) ? len - pos : length; + return Char_.XtoStr(ary, pos, length); + } + public String XtoStrByPos(int bgn, int end) { + if (bgn < 0) bgn = 0; if (end > len - 1) end = len - 1; + return Char_.XtoStr(ary, bgn, end - bgn + 1); + } + public static CharStream pos0_(String text) { + CharStream rv = new CharStream(); + rv.ary = String_.XtoCharAry(text); + rv.len = Array_.Len(rv.ary); + rv.MoveNext(); // bgn at pos=0 + return rv; + } CharStream(){} +} diff --git a/100_core/src_150_text/gplx/texts/CharStream_tst.java b/100_core/src_150_text/gplx/texts/CharStream_tst.java new file mode 100644 index 000000000..16e98a3a1 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/CharStream_tst.java @@ -0,0 +1,61 @@ +/* +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.texts; import gplx.*; +import org.junit.*; +public class CharStream_tst { + @Before public void setup() { + stream = CharStream.pos0_("abcdefgh"); + } + @Test public void XtoStr() { + Tfds.Eq(stream.XtoStr(), "abcdefgh"); + } + @Test public void CurrentText() { + stream.MoveNextBy(1); + Tfds.Eq(stream.XtoStrAtCur(2), "bc"); + Tfds.Eq(stream.XtoStr(), "abcdefgh"); + } + @Test public void CurrentText_outOfBounds() { + stream.MoveNextBy(7); + Tfds.Eq(stream.XtoStrAtCur(2), "h"); + } + @Test public void Match() { + stream.MoveNextBy(6); + tst_Match(true, "g"); + tst_Match(false, "z"); + tst_Match(true, "gh"); + tst_Match(false, "gz"); + tst_Match(false, "ghi"); + } + @Test public void AtBounds() { + stream.MoveTo(-1); + tst_AtBounds(true, false, false); + + stream.MoveTo(0); + tst_AtBounds(false, true, false); + + stream.MoveTo(stream.Len()); + tst_AtBounds(false, false, true); + } + void tst_Match(boolean expd, String text) {Tfds.Eq(expd, stream.Match(text));} + void tst_AtBounds(boolean atBgn, boolean atMid, boolean atEnd) { + Tfds.Eq(atBgn, stream.AtBgn()); + Tfds.Eq(atMid, stream.AtMid()); + Tfds.Eq(atEnd, stream.AtEnd()); + } + CharStream stream; +} diff --git a/100_core/src_150_text/gplx/texts/EncodingAdp.java b/100_core/src_150_text/gplx/texts/EncodingAdp.java new file mode 100644 index 000000000..6fff6ec08 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/EncodingAdp.java @@ -0,0 +1,29 @@ +/* +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 . +*/ +//namespace gplx.texts { +// public class EncodingAdp { +// public int Val() {return val;} int val; +// public String Name() {return name;} private String name; +// +// EncodingAdp(String n, int v) {name = n; val = v;} +// //public Encoding UnderObj() {return under;} Encoding under; + // public static final EncodingAdp Default = new EncodingAdp("default", 1); // Encoding.Default b/c characters like — could not be read under UTF-8 +// public static final EncodingAdp Ascii = new EncodingAdp("ASCII", 2); +// public static final EncodingAdp Utf8 = new EncodingAdp("UTF-8", 3); // NOTE: must suppress BOM or else  will show up in Excel 2000 +// } +//} diff --git a/100_core/src_150_text/gplx/texts/HexDecUtl.java b/100_core/src_150_text/gplx/texts/HexDecUtl.java new file mode 100644 index 000000000..31e28ddb8 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/HexDecUtl.java @@ -0,0 +1,96 @@ +/* +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.texts; import gplx.*; +public class HexDecUtl { + public static int parse_or_(String raw, int or) { + int rv = 0; int digit; int factor = 1, rawLen = String_.Len(raw); + for (int i = rawLen - 1; i >= 0; i--) { + digit = XtoInt(String_.CharAt(raw, i)); + if (digit == -1) return or; + rv += digit * factor; + factor *= 16; + } + return rv; + } + public static int parse_or_(byte[] raw, int or) {return parse_or_(raw, 0, raw.length, or);} + public static int parse_or_(byte[] raw, int bgn, int end, int or) { + int rv = 0; int factor = 1; + byte b = Byte_.MaxValue_127; + for (int i = end - 1; i >= bgn; i--) { + switch (raw[i]) { + case Byte_ascii.Num_0: b = 0; break; case Byte_ascii.Num_1: b = 1; break; case Byte_ascii.Num_2: b = 2; break; case Byte_ascii.Num_3: b = 3; break; case Byte_ascii.Num_4: b = 4; break; + case Byte_ascii.Num_5: b = 5; break; case Byte_ascii.Num_6: b = 6; break; case Byte_ascii.Num_7: b = 7; break; case Byte_ascii.Num_8: b = 8; break; case Byte_ascii.Num_9: b = 9; break; + case Byte_ascii.Ltr_A: b = 10; break; case Byte_ascii.Ltr_B: b = 11; break; case Byte_ascii.Ltr_C: b = 12; break; case Byte_ascii.Ltr_D: b = 13; break; case Byte_ascii.Ltr_E: b = 14; break; case Byte_ascii.Ltr_F: b = 15; break; + case Byte_ascii.Ltr_a: b = 10; break; case Byte_ascii.Ltr_b: b = 11; break; case Byte_ascii.Ltr_c: b = 12; break; case Byte_ascii.Ltr_d: b = 13; break; case Byte_ascii.Ltr_e: b = 14; break; case Byte_ascii.Ltr_f: b = 15; break; + default: b = Byte_.MaxValue_127; break; + } + if (b == Byte_.MaxValue_127) return or; + rv += b * factor; + factor *= 16; + } + return rv; + } + public static int parse_(String raw) { + int rv = parse_or_(raw, -1); if (rv == -1) throw Err_.parse_("HexDec", "raw"); + return rv; + } + public static String XtoStr(int val, int pad) { + char[] ary = new char[8]; int idx = 8; // 8 is max len of hexString; (2^4 * 8); EX: int.MaxValue = 7FFFFFFF + do { + int byt = val % 16; + ary[--idx] = XtoChar(byt); + val /= 16; + } while (val > 0); + while (8 - idx < pad) // pad left with zeros + ary[--idx] = '0'; + return String_.new_charAry_(ary, idx, 8-idx); + } + static int XtoInt(char c) { + switch (c) { + case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; + case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; + case 'A': return 10; case 'B': return 11; case 'C': return 12; case 'D': return 13; case 'E': return 14; case 'F': return 15; + case 'a': return 10; case 'b': return 11; case 'c': return 12; case 'd': return 13; case 'e': return 14; case 'f': return 15; + default: return -1; + } + } + static char XtoChar(int val) { + switch (val) { + case 0: return '0'; case 1: return '1'; case 2: return '2'; case 3: return '3'; case 4: return '4'; + case 5: return '5'; case 6: return '6'; case 7: return '7'; case 8: return '8'; case 9: return '9'; + case 10: return 'A'; case 11: return 'B'; case 12: return 'C'; case 13: return 'D'; case 14: return 'E'; case 15: return 'F'; + default: throw Err_.parse_("hexstring", Int_.XtoStr(val)); + } + } + static byte Xto_byte(int v) { + switch (v) { + case 0: return Byte_ascii.Num_0; case 1: return Byte_ascii.Num_1; case 2: return Byte_ascii.Num_2; case 3: return Byte_ascii.Num_3; case 4: return Byte_ascii.Num_4; + case 5: return Byte_ascii.Num_5; case 6: return Byte_ascii.Num_6; case 7: return Byte_ascii.Num_7; case 8: return Byte_ascii.Num_8; case 9: return Byte_ascii.Num_9; + case 10: return Byte_ascii.Ltr_A; case 11: return Byte_ascii.Ltr_B; case 12: return Byte_ascii.Ltr_C; case 13: return Byte_ascii.Ltr_D; case 14: return Byte_ascii.Ltr_E; case 15: return Byte_ascii.Ltr_F; + default: throw Err_.parse_("hexstring", Int_.XtoStr(v)); + } + } + public static void Write(byte[] bry, int bgn, int end, int val) { + for (int i = end - 1; i > bgn - 1; i--) { + int b = val % 16; + bry[i] = Xto_byte(b); + val /= 16; + if (val == 0) break; + } + } +} diff --git a/100_core/src_150_text/gplx/texts/HexDecUtl_tst.java b/100_core/src_150_text/gplx/texts/HexDecUtl_tst.java new file mode 100644 index 000000000..b67848e6e --- /dev/null +++ b/100_core/src_150_text/gplx/texts/HexDecUtl_tst.java @@ -0,0 +1,63 @@ +/* +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.texts; import gplx.*; +import org.junit.*; +public class HexDecUtl_tst { + @Test public void XtoInt() { + tst_XtoInt("0", 0); + tst_XtoInt("F", 15); + tst_XtoInt("0F", 15); + tst_XtoInt("10", 16); + tst_XtoInt("20", 32); + tst_XtoInt("FF", 255); + tst_XtoInt("100", 256); + tst_XtoInt("0a", 10); + tst_XtoInt("7FFFFFFF", Int_.MaxValue); + tst_XtoInt_bry("100", 256); + } + @Test public void XtoStr() { + tst_XtoStr(0, "0"); + tst_XtoStr(15, "F"); + tst_XtoStr(16, "10"); + tst_XtoStr(32, "20"); + tst_XtoStr(255, "FF"); + tst_XtoStr(Int_.MaxValue, "7FFFFFFF"); + + tst_XtoStr(15, 2, "0F"); + tst_XtoStr(15, 3, "00F"); + } + @Test public void Write() { + tst_Write("[00000000]", 1, 9, 15, "[0000000F]"); + tst_Write("[00000000]", 1, 9, 255, "[000000FF]"); + } + private void tst_Write(String s, int bgn, int end, int val, String expd) { + byte[] bry = Bry_.new_ascii_(s); + HexDecUtl.Write(bry, bgn, end, val); + Tfds.Eq(expd, String_.new_ascii_(bry)); + } + private void tst_XtoInt(String raw, int expd) { + int actl = HexDecUtl.parse_(raw); + Tfds.Eq(expd, actl); + } + private void tst_XtoInt_bry(String raw, int expd) {Tfds.Eq(expd, HexDecUtl.parse_or_(Bry_.new_ascii_(raw), -1));} + private void tst_XtoStr(int val, String expd) {tst_XtoStr(val, 0, expd);} + private void tst_XtoStr(int val, int pad, String expd) { + String actl = HexDecUtl.XtoStr(val, pad); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxAdp.java b/100_core/src_150_text/gplx/texts/RegxAdp.java new file mode 100644 index 000000000..2071b660a --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxAdp.java @@ -0,0 +1,60 @@ +/* +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.texts; import gplx.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +public class RegxAdp { + void Under_sync() { + try {under = Pattern.compile(pattern, Pattern.DOTALL);} + catch (Exception e) { // NOTE: if invalid, then default to empty pattern (which should return nothing); EX:d:〆る generates [^]; DATE:2013-10-20 + pattern_is_invalid = true; + under = Pattern.compile("", Pattern.DOTALL); + } + } private Pattern under; + public RegxMatch Match(String input, int bgn) { + Matcher match = under.matcher(input); + boolean success = match.find(bgn); + int match_bgn = success ? match.start() : String_.Find_none; + int match_end = success ? match.end() : String_.Find_none; + RegxGroup[] ary = RegxGroup.Ary_empty; + int groups_len = match.groupCount(); + if (success && groups_len > 0) { + ary = new RegxGroup[groups_len]; + for (int i = 0; i < groups_len; i++) + ary[i] = new RegxGroup(true, match.start(i + 1), match.end(i + 1), match.group(i + 1)); + } + return new RegxMatch(success, match_bgn, match_end, ary); + } + public String ReplaceAll(String input, String replace) {return under.matcher(input).replaceAll(replace);} + public String Pattern() {return pattern;} public RegxAdp Pattern_(String val) {pattern = val; Under_sync(); return this;} private String pattern; + public boolean Pattern_is_invalid() {return pattern_is_invalid;} private boolean pattern_is_invalid = false; + public RegxMatch[] Match_all(String text, int bgn) { + int idx = bgn; + ListAdp rv = ListAdp_.new_(); + int len = String_.Len(text); + while (idx < len) { + RegxMatch match = this.Match(text, idx); + if (match.Rslt_none()) break; + rv.Add(match); + int find_bgn = match.Find_bgn(); + idx = find_bgn + match.Find_len(); + } + return (RegxMatch[])rv.XtoAry(RegxMatch.class); + } + @gplx.Internal protected RegxAdp(String regx) {Pattern_(regx);} +} diff --git a/100_core/src_150_text/gplx/texts/RegxAdp_.java b/100_core/src_150_text/gplx/texts/RegxAdp_.java new file mode 100644 index 000000000..e09000507 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxAdp_.java @@ -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 . +*/ +package gplx.texts; import gplx.*; +public class RegxAdp_ { + public static RegxAdp_mpo_find Find_args(String input, String find) {return new RegxAdp_mpo_find().Input_(input).Find_(find);} + public static RegxAdp_mpo_replace Replace_args(String input, String find, String replace) {return new RegxAdp_mpo_replace().Input_(input).Find_(find).Replace_(replace);} + public static RegxAdp new_(String pattern) {return new RegxAdp(pattern);} + public static String Replace(String raw, String regx, String replace) {return Replace_args(raw, regx, replace).Exec_asStr();} + public static boolean Match(String input, String pattern) { + RegxAdp rv = new RegxAdp(pattern); + return rv.Match(input, 0).Rslt(); + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxAdp__tst.java b/100_core/src_150_text/gplx/texts/RegxAdp__tst.java new file mode 100644 index 000000000..590bdbcb3 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxAdp__tst.java @@ -0,0 +1,92 @@ +/* +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.texts; import gplx.*; +import org.junit.*; +public class RegxAdp__tst implements TfdsEqListItmStr { + @Test public void Match() { + tst_Match("a", "a", true); // basic + tst_Match("a", "b", false); // matchNot + tst_Match("a", "ab", true); // matchPart + tst_Match("a\\+b", "a+b", true); // matchEscape + tst_Match("[^a]", "b", true); // charSet_negate + } void tst_Match(String find, String input, boolean expd) {Tfds.Eq(expd, RegxAdp_.Match(input, find));} + @Test public void Match_all() { + tst_Match_all("#REDIRECT [[Template:Error]]", "^\\p{Nd}*", 0); // handle match = true but len = 0 DATE:2013-04-10 + } void tst_Match_all(String input, String regx, int expd) {Tfds.Eq(expd, RegxAdp_.new_(regx).Match_all(input, 0).length);} + @Test public void Replace() { + tst_Replace("ab", "a", "b", "bb"); // basic + tst_Replace("ab", "c", "b", "ab"); // replaceNot + tst_Replace("aba", "a", "b", "bbb"); // replaceMultiple + } void tst_Replace(String input, String find, String replace, String expd) {Tfds.Eq(expd, RegxAdp_.Replace(input, find, replace));} + @Test public void Match_WholeWord() { + tst_WholeWord("a", "ab a", true); // pass a + tst_WholeWord("a", "ab c", false); // fail ab + tst_WholeWord("a", "a_", false); // fail a_ + tst_WholeWord("[a]", "a [a] c", true); // pass [a] + tst_WholeWord("[a]", "a[a]c", false); // fail a[a]c + } void tst_WholeWord(String regx, String text, boolean expd) {Tfds.Eq(expd, RegxAdp_.Match(text, RegxBldr.WholeWord(regx)));} + @Test public void Match_As() { + tst_Regx("public static [A-Za-z0-9_]+ as_\\(Object obj\\)", "public static Obj1 as_(Object obj) {return obj instanceof Obj1 ? (Obj1)obj : null;}", true); + tst_Regx("public static [A-Za-z0-9_]+ as_\\(Object obj\\)", "public static boolean Asterisk(Object obj) {}", false); + } void tst_Regx(String regx, String text, boolean expd) {Tfds.Eq(expd, RegxAdp_.Match(text, regx));} + @Test public void Find() { + tst_Matches("b", "a b c b a", match_(2, 1), match_(6, 1)); + tst_Matches("d", "a b c b a"); + tst_Matches("b", "a b c b a b b", matches_(2, 6, 10, 12)); // BUGFIX: multiple entries did not work b/c of += instead of + + } + @Test public void Groups() { + tst_Groups("abc def ghi dz", "(d\\p{L}+)", "def", "dz"); + } + RegxMatch[] matches_(int... bgnAry) { + int aryLen = Array_.Len(bgnAry); + RegxMatch[] rv = new RegxMatch[aryLen]; + for (int i = 0; i < aryLen; i++) + rv[i] = match_(bgnAry[i]); + return rv; + } + RegxMatch match_(int bgn) {return match_(bgn, Int_.MinValue);} + RegxMatch match_(int bgn, int len) {return new RegxMatch(true, bgn, bgn + len, RegxGroup.Ary_empty);} + void tst_Matches(String find, String input, RegxMatch... expd) { + ListAdp expdList = Array_.XtoList(expd); + ListAdp actlList = RegxAdp_.Find_args(input, find).Exec_asList(); + Tfds.Eq_list(expdList, actlList, this); + } + void tst_Groups(String text, String regx, String... expd) { + RegxAdp regx_mgr = RegxAdp_.new_(regx); + RegxMatch[] rslts = regx_mgr.Match_all(text, 0); + Tfds.Eq_ary_str(expd, Xto_ary(rslts)); + } + String[] Xto_ary(RegxMatch[] ary) { + ListAdp rv = ListAdp_.new_(); + int len = ary.length; + for (int i = 0; i < len; i++) { + RegxMatch itm = ary[i]; + int cap_len = itm.Groups().length; + for (int j = 0; j < cap_len; j++) { + rv.Add(itm.Groups()[j].Val()); + } + } + return rv.XtoStrAry(); + } + public String XtoStr(Object curObj, Object expdObj) { + RegxMatch cur = (RegxMatch)curObj, expd = (RegxMatch)expdObj; + String rv = "bgn=" + cur.Find_bgn(); + if (expd != null && expd.Find_len() != Int_.MinValue) rv += " len=" + cur.Find_len(); + return rv; + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxAdp_mpo_find.java b/100_core/src_150_text/gplx/texts/RegxAdp_mpo_find.java new file mode 100644 index 000000000..5cabafcde --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxAdp_mpo_find.java @@ -0,0 +1,36 @@ +/* +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.texts; import gplx.*; +public class RegxAdp_mpo_find { + public String Input() {return input;} public RegxAdp_mpo_find Input_(String val) {input = val; return this;} private String input; + public String Find() {return find;} public RegxAdp_mpo_find Find_(String val) {find = val; return this;} private String find; + public ListAdp Exec_asList() { + RegxAdp regx = RegxAdp_.new_(find); + int idx = 0; + ListAdp rv = ListAdp_.new_(); + while (true) { + RegxMatch match = regx.Match(input, idx); + if (match.Rslt_none()) break; + rv.Add(match); + int findBgn = match.Find_bgn(); + idx = findBgn + match.Find_len(); + if (idx > String_.Len(input)) break; + } + return rv; + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxAdp_mpo_replace.java b/100_core/src_150_text/gplx/texts/RegxAdp_mpo_replace.java new file mode 100644 index 000000000..9165092e7 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxAdp_mpo_replace.java @@ -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 . +*/ +package gplx.texts; import gplx.*; +public class RegxAdp_mpo_replace { + public String Input() {return input;} public RegxAdp_mpo_replace Input_(String val) {input = val; return this;} private String input; + public String Find() {return find;} public RegxAdp_mpo_replace Find_(String val) {find = val; return this;} private String find; + public String Replace() {return replace;} public RegxAdp_mpo_replace Replace_(String val) {replace = val; return this;} private String replace; + public String Exec_asStr() { + RegxAdp regx = RegxAdp_.new_(find); + return regx.ReplaceAll(input, replace); + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxBldr.java b/100_core/src_150_text/gplx/texts/RegxBldr.java new file mode 100644 index 000000000..01de147d4 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxBldr.java @@ -0,0 +1,61 @@ +/* +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.texts; import gplx.*; +public class RegxBldr { + public static String Includes(String characters) {return String_.Concat_any(RegxBldr.Tkn_CharSetBegin, characters, RegxBldr.Tkn_CharSetEnd);} + public static String Excludes(String characters) {return String_.Concat_any(RegxBldr.Tkn_CharSetBegin, RegxBldr.Tkn_Not, characters, RegxBldr.Tkn_CharSetEnd);} + public static String WholeWord(String word) {return String_.Concat_any("(?. +*/ +package gplx.texts; import gplx.*; +public class RegxGroup { + public RegxGroup(boolean rslt, int bgn, int end, String val) {this.rslt = rslt; this.bgn = bgn; this.end = end; this.val = val;} + public boolean Rslt() {return rslt;} private boolean rslt; + public int Bgn() {return bgn;} int bgn; + public int End() {return end;} int end; + public String Val() {return val;} private String val; + public static final RegxGroup[] Ary_empty = new RegxGroup[0]; +} diff --git a/100_core/src_150_text/gplx/texts/RegxMatch.java b/100_core/src_150_text/gplx/texts/RegxMatch.java new file mode 100644 index 000000000..07ec19656 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxMatch.java @@ -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 . +*/ +package gplx.texts; import gplx.*; +public class RegxMatch { + public RegxMatch(boolean rslt, int find_bgn, int find_end, RegxGroup[] groups) {this.rslt = rslt; this.find_bgn = find_bgn; this.find_end = find_end; this.groups = groups;} + public boolean Rslt() {return rslt;} private boolean rslt; + public boolean Rslt_none() {return !rslt || (find_end - find_bgn) == 0;} // NOTE: find_end - find_bgn == 0 means find.length == 0; treating these as failed match, even thought matcher.find = true; DATE:2013-04-10 + public int Find_bgn() {return find_bgn;} int find_bgn; + public int Find_end() {return find_end;} int find_end; + public int Find_len() {return find_end - find_bgn;} + public RegxGroup[] Groups() {return groups;} RegxGroup[] groups = RegxGroup.Ary_empty; + public static final RegxMatch[] Ary_empty = new RegxMatch[0]; +} diff --git a/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch.java b/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch.java new file mode 100644 index 000000000..f156a3e9f --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch.java @@ -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 . +*/ +package gplx.texts; import gplx.*; +public class RegxPatn_cls_ioMatch { + public String Raw() {return raw;} private String raw; + public boolean CaseSensitive() {return caseSensitive;} private boolean caseSensitive; + public boolean Matches(String text) { + text = String_.CaseNormalize(caseSensitive, text); + return RegxAdp_.Match(text, compiled);} // WNT-centric: Io_mgr paths are case-insensitive; + @Override public String toString() {return raw;} + + String compiled; + @gplx.Internal protected RegxPatn_cls_ioMatch(String raw, String compiled, boolean caseSensitive) { + this.caseSensitive = caseSensitive; + this.raw = raw; + compiled = String_.CaseNormalize(caseSensitive, compiled); + this.compiled = compiled; + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch_.java b/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch_.java new file mode 100644 index 000000000..2c4a0730e --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch_.java @@ -0,0 +1,52 @@ +/* +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.texts; import gplx.*; +public class RegxPatn_cls_ioMatch_ { + public static final String Wildcard = "*"; + public static final String OrDelimiter = "|"; + public static final RegxPatn_cls_ioMatch All = RegxPatn_cls_ioMatch_.parse_(Wildcard, false); + public static final String ImpossiblePath = "<>"; //"<>" should be an impossible url; NOTE: do not pick * or | or : or \ + public static final RegxPatn_cls_ioMatch None = RegxPatn_cls_ioMatch_.parse_(RegxPatn_cls_ioMatch_.ImpossiblePath, false); + public static RegxPatn_cls_ioMatch cast_(Object obj) {try {return (RegxPatn_cls_ioMatch)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, RegxPatn_cls_ioMatch.class, obj);}} + public static RegxPatn_cls_ioMatch parse_(String raw, boolean caseSensitive) { + String compiled = RegxPatn_cls_ioMatch_.Compile(raw); + return new RegxPatn_cls_ioMatch(raw, compiled, caseSensitive); + } + @gplx.Internal protected static String Compile(String raw) { + if (raw == ImpossiblePath) return ImpossiblePath; + + String_bldr sb = String_bldr_.new_(); + sb.Add(RegxBldr.Tkn_LineBegin); // Char_LineBegin for exact match (else "LIKE a" would match "abc") + int rawLen = String_.Len(raw); + for (int i = 0; i < rawLen; i++) { + char c = String_.CharAt(raw, i); + if (c == '\\') + sb.Add("\\\\"); + else if (c == '*') + sb.Add(".").Add(RegxBldr.Tkn_Wild_0Plus); + else if (c == '|') + sb.Add(RegxBldr.Tkn_LineEnd).Add("|").Add(RegxBldr.Tkn_LineBegin); // each term must be bracketed by lineBgn/lineEnd; ex: A|B -> ^A$|^B$ + else + sb.Add(c); + } + sb.Add(RegxBldr.Tkn_LineEnd); + return sb.XtoStr(); + } + public static final String InvalidCharacters = "|*?\"<>"; // : / \ are omitted b/c they will cause full paths to fail + public static final String ValidCharacters = RegxBldr.Excludes(InvalidCharacters); +} diff --git a/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch_tst.java b/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch_tst.java new file mode 100644 index 000000000..b684eea71 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxPatn_cls_ioMatch_tst.java @@ -0,0 +1,58 @@ +/* +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.texts; import gplx.*; +import org.junit.*; +public class RegxPatn_cls_ioMatch_tst { + @Test public void SimpleMatches() { + tst_Matches("file.cs", "file.cs", true); // basic + tst_Matches("file.cs", "file.cs.exe", false); // fail: must match name precisely + tst_Matches("file.cs", "tst_file.cs", false); // fail: must match name precisely + } + @Test public void Wildcard() { + tst_Matches("*.cs", "file.cs", true); // pass: before + tst_Matches("file*", "file_valid.cs", true); // pass: after + tst_Matches("*.exe", "file.cs", false); // fail: before + tst_Matches("file*", "invalid_file.cs", false); // fail: after + } + @Test public void DoubleWildcard() { + tst_Matches("*cs*", "file.cs", true); // pass: after + tst_Matches("*cs*", "csFile.exe", true); // pass: before + tst_Matches("*cs*", "file.cs.exe", true); // pass: middle + tst_Matches("*cs*", "file.exe", false); // fail + } + @Test public void Compound() { + tst_Matches("*.cs|*.exe", "file.cs", true); // pass: match first + tst_Matches("*.cs|*.exe", "file.exe", true); // pass: match second + tst_Matches("*.cs|*.exe", "file.dll", false); // fail: match neither + tst_Matches("*.cs|*.exe", "file.cs.exe.dll", false); // fail: match neither (though both are embedded) + } + @Test public void Backslash() { + tst_Matches("*\\bin\\*", "C:\\project\\bin\\", true); // pass: dir + tst_Matches("*\\bin\\*", "C:\\project\\bin\\file.dll", true); // pass: fil + tst_Matches("*\\bin\\*", "C:\\project\\binFiles\\", false); // fail + } + @Test public void MixedCase() { + tst_Matches("file.cs", "file.cs", true); // pass: same case + tst_Matches("file.cs", "File.cS", true); // pass: diff case + } + void tst_Matches(String regx, String raw, boolean expd) { + RegxPatn_cls_ioMatch pattern = RegxPatn_cls_ioMatch_.parse_(regx, false); + boolean actl = pattern.Matches(raw); + Tfds.Eq(expd, actl); + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxPatn_cls_like.java b/100_core/src_150_text/gplx/texts/RegxPatn_cls_like.java new file mode 100644 index 000000000..ee6580630 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxPatn_cls_like.java @@ -0,0 +1,31 @@ +/* +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.texts; import gplx.*; +public class RegxPatn_cls_like { + public char Escape() {return escape;} char escape; public static final char EscapeDefault = '|'; + public String Raw() {return raw;} private String raw; + public boolean Matches(String text) {return RegxAdp_.Match(text, compiled);} + @Override public String toString() {return String_.Format("LIKE {0} ESCAPE {1} -> {2}", raw, escape, compiled);} + + String compiled; + @gplx.Internal protected RegxPatn_cls_like(String raw, String compiled, char escape) { + this.raw = raw; + this.compiled = compiled; + this.escape = escape; + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxPatn_cls_like_.java b/100_core/src_150_text/gplx/texts/RegxPatn_cls_like_.java new file mode 100644 index 000000000..83c3edce7 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxPatn_cls_like_.java @@ -0,0 +1,62 @@ +/* +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.texts; import gplx.*; +public class RegxPatn_cls_like_ { + public static RegxPatn_cls_like parse_(String regxRaw, char escape) { + String regx = Compile(regxRaw, escape); + return new RegxPatn_cls_like(regxRaw, regx, escape); + } + static String Compile(String raw, char escape) { + char Wildcard = '%', AnyChar = '_'; + boolean insideCharSet = false; + String_bldr sb = String_bldr_.new_(); + sb.Add(RegxBldr.Tkn_LineBegin); + int rawLen = String_.Len(raw); + for (int i = 0; i < rawLen; i++) { + char c = String_.CharAt(raw, i); + if (c == escape) { // escape: ignore cur, append next + i++; + if (i < rawLen) sb.Add(String_.CharAt(raw, i)); + else throw Err_.new_("escape cannot be last char").Add("raw", raw).Add("escape", escape).Add("i", i); + } + else if (c == Wildcard) { // % -> .* + sb.Add(RegxBldr.Tkn_AnyChar).Add(RegxBldr.Tkn_Wild_0Plus); + } + else if (c == AnyChar) // _ -> . + sb.Add(RegxBldr.Tkn_AnyChar); + else if (c == RegxBldr.Tkn_CharSetBegin) { // toggle insideCharSet for ^ + insideCharSet = true; + sb.Add(c); + } + else if (c == RegxBldr.Tkn_CharSetEnd) { // toggle insideCharSet for ^ + insideCharSet = false; + sb.Add(c); + } + else if (c == RegxBldr.Tkn_Not && insideCharSet) { // ^ is used for Not in CharSet, but also used for LineStart; do not escape if insideCharSet + insideCharSet = false; + sb.Add(c); + } + else if (RegxBldr.RegxChar_chk(c)) + sb.Add(RegxBldr.Tkn_Escape).Add(c); + else // regular text + sb.Add(c); + } + sb.Add(RegxBldr.Tkn_LineEnd); + return sb.XtoStr(); + } +} diff --git a/100_core/src_150_text/gplx/texts/RegxPatn_cls_like_tst.java b/100_core/src_150_text/gplx/texts/RegxPatn_cls_like_tst.java new file mode 100644 index 000000000..566b0093c --- /dev/null +++ b/100_core/src_150_text/gplx/texts/RegxPatn_cls_like_tst.java @@ -0,0 +1,86 @@ +/* +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.texts; import gplx.*; +import org.junit.*; +public class RegxPatn_cls_like_tst { + @Test public void Basic() { + tst_Match("abcd", "abcd", true); // basic; pass + tst_Match("abcd", "zbcd", false); // basic; fail + tst_Match("abcd", "abc", false); // no wildcard; must be exact match + tst_Match("a cd", "a cd", true); // check space works + } + @Test public void Wildcard() { + tst_Match("abcd", "a%", true); // bgn; pass + tst_Match("abcd", "b%", false); // bgn; fail + tst_Match("abcd", "%d", true); // end; pass + tst_Match("abcd", "%c", false); // end; fail + tst_Match("abcd", "%b%", true); // flank; pass + tst_Match("abcd", "%e%", false); // flank; fail + tst_Match("abcd", "%a%", true); // flank; bgn; pass + tst_Match("abcd", "%d%", true); // flank; end; pass + } + @Test public void Any() { + tst_Match("abcd", "a_cd", true); // basic; pass + tst_Match("abcd", "z_cd", false); // basic; fail + tst_Match("abcd", "a_c", false); // fail; check no wildcard + } + @Test public void CharSet() { + tst_Match("abcd", "a[b]cd", true); // pass + tst_Match("abcd", "a[x]cd", false); // fail + tst_Match("abcd", "a[bcde]cd", true); // multiple; pass + tst_Match("abcd", "a[xyz]cd", false); // multiple; fail + tst_Match("abcd", "a[^z]cd", true); // not; pass + tst_Match("abcd", "a[^b]cd", false); // not; fail + } + @Test public void Escape() { + tst_Match("a%b", "a|%b", true); // escape wildcard; pass + tst_Match("a%bc", "a|%b", false); // escape wildcard; fail + tst_Match("a|b", "a|b", false); // escape char; fail + tst_Match("a|b", "a||b", true); // escape char; pass + } + @Test public void Escape_diffChar() { + tst_Match("a%b", "a~%b", '~', true); // escape wildcard; pass + tst_Match("a%bc", "a~%b", '~', false); // escape wildcard; fail + tst_Match("a|b", "a|b", '~', true); // no escape needed + tst_Match("a~b", "a~b", '~', false); // escape char; fail + tst_Match("a~b", "a~~b", '~', true); // escape char; pass + } + @Test public void Chars() { // Escape RegxBldr; ex: LIKE 'a{' -> a\{ + tst_EscapeRegxChar(RegxBldr.Tkn_Escape); // \ + tst_EscapeRegxChar(RegxBldr.Tkn_GroupBegin); // [ + tst_EscapeRegxChar(RegxBldr.Tkn_GroupEnd); // ] + tst_EscapeRegxChar(RegxBldr.Tkn_LineBegin); // ^ + tst_EscapeRegxChar(RegxBldr.Tkn_LineEnd); // $ + tst_EscapeRegxChar(RegxBldr.Tkn_RepBegin); // { + tst_EscapeRegxChar(RegxBldr.Tkn_RepEnd); // } + tst_EscapeRegxChar(RegxBldr.Tkn_Wild_0or1); // ? + tst_EscapeRegxChar(RegxBldr.Tkn_Wild_0Plus); // * + tst_EscapeRegxChar(RegxBldr.Tkn_Wild_1Plus); // + + } + void tst_Match(String raw, String regx, boolean expd) {tst_Match(raw, regx, RegxPatn_cls_like.EscapeDefault, expd);} + void tst_Match(String raw, String regx, char escape, boolean expd) { + RegxPatn_cls_like like = RegxPatn_cls_like_.parse_(regx, escape); + boolean actl = like.Matches(raw); + Tfds.Eq(expd, actl, "raw={0} regx={1} expd={2}", raw, regx, expd); + } + void tst_EscapeRegxChar(char regexChar) { + RegxPatn_cls_like like = RegxPatn_cls_like_.parse_(Object_.XtoStr_OrEmpty(regexChar), '|'); + Tfds.Eq(true, like.Matches(Object_.XtoStr_OrEmpty(regexChar))); + Tfds.Eq(false, like.Matches("a")); // catches errors for improper escaping of wildcard + } +} diff --git a/100_core/src_150_text/gplx/texts/StringTableBldr.java b/100_core/src_150_text/gplx/texts/StringTableBldr.java new file mode 100644 index 000000000..0e3847e16 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/StringTableBldr.java @@ -0,0 +1,56 @@ +/* +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.texts; import gplx.*; +public class StringTableBldr { + public void ClearRows() {rows.Clear();} + public StringTableCol Col_(int i) {return FetchAtOrNew(i);} + public StringTableBldr DefaultHalign_(StringTableColAlign v) {defaultHalign = v; return this;} StringTableColAlign defaultHalign = StringTableColAlign.Left; + public StringTableBldr Add(String... row) { + rows.Add(row); + for (int i = 0; i < row.length; i++) { + StringTableCol col = FetchAtOrNew(i); + col.AdjustFor(row[i]); + } + return this; + } + public StringTableCol FetchAtOrNew(int i) { + if (i < cols.Count()) return StringTableCol.as_(cols.FetchAt(i)); + StringTableCol col = StringTableCol.new_(); + col.Halign_(defaultHalign); + cols.Add(i, col); + return col; + } + public String XtoStr() { + sb.Clear(); + for (int rowI = 0; rowI < rows.Count(); rowI++) { + String[] row = (String[])rows.FetchAt(rowI); + for (int colI = 0; colI < row.length; colI++) { + if (colI != 0) sb.Add(" "); + StringTableCol col = StringTableCol.as_(cols.FetchAt(colI)); if (col == null) throw Err_.missing_idx_(colI, cols.Count()); + sb.Add(col.PadCell(row[colI])); + } + sb.Add(String_.CrLf); + } + return sb.XtoStrAndClear(); + } + + public static StringTableBldr new_() {return new StringTableBldr();} StringTableBldr() {} + OrderedHash cols = OrderedHash_.new_(); + ListAdp rows = ListAdp_.new_(); + String_bldr sb = String_bldr_.new_(); +} diff --git a/100_core/src_150_text/gplx/texts/StringTableBldr_tst.java b/100_core/src_150_text/gplx/texts/StringTableBldr_tst.java new file mode 100644 index 000000000..11ae3414e --- /dev/null +++ b/100_core/src_150_text/gplx/texts/StringTableBldr_tst.java @@ -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 . +*/ +package gplx.texts; import gplx.*; +import org.junit.*; +public class StringTableBldr_tst { + @Before public void setup() { + bldr = StringTableBldr.new_(); + } StringTableBldr bldr; + @Test public void TwoCols() { + bldr.Add("a", "aa") + .Add("bb", "b"); + tst_XtoStr + ( "a aa" + , "bb b " + , "" + ); + } + @Test public void RightAlign() { + bldr.Add("a", "aa") + .Add("bb", "b"); + bldr.FetchAtOrNew(0).Halign_(StringTableColAlign.Right); + bldr.FetchAtOrNew(1).Halign_(StringTableColAlign.Right); + tst_XtoStr + ( " a aa" + , "bb b" + , "" + ); + } + @Test public void CenterAlign() { + bldr.Add("aaaa", "a") + .Add("b", "bbbb"); + bldr.FetchAtOrNew(0).Halign_(StringTableColAlign.Mid); + bldr.FetchAtOrNew(1).Halign_(StringTableColAlign.Mid); + tst_XtoStr + ( "aaaa a " + , " b bbbb" + , "" + ); + } + void tst_XtoStr(String... expdLines) { + String expd = String_.ConcatWith_any(String_.CrLf, (Object[])expdLines); + Tfds.Eq(expd, bldr.XtoStr()); + } +} diff --git a/100_core/src_150_text/gplx/texts/StringTableCol.java b/100_core/src_150_text/gplx/texts/StringTableCol.java new file mode 100644 index 000000000..a273649fa --- /dev/null +++ b/100_core/src_150_text/gplx/texts/StringTableCol.java @@ -0,0 +1,39 @@ +/* +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.texts; import gplx.*; +public class StringTableCol { + public StringTableColAlign Halign() {return halign;} public StringTableCol Halign_(StringTableColAlign val) {halign = val; return this;} StringTableColAlign halign = StringTableColAlign.Left; + public int LengthMax() {return lengthMax;} int lengthMax = Int_.MinValue; + public int LengthMin() {return lengthMin;} int lengthMin = Int_.MaxValue; + public void AdjustFor(String s) { + int length = String_.Len(s); + if (length > lengthMax) lengthMax = length; + if (length < lengthMin) lengthMin = length; + } + public String PadCell(String cell) { + int diff = lengthMax - String_.Len(cell); + int val = halign.Val(); + if (val == StringTableColAlign.Left.Val()) return cell + String_.Repeat(" ", diff); + else if (val == StringTableColAlign.Right.Val()) return String_.Repeat(" ", diff) + cell; + else if (val == StringTableColAlign.Mid.Val()) return String_.Concat(String_.Repeat(" ", diff / 2), cell, String_.Repeat(" ", (diff / 2) + (diff % 2))); + else throw Err_.unhandled(halign.Val()); + } + public static StringTableCol new_() {return new StringTableCol();} StringTableCol() {} + public static StringTableCol as_(Object obj) {return obj instanceof StringTableCol ? (StringTableCol)obj : null;} + public static StringTableCol cast_(Object obj) {try {return (StringTableCol)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, StringTableCol.class, obj);}} +} diff --git a/100_core/src_150_text/gplx/texts/StringTableColAlign.java b/100_core/src_150_text/gplx/texts/StringTableColAlign.java new file mode 100644 index 000000000..24e919606 --- /dev/null +++ b/100_core/src_150_text/gplx/texts/StringTableColAlign.java @@ -0,0 +1,29 @@ +/* +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.texts; import gplx.*; +public class StringTableColAlign { + public int Val() {return val;} int val = 0; + public static StringTableColAlign new_(int v) { + StringTableColAlign rv = new StringTableColAlign(); + rv.val = v; + return rv; + } StringTableColAlign() {} + public static final StringTableColAlign Left = new_(0); + public static final StringTableColAlign Mid = new_(1); + public static final StringTableColAlign Right = new_(2); +} diff --git a/100_core/src_160_hash/gplx/security/HashAlgo.java b/100_core/src_160_hash/gplx/security/HashAlgo.java new file mode 100644 index 000000000..1fad940f5 --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo.java @@ -0,0 +1,24 @@ +/* +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.security; import gplx.*; +import gplx.ios.*; /*IoStream*/ +public interface HashAlgo { + String Key(); + String CalcHash(ConsoleDlg dialog, IoStream stream); + byte[] Calc_hash_bry(byte[] v); +} diff --git a/100_core/src_160_hash/gplx/security/HashAlgo_.java b/100_core/src_160_hash/gplx/security/HashAlgo_.java new file mode 100644 index 000000000..a136fbb47 --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo_.java @@ -0,0 +1,93 @@ +/* +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.security; import gplx.*; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import gplx.ios.*; /*IoStream*/import gplx.texts.*; /*Base32Converter*/ +public class HashAlgo_ { + public static final HashAlgo Null = new HashAlgo_null(); + public static final HashAlgo Sha1 = HashAlgo_sha1.new_(); + public static final HashAlgo Md5 = HashAlgo_md5.new_(); + public static final HashAlgo Tth192 = HashAlgo_tth192.new_(); + public static HashAlgo as_(Object obj) {return obj instanceof HashAlgo ? (HashAlgo)obj : null;} + public static HashAlgo cast_(Object obj) {if (obj == null) return null; HashAlgo rv = as_(obj); if (rv == null) throw Err_.type_mismatch_(HashAlgo.class, obj); return rv;} + public static HashAlgo fetch_(String key) { + if (key == HashAlgo_md5.KEY) return Md5; + else if (key == HashAlgo_sha1.KEY) return Sha1; + else if (key == HashAlgo_tth192.KEY) return Tth192; + else throw Err_.unhandled(key); + } + public static HashAlgo store_orSelf_(SrlMgr mgr, String key, HashAlgo or) { + String algoType = mgr.SrlStrOr(key, or.Key()); + return mgr.Type_rdr() ? HashAlgo_.fetch_(algoType): or; + } +} +class HashAlgo_null implements HashAlgo { + public String Key() {return "HashAlgo_null";} + public byte[] Calc_hash_bry(byte[] v) {return Bry_.new_ascii_(CalcHash(ConsoleDlg_.Null, gplx.ios.IoStream_.ary_(v)));} + public String CalcHash(ConsoleDlg dialog, IoStream stream) {return "NullAlgoHash";} +} +class HashAlgo_md5 implements HashAlgo { + public String Key() {return KEY;} public static final String KEY = "md5"; + public byte[] Calc_hash_bry(byte[] v) {return Bry_.new_ascii_(CalcHash(ConsoleDlg_.Null, gplx.ios.IoStream_.ary_(v)));} + public String CalcHash(ConsoleDlg dialog, IoStream stream) {return HashAlgoUtl.CalcHashAsString(dialog, stream, "MD5");} + public static HashAlgo_md5 new_() {return new HashAlgo_md5();} HashAlgo_md5() {} +} +class HashAlgo_sha1 implements HashAlgo { + public String Key() {return KEY;} public static final String KEY = "sha1"; + public byte[] Calc_hash_bry(byte[] v) {return Bry_.new_ascii_(CalcHash(ConsoleDlg_.Null, gplx.ios.IoStream_.ary_(v)));} + public String CalcHash(ConsoleDlg dialog, IoStream stream) {return HashAlgoUtl.CalcHashAsString(dialog, stream, "SHA1");} + public static HashAlgo_sha1 new_() {return new HashAlgo_sha1();} HashAlgo_sha1() {} +} +class HashAlgoUtl { + public static String CalcHashAsString(ConsoleDlg dialog, IoStream stream, String key) { + MessageDigest md = null; + try {md = MessageDigest.getInstance(key);} + catch (NoSuchAlgorithmException e) {throw Err_arg.notFound_key_("key", key);} + byte[] buffer = new byte[8192]; + int read = 0; + long pos = 0, len = stream.Len(); // pos and len must be long, else will not hash files > 2 GB + while (true) { + read = stream.Read(buffer, 0, 8192); + if (pos >= len) break; + md.update(buffer, 0, read); + pos += read; + } + byte[] md5sum = md.digest(); + BigInteger bigInt = new BigInteger(1, md5sum); + String rv = bigInt.toString(16); + int rvLen = rv.length(); + while (rvLen < 32) { + rv = "0" + rv; + rvLen++; + } + return rv; + } + public static String XtoStrBase16(byte[] ary) { + BigInteger bigInt = new BigInteger(1, ary); + String rv = bigInt.toString(16); + int rvLen = rv.length(); + while (rvLen < 32) { + rv = "0" + rv; + rvLen++; + } + return rv; + } + public static String XtoStrBase32(byte[] ary) {return Base32Converter.Encode(ary);} +} diff --git a/100_core/src_160_hash/gplx/security/HashAlgo_md5_tst.java b/100_core/src_160_hash/gplx/security/HashAlgo_md5_tst.java new file mode 100644 index 000000000..b66184a1b --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo_md5_tst.java @@ -0,0 +1,81 @@ +/* +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.security; import gplx.*; +import org.junit.*; +import gplx.ios.*; /*IoStream*/ +public class HashAlgo_md5_tst { + @Test public void Empty() { + tst_CalcBase16FromString("", "d41d8cd98f00b204e9800998ecf8427e"); + } + @Test public void A() { + tst_CalcBase16FromString("a", "0cc175b9c0f1b6a831c399e269772661"); + } + @Test public void Abc() { + tst_CalcBase16FromString("abc", "900150983cd24fb0d6963f7d28e17f72"); + } + @Test public void A_Za_z0_9() { + tst_CalcBase16FromString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "d174ab98d277d9f5a5611c2c9f419d9f"); + } + //@Test + public void A_1Million() { + tst_CalcBase16FromString(String_.Repeat("a", 1000000), "7707d6ae4e027c70eea2a935c2296f21"); + } + void tst_CalcBase16FromString(String raw, String expd) { + IoStream stream = IoStream_.mem_txt_(Io_url_.Null, raw); + String actl = HashAlgo_.Md5.CalcHash(ConsoleDlg_.Null, stream); + Tfds.Eq(expd, actl); + } + /* + https://www.cosic.esat.kuleuven.be/nessie/testvectors/hash/md5/Md5-128.unverified.test-vectors + Set 1, vector# 0: + message="" (empty String) + hash=D41D8CD98F00B204E9800998ECF8427E + + Set 1, vector# 1: + message="a" + hash=0CC175B9C0F1B6A831C399E269772661 + + Set 1, vector# 2: + message="abc" + hash=900150983CD24FB0D6963F7D28E17F72 + + Set 1, vector# 3: + message="message digest" + hash=F96B697D7CB7938D525A2F31AAF161D0 + + Set 1, vector# 4: + message="abcdefghijklmnopqrstuvwxyz" + hash=C3FCD3D76192E4007DFB496CCA67E13B + + Set 1, vector# 5: + message="abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + hash=8215EF0796A20BCAAAE116D3876C664A + + Set 1, vector# 6: + message="A...Za...z0...9" + hash=D174AB98D277D9F5A5611C2C9F419D9F + + Set 1, vector# 7: + message=8 times "1234567890" + hash=57EDF4A22BE3C955AC49DA2E2107B67A + + Set 1, vector# 8: + message=1 million times "a" + hash=7707D6AE4E027C70EEA2A935C2296F21 + */ +} diff --git a/100_core/src_160_hash/gplx/security/HashAlgo_sha1_tst.java b/100_core/src_160_hash/gplx/security/HashAlgo_sha1_tst.java new file mode 100644 index 000000000..d1a37088a --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo_sha1_tst.java @@ -0,0 +1,81 @@ +/* +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.security; import gplx.*; +import org.junit.*; +import gplx.ios.*; /*IoStream*/ +public class HashAlgo_sha1_tst { + @Test public void Empty() { + tst_CalcBase16FromString("", "da39a3ee5e6b4b0d3255bfef95601890afd80709"); + } + @Test public void A() { + tst_CalcBase16FromString("a", "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); + } + @Test public void Abc() { + tst_CalcBase16FromString("abc", "a9993e364706816aba3e25717850c26c9cd0d89d"); + } + @Test public void A_Za_z0_9() { + tst_CalcBase16FromString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", "761c457bf73b14d27e9e9265c46f4b4dda11f940"); + } + //@Test + public void A_1Million() { + tst_CalcBase16FromString(String_.Repeat("a", 1000000), "34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + } + void tst_CalcBase16FromString(String raw, String expd) { + IoStream stream = IoStream_.mem_txt_(Io_url_.Null, raw); + String actl = HashAlgo_.Sha1.CalcHash(ConsoleDlg_.Null, stream); + Tfds.Eq(expd, actl); + } + /* + https://www.cosic.esat.kuleuven.be/nessie/testvectors/ + Set 1, vector# 0: + message="" (empty String) + hash=DA39A3EE5E6B4B0D3255BFEF95601890AFD80709 + + Set 1, vector# 1: + message="a" + hash=86F7E437FAA5A7FCE15D1DDCB9EAEAEA377667B8 + + Set 1, vector# 2: + message="abc" + hash=A9993E364706816ABA3E25717850C26C9CD0D89D + + Set 1, vector# 3: + message="message digest" + hash=C12252CEDA8BE8994D5FA0290A47231C1D16AAE3 + + Set 1, vector# 4: + message="abcdefghijklmnopqrstuvwxyz" + hash=32D10C7B8CF96570CA04CE37F2A19D84240D3A89 + + Set 1, vector# 5: + message="abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + hash=84983E441C3BD26EBAAE4AA1F95129E5E54670F1 + + Set 1, vector# 6: + message="A...Za...z0...9" + hash=761C457BF73B14D27E9E9265C46F4B4DDA11F940 + + Set 1, vector# 7: + message=8 times "1234567890" + hash=50ABF5706A150990A08B2C5EA40FA0E585554732 + + Set 1, vector# 8: + message=1 million times "a" + hash=34AA973CD4C4DAA4F61EEB2BDBAD27316534016F + */ +} diff --git a/100_core/src_160_hash/gplx/security/HashAlgo_tth192.java b/100_core/src_160_hash/gplx/security/HashAlgo_tth192.java new file mode 100644 index 000000000..a629391cb --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo_tth192.java @@ -0,0 +1,1005 @@ +/* +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.security; import gplx.*; +import gplx.ios.*; /*IoStream*/ +public class HashAlgo_tth192 implements HashAlgo { + public String Key() {return KEY;} public static final String KEY = "tth192"; + public int BlockSize() {return blockSize;} public void BlockSize_set(int v) {blockSize = v;} int blockSize = 1024; + public byte[] Calc_hash_bry(byte[] v) {return Bry_.new_ascii_(CalcHash(ConsoleDlg_.Null, gplx.ios.IoStream_.ary_(v)));} + public String CalcHash(ConsoleDlg dialog, IoStream stream) { + int leafCount = (int)(stream.Len() / blockSize); + HashDlgWtr dialogWtr = HashDlgWtr_.Current; + dialogWtr.Bgn(dialog, stream.Url(), CalcWorkUnits(stream.Len())); + if (stream.Len() % blockSize > 0) // if stream.length is not multiple of blockSize, add another leaf to hold leftover bytes + leafCount++; + else if (stream.Len() == 0) // if stream.length == 0, still allocate one leaf + leafCount = 1; + + hashMain = new byte[(leafCount / 2) + 1][]; // / 2 b/c HashAllBytes looks at pairs of slots; + 1 to avoid truncation + hashMainCount = 0; + HashAllBytes(dialogWtr, stream, leafCount); + byte[] rv = HashAllHashes(dialogWtr); + return HashAlgoUtl.XtoStrBase32(rv); + } + byte[] CalcHash_next(IoStream stream) { + if (blockA == null || blockA.length != blockSize) blockA = new byte[blockSize]; + if (blockB == null || blockB.length != blockSize) blockB = new byte[blockSize]; + + int dataSize = stream.Read(blockA, 0, blockSize); // if (dataSize < BlockSize) return (CalcHash_leaf(ShrinkArray(blockA, dataSize))); // TODO: reinstate? + dataSize = stream.Read(blockB, 0, blockSize); + if (dataSize < blockSize) blockB = ShrinkArray(blockB, dataSize); // shrink array to match data size (occurs at end of stream, when lastBytesCount < BlockSize) + return CalcHash_branch(CalcHash_leaf(blockA, 0), CalcHash_leaf(blockB, 1)); + } + void HashAllBytes(HashDlgWtr dialogWtr, IoStream stream, int leafCount) { + int total = leafCount / 2; + for (int i = 0; i < total; i++) { + if (dialogWtr.Canceled()) dialogWtr.Fail(stream); + hashMain[hashMainCount++] = CalcHash_next(stream); + dialogWtr.Do(2); + } + if (leafCount % 2 == 1) { // odd number of leaves => hash last leaf (since it was never in a pair) + byte[] block = new byte[blockSize]; + int dataSize = stream.Read(block, 0 , blockSize); + if (dataSize < blockSize) block = ShrinkArray(block,dataSize); // shrink array to match data size (occurs at end of stream, when lastBytesCount < BlockSize) + + hashMain[hashMainCount++] = CalcHash_leaf(block, 0); + dialogWtr.Do(1); + } + stream.Rls(); + } + byte[] HashAllHashes(HashDlgWtr dialogWtr) { + int currIdx = 0, lastIdx = hashMainCount - 1, reuseIdx = 0; + while (currIdx < lastIdx) { // calc till 1 is left; + while (currIdx < lastIdx) { // calc till 1 is left; EX: 8 slots; loop1 calcs 8; makes 4; loop0 restarts; loop1 calcs 4; makes 2; etc.. + byte[] hashA = hashMain[currIdx++]; + byte[] hashB = hashMain[currIdx++]; + hashMain[reuseIdx++] = CalcHash_branch(hashA, hashB); + dialogWtr.Do(2); + } + if (currIdx == lastIdx) // NOTE: currIdx == lastIdx only when odd number of leaves + hashMain[reuseIdx++] = hashMain[currIdx++]; +// for (int j = reuseIdx; j < lastIdx; j++) // PERF: free unused slots (may not be necessary) +// hashMain[j] = null; + lastIdx = reuseIdx - 1; + currIdx = 0; + reuseIdx = 0; + }; + dialogWtr.End(); + return (byte[])hashMain[lastIdx]; // return last hash as root hash + } + byte[] CalcHash_branch(byte[] blockA, byte[] blockB) { + // gen array: [0x01] + [blockA] + [blockB] + if (branchRv == null || branchRv.length != blockA.length + blockB.length + 1) + branchRv = new byte[blockA.length + blockB.length + 1]; + branchRv[0] = 0x01; // branch hash mark. + Array_.CopyTo(blockA, branchRv, 1); + Array_.CopyTo(blockB, branchRv, blockA.length + 1); + return CalcHash(branchRv); + } + byte[] CalcHash_leaf(byte[] raw, int i) { + // gen array: [0x00] + [raw] + byte[] rv = null; + if (i == 0) { + if (leaf0 == null || leaf0.length != raw.length + 1) { + leaf0 = new byte[raw.length + 1]; + } + rv = leaf0; + } + else { + if (leaf1 == null || leaf1.length != raw.length + 1) { + leaf1 = new byte[raw.length + 1]; + } + rv = leaf1; + } + + rv[0] = 0x00; // leaf hash mark. + Array_.CopyTo(raw, rv, 1); + return CalcHash(rv); + } + byte[] CalcHash(byte[] raw) { + algo.Initialize(); + return algo.ComputeHash(raw); + } + byte[] ShrinkArray(byte[] raw, int newLen) { + byte[] rv = new byte[newLen]; + for (int i = 0; i < newLen; i++) + rv[i] = raw[i]; + return rv; + } + public int CalcWorkUnits(long streamLength) { + int leafCount = (int)(streamLength / blockSize); + int phase1 = leafCount; + if (leafCount == 0) phase1 = 1; + else if (streamLength % blockSize != 0) phase1++; + int phase2 = phase1 <= 1 ? 0 : phase1 - 1; // see note + return phase1 + phase2; + } + Tiger192 algo = new Tiger192(); + byte[][] hashMain; int hashMainCount = 0; + byte[] blockA, blockB, branchRv, leaf0, leaf1; + public static HashAlgo_tth192 new_() {return new HashAlgo_tth192();} HashAlgo_tth192() {} +} +interface HashDlgWtr { + boolean Canceled(); + void Bgn(ConsoleDlg dialog, Io_url url, int total); + void Do(int increment); + void End(); + void Fail(IoStream stream); +} +class HashDlgWtr_ { + public static HashDlgWtr Current = HashDlgWtrDefault.new_(); +} +class HashDlgWtrDefault implements HashDlgWtr { + public int Total() {return total;} int total; + public int LastPercentage() {return lastPercentage;} int lastPercentage = 0; + public int Current() {return current;} int current = 0; + public boolean Canceled() {return dialog.CanceledChk();} + String p; + public void Bgn(ConsoleDlg dialog, Io_url url, int total) { + this.dialog = dialog; this.total = total; + p = url.Xto_api() + " - hash: "; + this.lastPercentage = 0; this.current = 0; + } + public void Do(int increment) { + current += increment; + int percentage = (current * 100) / total; + if (percentage <= lastPercentage) return; + dialog.WriteTempText(String_.LimitToFirst(p, dialog.CharsPerLineMax()) + Int_.XtoStr(percentage) + "%"); + lastPercentage = percentage; + } + public void End() {} + public void Fail(IoStream stream) { + // stream.Dispose(); + } + ConsoleDlg dialog; + public static HashDlgWtrDefault new_() {return new HashDlgWtrDefault();} +} +class Tiger192 extends BaseHash { + public void Initialize() { + this.resetContext(); + } + public byte[] ComputeHash(byte[] input) { + this.update(input, 0, input.length); + return this.digest(); + } + protected byte[] padBuffer() { + int n = (int) (count % BLOCK_SIZE); + int padding = (n < 56) ? (56 - n) : (120 - n); + byte[] pad = new byte[padding + 8]; + + pad[0] = 1; + long bits = count << 3; + + pad[padding++] = (byte) bits; + pad[padding++] = (byte) (bits >>> 8); + pad[padding++] = (byte) (bits >>> 16); + pad[padding++] = (byte) (bits >>> 24); + pad[padding++] = (byte) (bits >>> 32); + pad[padding++] = (byte) (bits >>> 40); + pad[padding++] = (byte) (bits >>> 48); + pad[padding ] = (byte) (bits >>> 56); + + return pad; + } + + protected byte[] getResult() { + return new byte[] { + (byte) a , (byte)(a >>> 8), (byte)(a >>> 16), + (byte)(a >>> 24), (byte)(a >>> 32), (byte)(a >>> 40), + (byte)(a >>> 48), (byte)(a >>> 56), + (byte) b , (byte)(b >>> 8), (byte)(b >>> 16), + (byte)(b >>> 24), (byte)(b >>> 32), (byte)(b >>> 40), + (byte)(b >>> 48), (byte)(b >>> 56), + (byte) c , (byte)(c >>> 8), (byte)(c >>> 16), + (byte)(c >>> 24), (byte)(c >>> 32), (byte)(c >>> 40), + (byte)(c >>> 48), (byte)(c >>> 56) + }; + } + + protected void resetContext() { + a = A; + b = B; + c = C; + } + protected void transform(byte[] in, int offset) { + long x0, x1, x2, x3, x4, x5, x6, x7; + + x0 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x1 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x2 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x3 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x4 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x5 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x6 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset++] & 0xFF) << 56); + x7 = ((long) in[offset++] & 0xFF) | ((long)(in[offset++] & 0xFF) << 8) + | ((long)(in[offset++] & 0xFF) << 16) | ((long)(in[offset++] & 0xFF) << 24) + | ((long)(in[offset++] & 0xFF) << 32) | ((long)(in[offset++] & 0xFF) << 40) + | ((long)(in[offset++] & 0xFF) << 48) | ((long)(in[offset ] & 0xFF) << 56); + + // save_abc ::= + long aa = a, bb = b, cc = c; + + // pass(aa, bb, cc, 5) ::= + cc ^= x0; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 5; + aa ^= x1; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 5; + bb ^= x2; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 5; + cc ^= x3; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 5; + aa ^= x4; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 5; + bb ^= x5; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 5; + cc ^= x6; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 5; + aa ^= x7; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 5; + + // key_schedule ::= + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5L; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >>> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >>> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFL; + + // pass(cc, aa, bb, 7) ::= + bb ^= x0; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 7; + cc ^= x1; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 7; + aa ^= x2; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 7; + bb ^= x3; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 7; + cc ^= x4; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 7; + aa ^= x5; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 7; + bb ^= x6; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 7; + cc ^= x7; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 7; + + // key_schedule ::= + x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5L; + x1 ^= x0; + x2 += x1; + x3 -= x2 ^ ((~x1) << 19); + x4 ^= x3; + x5 += x4; + x6 -= x5 ^ ((~x4) >>> 23); + x7 ^= x6; + x0 += x7; + x1 -= x0 ^ ((~x7) << 19); + x2 ^= x1; + x3 += x2; + x4 -= x3 ^ ((~x2) >>> 23); + x5 ^= x4; + x6 += x5; + x7 -= x6 ^ 0x0123456789ABCDEFL; + + // pass(bb,cc,aa,9) ::= + aa ^= x0; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 9; + bb ^= x1; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 9; + cc ^= x2; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 9; + aa ^= x3; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 9; + bb ^= x4; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 9; + cc ^= x5; + aa -= T1[(int) cc & 0xff] ^ T2[(int)(cc >> 16) & 0xff] + ^ T3[(int)(cc >> 32) & 0xff] ^ T4[(int)(cc >> 48) & 0xff]; + bb += T4[(int)(cc >> 8) & 0xff] ^ T3[(int)(cc >> 24) & 0xff] + ^ T2[(int)(cc >> 40) & 0xff] ^ T1[(int)(cc >> 56) & 0xff]; + bb *= 9; + aa ^= x6; + bb -= T1[(int) aa & 0xff] ^ T2[(int)(aa >> 16) & 0xff] + ^ T3[(int)(aa >> 32) & 0xff] ^ T4[(int)(aa >> 48) & 0xff]; + cc += T4[(int)(aa >> 8) & 0xff] ^ T3[(int)(aa >> 24) & 0xff] + ^ T2[(int)(aa >> 40) & 0xff] ^ T1[(int)(aa >> 56) & 0xff]; + cc *= 9; + bb ^= x7; + cc -= T1[(int) bb & 0xff] ^ T2[(int)(bb >> 16) & 0xff] + ^ T3[(int)(bb >> 32) & 0xff] ^ T4[(int)(bb >> 48) & 0xff]; + aa += T4[(int)(bb >> 8) & 0xff] ^ T3[(int)(bb >> 24) & 0xff] + ^ T2[(int)(bb >> 40) & 0xff] ^ T1[(int)(bb >> 56) & 0xff]; + aa *= 9; + + // feedforward ::= + a ^= aa; + b = bb - b; + c += cc; + } + protected static final int HASH_SIZE = 24; + + protected static final int BLOCK_SIZE = 64; + + /** Result when no data has been input. */ + + private static final long A = 0x0123456789ABCDEFL; + private static final long B = 0xFEDCBA9876543210L; + private static final long C = 0xF096A5B4C3B2E187L; + + /** S-Box T1. */ + private static final long[] T1 = { + 0x02AAB17CF7E90C5EL, 0xAC424B03E243A8ECL, 0x72CD5BE30DD5FCD3L, 0x6D019B93F6F97F3AL, + 0xCD9978FFD21F9193L, 0x7573A1C9708029E2L, 0xB164326B922A83C3L, 0x46883EEE04915870L, + 0xEAACE3057103ECE6L, 0xC54169B808A3535CL, 0x4CE754918DDEC47CL, 0x0AA2F4DFDC0DF40CL, + 0x10B76F18A74DBEFAL, 0xC6CCB6235AD1AB6AL, 0x13726121572FE2FFL, 0x1A488C6F199D921EL, + 0x4BC9F9F4DA0007CAL, 0x26F5E6F6E85241C7L, 0x859079DBEA5947B6L, 0x4F1885C5C99E8C92L, + 0xD78E761EA96F864BL, 0x8E36428C52B5C17DL, 0x69CF6827373063C1L, 0xB607C93D9BB4C56EL, + 0x7D820E760E76B5EAL, 0x645C9CC6F07FDC42L, 0xBF38A078243342E0L, 0x5F6B343C9D2E7D04L, + 0xF2C28AEB600B0EC6L, 0x6C0ED85F7254BCACL, 0x71592281A4DB4FE5L, 0x1967FA69CE0FED9FL, + 0xFD5293F8B96545DBL, 0xC879E9D7F2A7600BL, 0x860248920193194EL, 0xA4F9533B2D9CC0B3L, + 0x9053836C15957613L, 0xDB6DCF8AFC357BF1L, 0x18BEEA7A7A370F57L, 0x037117CA50B99066L, + 0x6AB30A9774424A35L, 0xF4E92F02E325249BL, 0x7739DB07061CCAE1L, 0xD8F3B49CECA42A05L, + 0xBD56BE3F51382F73L, 0x45FAED5843B0BB28L, 0x1C813D5C11BF1F83L, 0x8AF0E4B6D75FA169L, + 0x33EE18A487AD9999L, 0x3C26E8EAB1C94410L, 0xB510102BC0A822F9L, 0x141EEF310CE6123BL, + 0xFC65B90059DDB154L, 0xE0158640C5E0E607L, 0x884E079826C3A3CFL, 0x930D0D9523C535FDL, + 0x35638D754E9A2B00L, 0x4085FCCF40469DD5L, 0xC4B17AD28BE23A4CL, 0xCAB2F0FC6A3E6A2EL, + 0x2860971A6B943FCDL, 0x3DDE6EE212E30446L, 0x6222F32AE01765AEL, 0x5D550BB5478308FEL, + 0xA9EFA98DA0EDA22AL, 0xC351A71686C40DA7L, 0x1105586D9C867C84L, 0xDCFFEE85FDA22853L, + 0xCCFBD0262C5EEF76L, 0xBAF294CB8990D201L, 0xE69464F52AFAD975L, 0x94B013AFDF133E14L, + 0x06A7D1A32823C958L, 0x6F95FE5130F61119L, 0xD92AB34E462C06C0L, 0xED7BDE33887C71D2L, + 0x79746D6E6518393EL, 0x5BA419385D713329L, 0x7C1BA6B948A97564L, 0x31987C197BFDAC67L, + 0xDE6C23C44B053D02L, 0x581C49FED002D64DL, 0xDD474D6338261571L, 0xAA4546C3E473D062L, + 0x928FCE349455F860L, 0x48161BBACAAB94D9L, 0x63912430770E6F68L, 0x6EC8A5E602C6641CL, + 0x87282515337DDD2BL, 0x2CDA6B42034B701BL, 0xB03D37C181CB096DL, 0xE108438266C71C6FL, + 0x2B3180C7EB51B255L, 0xDF92B82F96C08BBCL, 0x5C68C8C0A632F3BAL, 0x5504CC861C3D0556L, + 0xABBFA4E55FB26B8FL, 0x41848B0AB3BACEB4L, 0xB334A273AA445D32L, 0xBCA696F0A85AD881L, + 0x24F6EC65B528D56CL, 0x0CE1512E90F4524AL, 0x4E9DD79D5506D35AL, 0x258905FAC6CE9779L, + 0x2019295B3E109B33L, 0xF8A9478B73A054CCL, 0x2924F2F934417EB0L, 0x3993357D536D1BC4L, + 0x38A81AC21DB6FF8BL, 0x47C4FBF17D6016BFL, 0x1E0FAADD7667E3F5L, 0x7ABCFF62938BEB96L, + 0xA78DAD948FC179C9L, 0x8F1F98B72911E50DL, 0x61E48EAE27121A91L, 0x4D62F7AD31859808L, + 0xECEBA345EF5CEAEBL, 0xF5CEB25EBC9684CEL, 0xF633E20CB7F76221L, 0xA32CDF06AB8293E4L, + 0x985A202CA5EE2CA4L, 0xCF0B8447CC8A8FB1L, 0x9F765244979859A3L, 0xA8D516B1A1240017L, + 0x0BD7BA3EBB5DC726L, 0xE54BCA55B86ADB39L, 0x1D7A3AFD6C478063L, 0x519EC608E7669EDDL, + 0x0E5715A2D149AA23L, 0x177D4571848FF194L, 0xEEB55F3241014C22L, 0x0F5E5CA13A6E2EC2L, + 0x8029927B75F5C361L, 0xAD139FABC3D6E436L, 0x0D5DF1A94CCF402FL, 0x3E8BD948BEA5DFC8L, + 0xA5A0D357BD3FF77EL, 0xA2D12E251F74F645L, 0x66FD9E525E81A082L, 0x2E0C90CE7F687A49L, + 0xC2E8BCBEBA973BC5L, 0x000001BCE509745FL, 0x423777BBE6DAB3D6L, 0xD1661C7EAEF06EB5L, + 0xA1781F354DAACFD8L, 0x2D11284A2B16AFFCL, 0xF1FC4F67FA891D1FL, 0x73ECC25DCB920ADAL, + 0xAE610C22C2A12651L, 0x96E0A810D356B78AL, 0x5A9A381F2FE7870FL, 0xD5AD62EDE94E5530L, + 0xD225E5E8368D1427L, 0x65977B70C7AF4631L, 0x99F889B2DE39D74FL, 0x233F30BF54E1D143L, + 0x9A9675D3D9A63C97L, 0x5470554FF334F9A8L, 0x166ACB744A4F5688L, 0x70C74CAAB2E4AEADL, + 0xF0D091646F294D12L, 0x57B82A89684031D1L, 0xEFD95A5A61BE0B6BL, 0x2FBD12E969F2F29AL, + 0x9BD37013FEFF9FE8L, 0x3F9B0404D6085A06L, 0x4940C1F3166CFE15L, 0x09542C4DCDF3DEFBL, + 0xB4C5218385CD5CE3L, 0xC935B7DC4462A641L, 0x3417F8A68ED3B63FL, 0xB80959295B215B40L, + 0xF99CDAEF3B8C8572L, 0x018C0614F8FCB95DL, 0x1B14ACCD1A3ACDF3L, 0x84D471F200BB732DL, + 0xC1A3110E95E8DA16L, 0x430A7220BF1A82B8L, 0xB77E090D39DF210EL, 0x5EF4BD9F3CD05E9DL, + 0x9D4FF6DA7E57A444L, 0xDA1D60E183D4A5F8L, 0xB287C38417998E47L, 0xFE3EDC121BB31886L, + 0xC7FE3CCC980CCBEFL, 0xE46FB590189BFD03L, 0x3732FD469A4C57DCL, 0x7EF700A07CF1AD65L, + 0x59C64468A31D8859L, 0x762FB0B4D45B61F6L, 0x155BAED099047718L, 0x68755E4C3D50BAA6L, + 0xE9214E7F22D8B4DFL, 0x2ADDBF532EAC95F4L, 0x32AE3909B4BD0109L, 0x834DF537B08E3450L, + 0xFA209DA84220728DL, 0x9E691D9B9EFE23F7L, 0x0446D288C4AE8D7FL, 0x7B4CC524E169785BL, + 0x21D87F0135CA1385L, 0xCEBB400F137B8AA5L, 0x272E2B66580796BEL, 0x3612264125C2B0DEL, + 0x057702BDAD1EFBB2L, 0xD4BABB8EACF84BE9L, 0x91583139641BC67BL, 0x8BDC2DE08036E024L, + 0x603C8156F49F68EDL, 0xF7D236F7DBEF5111L, 0x9727C4598AD21E80L, 0xA08A0896670A5FD7L, + 0xCB4A8F4309EBA9CBL, 0x81AF564B0F7036A1L, 0xC0B99AA778199ABDL, 0x959F1EC83FC8E952L, + 0x8C505077794A81B9L, 0x3ACAAF8F056338F0L, 0x07B43F50627A6778L, 0x4A44AB49F5ECCC77L, + 0x3BC3D6E4B679EE98L, 0x9CC0D4D1CF14108CL, 0x4406C00B206BC8A0L, 0x82A18854C8D72D89L, + 0x67E366B35C3C432CL, 0xB923DD61102B37F2L, 0x56AB2779D884271DL, 0xBE83E1B0FF1525AFL, + 0xFB7C65D4217E49A9L, 0x6BDBE0E76D48E7D4L, 0x08DF828745D9179EL, 0x22EA6A9ADD53BD34L, + 0xE36E141C5622200AL, 0x7F805D1B8CB750EEL, 0xAFE5C7A59F58E837L, 0xE27F996A4FB1C23CL, + 0xD3867DFB0775F0D0L, 0xD0E673DE6E88891AL, 0x123AEB9EAFB86C25L, 0x30F1D5D5C145B895L, + 0xBB434A2DEE7269E7L, 0x78CB67ECF931FA38L, 0xF33B0372323BBF9CL, 0x52D66336FB279C74L, + 0x505F33AC0AFB4EAAL, 0xE8A5CD99A2CCE187L, 0x534974801E2D30BBL, 0x8D2D5711D5876D90L, + 0x1F1A412891BC038EL, 0xD6E2E71D82E56648L, 0x74036C3A497732B7L, 0x89B67ED96361F5ABL, + 0xFFED95D8F1EA02A2L, 0xE72B3BD61464D43DL, 0xA6300F170BDC4820L, 0xEBC18760ED78A77AL + }; + + /** S-Box T2. */ + private static final long[] T2 = { + 0xE6A6BE5A05A12138L, 0xB5A122A5B4F87C98L, 0x563C6089140B6990L, 0x4C46CB2E391F5DD5L, + 0xD932ADDBC9B79434L, 0x08EA70E42015AFF5L, 0xD765A6673E478CF1L, 0xC4FB757EAB278D99L, + 0xDF11C6862D6E0692L, 0xDDEB84F10D7F3B16L, 0x6F2EF604A665EA04L, 0x4A8E0F0FF0E0DFB3L, + 0xA5EDEEF83DBCBA51L, 0xFC4F0A2A0EA4371EL, 0xE83E1DA85CB38429L, 0xDC8FF882BA1B1CE2L, + 0xCD45505E8353E80DL, 0x18D19A00D4DB0717L, 0x34A0CFEDA5F38101L, 0x0BE77E518887CAF2L, + 0x1E341438B3C45136L, 0xE05797F49089CCF9L, 0xFFD23F9DF2591D14L, 0x543DDA228595C5CDL, + 0x661F81FD99052A33L, 0x8736E641DB0F7B76L, 0x15227725418E5307L, 0xE25F7F46162EB2FAL, + 0x48A8B2126C13D9FEL, 0xAFDC541792E76EEAL, 0x03D912BFC6D1898FL, 0x31B1AAFA1B83F51BL, + 0xF1AC2796E42AB7D9L, 0x40A3A7D7FCD2EBACL, 0x1056136D0AFBBCC5L, 0x7889E1DD9A6D0C85L, + 0xD33525782A7974AAL, 0xA7E25D09078AC09BL, 0xBD4138B3EAC6EDD0L, 0x920ABFBE71EB9E70L, + 0xA2A5D0F54FC2625CL, 0xC054E36B0B1290A3L, 0xF6DD59FF62FE932BL, 0x3537354511A8AC7DL, + 0xCA845E9172FADCD4L, 0x84F82B60329D20DCL, 0x79C62CE1CD672F18L, 0x8B09A2ADD124642CL, + 0xD0C1E96A19D9E726L, 0x5A786A9B4BA9500CL, 0x0E020336634C43F3L, 0xC17B474AEB66D822L, + 0x6A731AE3EC9BAAC2L, 0x8226667AE0840258L, 0x67D4567691CAECA5L, 0x1D94155C4875ADB5L, + 0x6D00FD985B813FDFL, 0x51286EFCB774CD06L, 0x5E8834471FA744AFL, 0xF72CA0AEE761AE2EL, + 0xBE40E4CDAEE8E09AL, 0xE9970BBB5118F665L, 0x726E4BEB33DF1964L, 0x703B000729199762L, + 0x4631D816F5EF30A7L, 0xB880B5B51504A6BEL, 0x641793C37ED84B6CL, 0x7B21ED77F6E97D96L, + 0x776306312EF96B73L, 0xAE528948E86FF3F4L, 0x53DBD7F286A3F8F8L, 0x16CADCE74CFC1063L, + 0x005C19BDFA52C6DDL, 0x68868F5D64D46AD3L, 0x3A9D512CCF1E186AL, 0x367E62C2385660AEL, + 0xE359E7EA77DCB1D7L, 0x526C0773749ABE6EL, 0x735AE5F9D09F734BL, 0x493FC7CC8A558BA8L, + 0xB0B9C1533041AB45L, 0x321958BA470A59BDL, 0x852DB00B5F46C393L, 0x91209B2BD336B0E5L, + 0x6E604F7D659EF19FL, 0xB99A8AE2782CCB24L, 0xCCF52AB6C814C4C7L, 0x4727D9AFBE11727BL, + 0x7E950D0C0121B34DL, 0x756F435670AD471FL, 0xF5ADD442615A6849L, 0x4E87E09980B9957AL, + 0x2ACFA1DF50AEE355L, 0xD898263AFD2FD556L, 0xC8F4924DD80C8FD6L, 0xCF99CA3D754A173AL, + 0xFE477BACAF91BF3CL, 0xED5371F6D690C12DL, 0x831A5C285E687094L, 0xC5D3C90A3708A0A4L, + 0x0F7F903717D06580L, 0x19F9BB13B8FDF27FL, 0xB1BD6F1B4D502843L, 0x1C761BA38FFF4012L, + 0x0D1530C4E2E21F3BL, 0x8943CE69A7372C8AL, 0xE5184E11FEB5CE66L, 0x618BDB80BD736621L, + 0x7D29BAD68B574D0BL, 0x81BB613E25E6FE5BL, 0x071C9C10BC07913FL, 0xC7BEEB7909AC2D97L, + 0xC3E58D353BC5D757L, 0xEB017892F38F61E8L, 0xD4EFFB9C9B1CC21AL, 0x99727D26F494F7ABL, + 0xA3E063A2956B3E03L, 0x9D4A8B9A4AA09C30L, 0x3F6AB7D500090FB4L, 0x9CC0F2A057268AC0L, + 0x3DEE9D2DEDBF42D1L, 0x330F49C87960A972L, 0xC6B2720287421B41L, 0x0AC59EC07C00369CL, + 0xEF4EAC49CB353425L, 0xF450244EEF0129D8L, 0x8ACC46E5CAF4DEB6L, 0x2FFEAB63989263F7L, + 0x8F7CB9FE5D7A4578L, 0x5BD8F7644E634635L, 0x427A7315BF2DC900L, 0x17D0C4AA2125261CL, + 0x3992486C93518E50L, 0xB4CBFEE0A2D7D4C3L, 0x7C75D6202C5DDD8DL, 0xDBC295D8E35B6C61L, + 0x60B369D302032B19L, 0xCE42685FDCE44132L, 0x06F3DDB9DDF65610L, 0x8EA4D21DB5E148F0L, + 0x20B0FCE62FCD496FL, 0x2C1B912358B0EE31L, 0xB28317B818F5A308L, 0xA89C1E189CA6D2CFL, + 0x0C6B18576AAADBC8L, 0xB65DEAA91299FAE3L, 0xFB2B794B7F1027E7L, 0x04E4317F443B5BEBL, + 0x4B852D325939D0A6L, 0xD5AE6BEEFB207FFCL, 0x309682B281C7D374L, 0xBAE309A194C3B475L, + 0x8CC3F97B13B49F05L, 0x98A9422FF8293967L, 0x244B16B01076FF7CL, 0xF8BF571C663D67EEL, + 0x1F0D6758EEE30DA1L, 0xC9B611D97ADEB9B7L, 0xB7AFD5887B6C57A2L, 0x6290AE846B984FE1L, + 0x94DF4CDEACC1A5FDL, 0x058A5BD1C5483AFFL, 0x63166CC142BA3C37L, 0x8DB8526EB2F76F40L, + 0xE10880036F0D6D4EL, 0x9E0523C9971D311DL, 0x45EC2824CC7CD691L, 0x575B8359E62382C9L, + 0xFA9E400DC4889995L, 0xD1823ECB45721568L, 0xDAFD983B8206082FL, 0xAA7D29082386A8CBL, + 0x269FCD4403B87588L, 0x1B91F5F728BDD1E0L, 0xE4669F39040201F6L, 0x7A1D7C218CF04ADEL, + 0x65623C29D79CE5CEL, 0x2368449096C00BB1L, 0xAB9BF1879DA503BAL, 0xBC23ECB1A458058EL, + 0x9A58DF01BB401ECCL, 0xA070E868A85F143DL, 0x4FF188307DF2239EL, 0x14D565B41A641183L, + 0xEE13337452701602L, 0x950E3DCF3F285E09L, 0x59930254B9C80953L, 0x3BF299408930DA6DL, + 0xA955943F53691387L, 0xA15EDECAA9CB8784L, 0x29142127352BE9A0L, 0x76F0371FFF4E7AFBL, + 0x0239F450274F2228L, 0xBB073AF01D5E868BL, 0xBFC80571C10E96C1L, 0xD267088568222E23L, + 0x9671A3D48E80B5B0L, 0x55B5D38AE193BB81L, 0x693AE2D0A18B04B8L, 0x5C48B4ECADD5335FL, + 0xFD743B194916A1CAL, 0x2577018134BE98C4L, 0xE77987E83C54A4ADL, 0x28E11014DA33E1B9L, + 0x270CC59E226AA213L, 0x71495F756D1A5F60L, 0x9BE853FB60AFEF77L, 0xADC786A7F7443DBFL, + 0x0904456173B29A82L, 0x58BC7A66C232BD5EL, 0xF306558C673AC8B2L, 0x41F639C6B6C9772AL, + 0x216DEFE99FDA35DAL, 0x11640CC71C7BE615L, 0x93C43694565C5527L, 0xEA038E6246777839L, + 0xF9ABF3CE5A3E2469L, 0x741E768D0FD312D2L, 0x0144B883CED652C6L, 0xC20B5A5BA33F8552L, + 0x1AE69633C3435A9DL, 0x97A28CA4088CFDECL, 0x8824A43C1E96F420L, 0x37612FA66EEEA746L, + 0x6B4CB165F9CF0E5AL, 0x43AA1C06A0ABFB4AL, 0x7F4DC26FF162796BL, 0x6CBACC8E54ED9B0FL, + 0xA6B7FFEFD2BB253EL, 0x2E25BC95B0A29D4FL, 0x86D6A58BDEF1388CL, 0xDED74AC576B6F054L, + 0x8030BDBC2B45805DL, 0x3C81AF70E94D9289L, 0x3EFF6DDA9E3100DBL, 0xB38DC39FDFCC8847L, + 0x123885528D17B87EL, 0xF2DA0ED240B1B642L, 0x44CEFADCD54BF9A9L, 0x1312200E433C7EE6L, + 0x9FFCC84F3A78C748L, 0xF0CD1F72248576BBL, 0xEC6974053638CFE4L, 0x2BA7B67C0CEC4E4CL, + 0xAC2F4DF3E5CE32EDL, 0xCB33D14326EA4C11L, 0xA4E9044CC77E58BCL, 0x5F513293D934FCEFL, + 0x5DC9645506E55444L, 0x50DE418F317DE40AL, 0x388CB31A69DDE259L, 0x2DB4A83455820A86L, + 0x9010A91E84711AE9L, 0x4DF7F0B7B1498371L, 0xD62A2EABC0977179L, 0x22FAC097AA8D5C0EL + }; + + /** S-Box T3. */ + private static final long[] T3 = { + 0xF49FCC2FF1DAF39BL, 0x487FD5C66FF29281L, 0xE8A30667FCDCA83FL, 0x2C9B4BE3D2FCCE63L, + 0xDA3FF74B93FBBBC2L, 0x2FA165D2FE70BA66L, 0xA103E279970E93D4L, 0xBECDEC77B0E45E71L, + 0xCFB41E723985E497L, 0xB70AAA025EF75017L, 0xD42309F03840B8E0L, 0x8EFC1AD035898579L, + 0x96C6920BE2B2ABC5L, 0x66AF4163375A9172L, 0x2174ABDCCA7127FBL, 0xB33CCEA64A72FF41L, + 0xF04A4933083066A5L, 0x8D970ACDD7289AF5L, 0x8F96E8E031C8C25EL, 0xF3FEC02276875D47L, + 0xEC7BF310056190DDL, 0xF5ADB0AEBB0F1491L, 0x9B50F8850FD58892L, 0x4975488358B74DE8L, + 0xA3354FF691531C61L, 0x0702BBE481D2C6EEL, 0x89FB24057DEDED98L, 0xAC3075138596E902L, + 0x1D2D3580172772EDL, 0xEB738FC28E6BC30DL, 0x5854EF8F63044326L, 0x9E5C52325ADD3BBEL, + 0x90AA53CF325C4623L, 0xC1D24D51349DD067L, 0x2051CFEEA69EA624L, 0x13220F0A862E7E4FL, + 0xCE39399404E04864L, 0xD9C42CA47086FCB7L, 0x685AD2238A03E7CCL, 0x066484B2AB2FF1DBL, + 0xFE9D5D70EFBF79ECL, 0x5B13B9DD9C481854L, 0x15F0D475ED1509ADL, 0x0BEBCD060EC79851L, + 0xD58C6791183AB7F8L, 0xD1187C5052F3EEE4L, 0xC95D1192E54E82FFL, 0x86EEA14CB9AC6CA2L, + 0x3485BEB153677D5DL, 0xDD191D781F8C492AL, 0xF60866BAA784EBF9L, 0x518F643BA2D08C74L, + 0x8852E956E1087C22L, 0xA768CB8DC410AE8DL, 0x38047726BFEC8E1AL, 0xA67738B4CD3B45AAL, + 0xAD16691CEC0DDE19L, 0xC6D4319380462E07L, 0xC5A5876D0BA61938L, 0x16B9FA1FA58FD840L, + 0x188AB1173CA74F18L, 0xABDA2F98C99C021FL, 0x3E0580AB134AE816L, 0x5F3B05B773645ABBL, + 0x2501A2BE5575F2F6L, 0x1B2F74004E7E8BA9L, 0x1CD7580371E8D953L, 0x7F6ED89562764E30L, + 0xB15926FF596F003DL, 0x9F65293DA8C5D6B9L, 0x6ECEF04DD690F84CL, 0x4782275FFF33AF88L, + 0xE41433083F820801L, 0xFD0DFE409A1AF9B5L, 0x4325A3342CDB396BL, 0x8AE77E62B301B252L, + 0xC36F9E9F6655615AL, 0x85455A2D92D32C09L, 0xF2C7DEA949477485L, 0x63CFB4C133A39EBAL, + 0x83B040CC6EBC5462L, 0x3B9454C8FDB326B0L, 0x56F56A9E87FFD78CL, 0x2DC2940D99F42BC6L, + 0x98F7DF096B096E2DL, 0x19A6E01E3AD852BFL, 0x42A99CCBDBD4B40BL, 0xA59998AF45E9C559L, + 0x366295E807D93186L, 0x6B48181BFAA1F773L, 0x1FEC57E2157A0A1DL, 0x4667446AF6201AD5L, + 0xE615EBCACFB0F075L, 0xB8F31F4F68290778L, 0x22713ED6CE22D11EL, 0x3057C1A72EC3C93BL, + 0xCB46ACC37C3F1F2FL, 0xDBB893FD02AAF50EL, 0x331FD92E600B9FCFL, 0xA498F96148EA3AD6L, + 0xA8D8426E8B6A83EAL, 0xA089B274B7735CDCL, 0x87F6B3731E524A11L, 0x118808E5CBC96749L, + 0x9906E4C7B19BD394L, 0xAFED7F7E9B24A20CL, 0x6509EADEEB3644A7L, 0x6C1EF1D3E8EF0EDEL, + 0xB9C97D43E9798FB4L, 0xA2F2D784740C28A3L, 0x7B8496476197566FL, 0x7A5BE3E6B65F069DL, + 0xF96330ED78BE6F10L, 0xEEE60DE77A076A15L, 0x2B4BEE4AA08B9BD0L, 0x6A56A63EC7B8894EL, + 0x02121359BA34FEF4L, 0x4CBF99F8283703FCL, 0x398071350CAF30C8L, 0xD0A77A89F017687AL, + 0xF1C1A9EB9E423569L, 0x8C7976282DEE8199L, 0x5D1737A5DD1F7ABDL, 0x4F53433C09A9FA80L, + 0xFA8B0C53DF7CA1D9L, 0x3FD9DCBC886CCB77L, 0xC040917CA91B4720L, 0x7DD00142F9D1DCDFL, + 0x8476FC1D4F387B58L, 0x23F8E7C5F3316503L, 0x032A2244E7E37339L, 0x5C87A5D750F5A74BL, + 0x082B4CC43698992EL, 0xDF917BECB858F63CL, 0x3270B8FC5BF86DDAL, 0x10AE72BB29B5DD76L, + 0x576AC94E7700362BL, 0x1AD112DAC61EFB8FL, 0x691BC30EC5FAA427L, 0xFF246311CC327143L, + 0x3142368E30E53206L, 0x71380E31E02CA396L, 0x958D5C960AAD76F1L, 0xF8D6F430C16DA536L, + 0xC8FFD13F1BE7E1D2L, 0x7578AE66004DDBE1L, 0x05833F01067BE646L, 0xBB34B5AD3BFE586DL, + 0x095F34C9A12B97F0L, 0x247AB64525D60CA8L, 0xDCDBC6F3017477D1L, 0x4A2E14D4DECAD24DL, + 0xBDB5E6D9BE0A1EEBL, 0x2A7E70F7794301ABL, 0xDEF42D8A270540FDL, 0x01078EC0A34C22C1L, + 0xE5DE511AF4C16387L, 0x7EBB3A52BD9A330AL, 0x77697857AA7D6435L, 0x004E831603AE4C32L, + 0xE7A21020AD78E312L, 0x9D41A70C6AB420F2L, 0x28E06C18EA1141E6L, 0xD2B28CBD984F6B28L, + 0x26B75F6C446E9D83L, 0xBA47568C4D418D7FL, 0xD80BADBFE6183D8EL, 0x0E206D7F5F166044L, + 0xE258A43911CBCA3EL, 0x723A1746B21DC0BCL, 0xC7CAA854F5D7CDD3L, 0x7CAC32883D261D9CL, + 0x7690C26423BA942CL, 0x17E55524478042B8L, 0xE0BE477656A2389FL, 0x4D289B5E67AB2DA0L, + 0x44862B9C8FBBFD31L, 0xB47CC8049D141365L, 0x822C1B362B91C793L, 0x4EB14655FB13DFD8L, + 0x1ECBBA0714E2A97BL, 0x6143459D5CDE5F14L, 0x53A8FBF1D5F0AC89L, 0x97EA04D81C5E5B00L, + 0x622181A8D4FDB3F3L, 0xE9BCD341572A1208L, 0x1411258643CCE58AL, 0x9144C5FEA4C6E0A4L, + 0x0D33D06565CF620FL, 0x54A48D489F219CA1L, 0xC43E5EAC6D63C821L, 0xA9728B3A72770DAFL, + 0xD7934E7B20DF87EFL, 0xE35503B61A3E86E5L, 0xCAE321FBC819D504L, 0x129A50B3AC60BFA6L, + 0xCD5E68EA7E9FB6C3L, 0xB01C90199483B1C7L, 0x3DE93CD5C295376CL, 0xAED52EDF2AB9AD13L, + 0x2E60F512C0A07884L, 0xBC3D86A3E36210C9L, 0x35269D9B163951CEL, 0x0C7D6E2AD0CDB5FAL, + 0x59E86297D87F5733L, 0x298EF221898DB0E7L, 0x55000029D1A5AA7EL, 0x8BC08AE1B5061B45L, + 0xC2C31C2B6C92703AL, 0x94CC596BAF25EF42L, 0x0A1D73DB22540456L, 0x04B6A0F9D9C4179AL, + 0xEFFDAFA2AE3D3C60L, 0xF7C8075BB49496C4L, 0x9CC5C7141D1CD4E3L, 0x78BD1638218E5534L, + 0xB2F11568F850246AL, 0xEDFABCFA9502BC29L, 0x796CE5F2DA23051BL, 0xAAE128B0DC93537CL, + 0x3A493DA0EE4B29AEL, 0xB5DF6B2C416895D7L, 0xFCABBD25122D7F37L, 0x70810B58105DC4B1L, + 0xE10FDD37F7882A90L, 0x524DCAB5518A3F5CL, 0x3C9E85878451255BL, 0x4029828119BD34E2L, + 0x74A05B6F5D3CECCBL, 0xB610021542E13ECAL, 0x0FF979D12F59E2ACL, 0x6037DA27E4F9CC50L, + 0x5E92975A0DF1847DL, 0xD66DE190D3E623FEL, 0x5032D6B87B568048L, 0x9A36B7CE8235216EL, + 0x80272A7A24F64B4AL, 0x93EFED8B8C6916F7L, 0x37DDBFF44CCE1555L, 0x4B95DB5D4B99BD25L, + 0x92D3FDA169812FC0L, 0xFB1A4A9A90660BB6L, 0x730C196946A4B9B2L, 0x81E289AA7F49DA68L, + 0x64669A0F83B1A05FL, 0x27B3FF7D9644F48BL, 0xCC6B615C8DB675B3L, 0x674F20B9BCEBBE95L, + 0x6F31238275655982L, 0x5AE488713E45CF05L, 0xBF619F9954C21157L, 0xEABAC46040A8EAE9L, + 0x454C6FE9F2C0C1CDL, 0x419CF6496412691CL, 0xD3DC3BEF265B0F70L, 0x6D0E60F5C3578A9EL + }; + + /** S-Box T4. */ + private static final long[] T4 = { + 0x5B0E608526323C55L, 0x1A46C1A9FA1B59F5L, 0xA9E245A17C4C8FFAL, 0x65CA5159DB2955D7L, + 0x05DB0A76CE35AFC2L, 0x81EAC77EA9113D45L, 0x528EF88AB6AC0A0DL, 0xA09EA253597BE3FFL, + 0x430DDFB3AC48CD56L, 0xC4B3A67AF45CE46FL, 0x4ECECFD8FBE2D05EL, 0x3EF56F10B39935F0L, + 0x0B22D6829CD619C6L, 0x17FD460A74DF2069L, 0x6CF8CC8E8510ED40L, 0xD6C824BF3A6ECAA7L, + 0x61243D581A817049L, 0x048BACB6BBC163A2L, 0xD9A38AC27D44CC32L, 0x7FDDFF5BAAF410ABL, + 0xAD6D495AA804824BL, 0xE1A6A74F2D8C9F94L, 0xD4F7851235DEE8E3L, 0xFD4B7F886540D893L, + 0x247C20042AA4BFDAL, 0x096EA1C517D1327CL, 0xD56966B4361A6685L, 0x277DA5C31221057DL, + 0x94D59893A43ACFF7L, 0x64F0C51CCDC02281L, 0x3D33BCC4FF6189DBL, 0xE005CB184CE66AF1L, + 0xFF5CCD1D1DB99BEAL, 0xB0B854A7FE42980FL, 0x7BD46A6A718D4B9FL, 0xD10FA8CC22A5FD8CL, + 0xD31484952BE4BD31L, 0xC7FA975FCB243847L, 0x4886ED1E5846C407L, 0x28CDDB791EB70B04L, + 0xC2B00BE2F573417FL, 0x5C9590452180F877L, 0x7A6BDDFFF370EB00L, 0xCE509E38D6D9D6A4L, + 0xEBEB0F00647FA702L, 0x1DCC06CF76606F06L, 0xE4D9F28BA286FF0AL, 0xD85A305DC918C262L, + 0x475B1D8732225F54L, 0x2D4FB51668CCB5FEL, 0xA679B9D9D72BBA20L, 0x53841C0D912D43A5L, + 0x3B7EAA48BF12A4E8L, 0x781E0E47F22F1DDFL, 0xEFF20CE60AB50973L, 0x20D261D19DFFB742L, + 0x16A12B03062A2E39L, 0x1960EB2239650495L, 0x251C16FED50EB8B8L, 0x9AC0C330F826016EL, + 0xED152665953E7671L, 0x02D63194A6369570L, 0x5074F08394B1C987L, 0x70BA598C90B25CE1L, + 0x794A15810B9742F6L, 0x0D5925E9FCAF8C6CL, 0x3067716CD868744EL, 0x910AB077E8D7731BL, + 0x6A61BBDB5AC42F61L, 0x93513EFBF0851567L, 0xF494724B9E83E9D5L, 0xE887E1985C09648DL, + 0x34B1D3C675370CFDL, 0xDC35E433BC0D255DL, 0xD0AAB84234131BE0L, 0x08042A50B48B7EAFL, + 0x9997C4EE44A3AB35L, 0x829A7B49201799D0L, 0x263B8307B7C54441L, 0x752F95F4FD6A6CA6L, + 0x927217402C08C6E5L, 0x2A8AB754A795D9EEL, 0xA442F7552F72943DL, 0x2C31334E19781208L, + 0x4FA98D7CEAEE6291L, 0x55C3862F665DB309L, 0xBD0610175D53B1F3L, 0x46FE6CB840413F27L, + 0x3FE03792DF0CFA59L, 0xCFE700372EB85E8FL, 0xA7BE29E7ADBCE118L, 0xE544EE5CDE8431DDL, + 0x8A781B1B41F1873EL, 0xA5C94C78A0D2F0E7L, 0x39412E2877B60728L, 0xA1265EF3AFC9A62CL, + 0xBCC2770C6A2506C5L, 0x3AB66DD5DCE1CE12L, 0xE65499D04A675B37L, 0x7D8F523481BFD216L, + 0x0F6F64FCEC15F389L, 0x74EFBE618B5B13C8L, 0xACDC82B714273E1DL, 0xDD40BFE003199D17L, + 0x37E99257E7E061F8L, 0xFA52626904775AAAL, 0x8BBBF63A463D56F9L, 0xF0013F1543A26E64L, + 0xA8307E9F879EC898L, 0xCC4C27A4150177CCL, 0x1B432F2CCA1D3348L, 0xDE1D1F8F9F6FA013L, + 0x606602A047A7DDD6L, 0xD237AB64CC1CB2C7L, 0x9B938E7225FCD1D3L, 0xEC4E03708E0FF476L, + 0xFEB2FBDA3D03C12DL, 0xAE0BCED2EE43889AL, 0x22CB8923EBFB4F43L, 0x69360D013CF7396DL, + 0x855E3602D2D4E022L, 0x073805BAD01F784CL, 0x33E17A133852F546L, 0xDF4874058AC7B638L, + 0xBA92B29C678AA14AL, 0x0CE89FC76CFAADCDL, 0x5F9D4E0908339E34L, 0xF1AFE9291F5923B9L, + 0x6E3480F60F4A265FL, 0xEEBF3A2AB29B841CL, 0xE21938A88F91B4ADL, 0x57DFEFF845C6D3C3L, + 0x2F006B0BF62CAAF2L, 0x62F479EF6F75EE78L, 0x11A55AD41C8916A9L, 0xF229D29084FED453L, + 0x42F1C27B16B000E6L, 0x2B1F76749823C074L, 0x4B76ECA3C2745360L, 0x8C98F463B91691BDL, + 0x14BCC93CF1ADE66AL, 0x8885213E6D458397L, 0x8E177DF0274D4711L, 0xB49B73B5503F2951L, + 0x10168168C3F96B6BL, 0x0E3D963B63CAB0AEL, 0x8DFC4B5655A1DB14L, 0xF789F1356E14DE5CL, + 0x683E68AF4E51DAC1L, 0xC9A84F9D8D4B0FD9L, 0x3691E03F52A0F9D1L, 0x5ED86E46E1878E80L, + 0x3C711A0E99D07150L, 0x5A0865B20C4E9310L, 0x56FBFC1FE4F0682EL, 0xEA8D5DE3105EDF9BL, + 0x71ABFDB12379187AL, 0x2EB99DE1BEE77B9CL, 0x21ECC0EA33CF4523L, 0x59A4D7521805C7A1L, + 0x3896F5EB56AE7C72L, 0xAA638F3DB18F75DCL, 0x9F39358DABE9808EL, 0xB7DEFA91C00B72ACL, + 0x6B5541FD62492D92L, 0x6DC6DEE8F92E4D5BL, 0x353F57ABC4BEEA7EL, 0x735769D6DA5690CEL, + 0x0A234AA642391484L, 0xF6F9508028F80D9DL, 0xB8E319A27AB3F215L, 0x31AD9C1151341A4DL, + 0x773C22A57BEF5805L, 0x45C7561A07968633L, 0xF913DA9E249DBE36L, 0xDA652D9B78A64C68L, + 0x4C27A97F3BC334EFL, 0x76621220E66B17F4L, 0x967743899ACD7D0BL, 0xF3EE5BCAE0ED6782L, + 0x409F753600C879FCL, 0x06D09A39B5926DB6L, 0x6F83AEB0317AC588L, 0x01E6CA4A86381F21L, + 0x66FF3462D19F3025L, 0x72207C24DDFD3BFBL, 0x4AF6B6D3E2ECE2EBL, 0x9C994DBEC7EA08DEL, + 0x49ACE597B09A8BC4L, 0xB38C4766CF0797BAL, 0x131B9373C57C2A75L, 0xB1822CCE61931E58L, + 0x9D7555B909BA1C0CL, 0x127FAFDD937D11D2L, 0x29DA3BADC66D92E4L, 0xA2C1D57154C2ECBCL, + 0x58C5134D82F6FE24L, 0x1C3AE3515B62274FL, 0xE907C82E01CB8126L, 0xF8ED091913E37FCBL, + 0x3249D8F9C80046C9L, 0x80CF9BEDE388FB63L, 0x1881539A116CF19EL, 0x5103F3F76BD52457L, + 0x15B7E6F5AE47F7A8L, 0xDBD7C6DED47E9CCFL, 0x44E55C410228BB1AL, 0xB647D4255EDB4E99L, + 0x5D11882BB8AAFC30L, 0xF5098BBB29D3212AL, 0x8FB5EA14E90296B3L, 0x677B942157DD025AL, + 0xFB58E7C0A390ACB5L, 0x89D3674C83BD4A01L, 0x9E2DA4DF4BF3B93BL, 0xFCC41E328CAB4829L, + 0x03F38C96BA582C52L, 0xCAD1BDBD7FD85DB2L, 0xBBB442C16082AE83L, 0xB95FE86BA5DA9AB0L, + 0xB22E04673771A93FL, 0x845358C9493152D8L, 0xBE2A488697B4541EL, 0x95A2DC2DD38E6966L, + 0xC02C11AC923C852BL, 0x2388B1990DF2A87BL, 0x7C8008FA1B4F37BEL, 0x1F70D0C84D54E503L, + 0x5490ADEC7ECE57D4L, 0x002B3C27D9063A3AL, 0x7EAEA3848030A2BFL, 0xC602326DED2003C0L, + 0x83A7287D69A94086L, 0xC57A5FCB30F57A8AL, 0xB56844E479EBE779L, 0xA373B40F05DCBCE9L, + 0xD71A786E88570EE2L, 0x879CBACDBDE8F6A0L, 0x976AD1BCC164A32FL, 0xAB21E25E9666D78BL, + 0x901063AAE5E5C33CL, 0x9818B34448698D90L, 0xE36487AE3E1E8ABBL, 0xAFBDF931893BDCB4L, + 0x6345A0DC5FBBD519L, 0x8628FE269B9465CAL, 0x1E5D01603F9C51ECL, 0x4DE44006A15049B7L, + 0xBF6C70E5F776CBB1L, 0x411218F2EF552BEDL, 0xCB0C0708705A36A3L, 0xE74D14754F986044L, + 0xCD56D9430EA8280EL, 0xC12591D7535F5065L, 0xC83223F1720AEF96L, 0xC3A0396F7363A51FL + }; + protected static Boolean valid; // The cached self-test result. + + protected long a, b, c; // The context. + + public Tiger192() {super("tiger192", HASH_SIZE, BLOCK_SIZE);} + + /** + * Private copying constructor for cloning. + * + * @param that The instance being cloned. + */ + private Tiger192(Tiger192 that) { + this(); + this.a = that.a; + this.b = that.b; + this.c = that.c; + this.count = that.count; + this.buffer = (that.buffer != null) ? (byte[]) that.buffer.clone() : null; + } + + // Instance methods implementing BaseHash. + // ----------------------------------------------------------------------- + + public Object clone() { + return new Tiger192(this); + } +} + +//-------------------------------------------------------------------------- +//$Id: Tiger.java,v 1.1 2003/03/22 03:09:57 rsdio Exp $ +// +//Copyright (C) 2003, Free Software Foundation, Inc. +// +//This file is part of GNU Crypto. +// +//GNU Crypto is free software; you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation; either version 2, or (at your option) +//any later version. +// +//GNU Crypto 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 +//General Public License for more details. +// +//You should have received a copy of the GNU General Public License +//along with this program; see the file COPYING. If not, write to the +// +// Free Software Foundation Inc., +// 59 Temple Place - Suite 330, +// Boston, MA 02111-1307 +// USA +// +//Linking this library statically or dynamically with other modules is +//making a combined work based on this library. Thus, the terms and +//conditions of the GNU General Public License cover the whole +//combination. +// +//As a special exception, the copyright holders of this library give +//you permission to link this library with independent modules to +//produce an executable, regardless of the license terms of these +//independent modules, and to copy and distribute the resulting +//executable under terms of your choice, provided that you also meet, +//for each linked independent module, the terms and conditions of the +//license of that module. An independent module is a module which is +//not derived from or based on this library. If you modify this +//library, you may extend this exception to your version of the +//library, but you are not obligated to do so. If you do not wish to +//do so, delete this exception statement from your version. +// +//-------------------------------------------------------------------------- + +/** +* The Tiger message digest. Tiger was designed by Ross Anderson and Eli +* Biham, with the goal of producing a secure, fast hash function that +* performs especially well on next-generation 64-bit architectures, but +* is still efficient on 32- and 16-bit architectures. +* +*

Tiger processes data in 512-bit blocks and produces a 192-bit +* digest.

+* +*

References:

+*
    +*
  1. Tiger: A +* Fast New Hash Function, Ross Anderson and Eli Biham.
  2. +*
+* +* @version $Revision: 1.1 $ +*/ +abstract class BaseHash { + /** The canonical name prefix of the hash. */ + protected String name; + + /** The hash (output) size in bytes. */ + protected int hashSize; + + /** The hash (inner) block size in bytes. */ + protected int blockSize; + + /** Number of bytes processed so far. */ + protected long count; + + /** Temporary input buffer. */ + protected byte[] buffer; + + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + *

Trivial constructor for use by concrete subclasses.

+ * + * @param name the canonical name prefix of this instance. + * @param hashSize the block size of the output in bytes. + * @param blockSize the block size of the internal transform. + */ + protected BaseHash(String name, int hashSize, int blockSize) { + super(); + + this.name = name; + this.hashSize = hashSize; + this.blockSize = blockSize; + this.buffer = new byte[blockSize]; + + resetContext(); + } + + // Class methods + // ------------------------------------------------------------------------- + + // Instance methods + // ------------------------------------------------------------------------- + + // IMessageDigest interface implementation --------------------------------- + + public String name() { + return name; + } + + public int hashSize() { + return hashSize; + } + + public int blockSize() { + return blockSize; + } + + public void update(byte b) { + // compute number of bytes still unhashed; ie. present in buffer + int i = (int)(count % blockSize); + count++; + buffer[i] = b; + if (i == (blockSize - 1)) { + transform(buffer, 0); + } + } + + public void update(byte[] b, int offset, int len) { + int n = (int)(count % blockSize); + count += len; + int partLen = blockSize - n; + int i = 0; + + if (len >= partLen) { + System.arraycopy(b, offset, buffer, n, partLen); + transform(buffer, 0); + for (i = partLen; i + blockSize - 1 < len; i+= blockSize) { + transform(b, offset + i); + } + n = 0; + } + + if (i < len) { + System.arraycopy(b, offset + i, buffer, n, len - i); + } + } + + public byte[] digest() { + byte[] tail = padBuffer(); // pad remaining bytes in buffer + update(tail, 0, tail.length); // last transform of a message + byte[] result = getResult(); // make a result out of context + + reset(); // reset this instance for future re-use + + return result; + } + + public void reset() { // reset this instance for future re-use + count = 0L; + for (int i = 0; i < blockSize; ) { + buffer[i++] = 0; + } + + resetContext(); + } + + // methods to be implemented by concrete subclasses ------------------------ + + public abstract Object clone(); + + /** + *

Returns the byte array to use as padding before completing a hash + * operation.

+ * + * @return the bytes to pad the remaining bytes in the buffer before + * completing a hash operation. + */ + protected abstract byte[] padBuffer(); + + /** + *

Constructs the result from the contents of the current context.

+ * + * @return the output of the completed hash operation. + */ + protected abstract byte[] getResult(); + + /** Resets the instance for future re-use. */ + protected abstract void resetContext(); + + /** + *

The block digest transformation per se.

+ * + * @param in the blockSize long block, as an array of bytes to digest. + * @param offset the index where the data to digest is located within the + * input buffer. + */ + protected abstract void transform(byte[] in, int offset); +} + +//---------------------------------------------------------------------------- +//$Id: BaseHash.java,v 1.8 2002/11/07 17:17:45 raif Exp $ +// +//Copyright (C) 2001, 2002, Free Software Foundation, Inc. +// +//This file is part of GNU Crypto. +// +//GNU Crypto is free software; you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation; either version 2, or (at your option) +//any later version. +// +//GNU Crypto 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 +//General Public License for more details. +// +//You should have received a copy of the GNU General Public License +//along with this program; see the file COPYING. If not, write to the +// +// Free Software Foundation Inc., +// 59 Temple Place - Suite 330, +// Boston, MA 02111-1307 +// USA +// +//Linking this library statically or dynamically with other modules is +//making a combined work based on this library. Thus, the terms and +//conditions of the GNU General Public License cover the whole +//combination. +// +//As a special exception, the copyright holders of this library give +//you permission to link this library with independent modules to +//produce an executable, regardless of the license terms of these +//independent modules, and to copy and distribute the resulting +//executable under terms of your choice, provided that you also meet, +//for each linked independent module, the terms and conditions of the +//license of that module. An independent module is a module which is +//not derived from or based on this library. If you modify this +//library, you may extend this exception to your version of the +//library, but you are not obligated to do so. If you do not wish to +//do so, delete this exception statement from your version. +//---------------------------------------------------------------------------- +/** + *

A base abstract class to facilitate hash implementations.

+ * + * @version $Revision: 1.8 $ + */ diff --git a/100_core/src_160_hash/gplx/security/HashAlgo_tth192_tree_tst.java b/100_core/src_160_hash/gplx/security/HashAlgo_tth192_tree_tst.java new file mode 100644 index 000000000..c9dac39ed --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo_tth192_tree_tst.java @@ -0,0 +1,65 @@ +/* +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.security; import gplx.*; +import org.junit.*; +public class HashAlgo_tth192_tree_tst { + @Test public void CalcRecursiveHalves() { + tst_CalcRecursiveHalves(129, 128); + tst_CalcRecursiveHalves(128, 127); + tst_CalcRecursiveHalves(100, 99); + tst_CalcRecursiveHalves(20, 19); + tst_CalcRecursiveHalves(6, 5); + tst_CalcRecursiveHalves(5, 4); + tst_CalcRecursiveHalves(4, 3); + tst_CalcRecursiveHalves(3, 2); + tst_CalcRecursiveHalves(2, 1); + tst_CalcRecursiveHalves(1, 0); + tst_CalcRecursiveHalves(0, 0); + } + @Test public void CalcWorkUnits() { + tst_CalcWorkUnits(101, 21); // leafs; 10 full, 1 part (+11) -> reduce 11 to 5+1 (+5) -> reduce 6 to 3 (+3) -> reduce 3 to 1+1 (+1) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(100, 19); // leafs; 10 full (+10) -> reduce 10 to 5 (+5) -> reduce 5 to 2+1 (+2) -> reduce 3 to 1+1 (+1) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(30, 5); // leafs; 3 full (+3) -> reduce 3 to 1+1 (+1) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(11, 3); // leafs: 1 full, 1 part (+2) -> reduce 2 to 1 (+1) + tst_CalcWorkUnits(10, 1); + tst_CalcWorkUnits(9, 1); + tst_CalcWorkUnits(1, 1); + tst_CalcWorkUnits(0, 1); + } + void tst_CalcWorkUnits(int length, int expd) { + HashAlgo_tth192 algo = HashAlgo_tth192.new_(); algo.BlockSize_set(10); + int actl = algo.CalcWorkUnits(length); + Tfds.Eq(expd, actl); + } + void tst_CalcRecursiveHalves(int val, int expd) { + int actl = CalcRecursiveHalvesMock(val); + Tfds.Eq(expd, actl); + } + int CalcRecursiveHalvesMock(int val) { + if (val <= 1) return 0; + int rv = 0; + while (true) { + int multiple = val / 2; + int remainder = val % 2; + rv += multiple; + val = multiple + remainder; + if (val == 1) + return remainder == 0 ? rv : ++rv; + } + } +} diff --git a/100_core/src_160_hash/gplx/security/HashAlgo_tth192_tst.java b/100_core/src_160_hash/gplx/security/HashAlgo_tth192_tst.java new file mode 100644 index 000000000..f65879d89 --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashAlgo_tth192_tst.java @@ -0,0 +1,58 @@ +/* +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.security; import gplx.*; +import org.junit.*; import gplx.ios.*; /*IoStream*/ +public class HashAlgo_tth192_tst { + @Test public void Char0000() {tst_CalcBase32FromString("", "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ");} + @Test public void Char0001() {tst_CalcBase32FromString("\0", "VK54ZIEEVTWNAUI5D5RDFIL37LX2IQNSTAXFKSA");} + @Test public void Char0002() {tst_CalcBase32FromString("ab", "XQXRSGMB3PSN2VGZYJMNJG6SOOQ3JIGQHD2I6PQ");} + @Test public void Char0003() {tst_CalcBase32FromString("abc", "ASD4UJSEH5M47PDYB46KBTSQTSGDKLBHYXOMUIA");} + @Test public void Char0004() {tst_CalcBase32FromString("abcd", "SQF2PFTVIFRR5KJSI45IDENXMB43NI7EIXYGHGI");} + @Test public void Char0005() {tst_CalcBase32FromString("abcde", "SKGLNP5WV7ZUMF6IUK5CYXBE3PI4C6PHWNVM2YQ");} + @Test public void Char0009() {tst_CalcBase32FromString("abcdefghi", "RUIKHZFO4NIY6NNUHJMAC2I26U3U65FZWCO3UFY");} + @Test public void Char1024() {tst_CalcBase32FromString(String_.Repeat("A", 1024), "L66Q4YVNAFWVS23X2HJIRA5ZJ7WXR3F26RSASFA");} + @Test public void Char1025() {tst_CalcBase32FromString(String_.Repeat("A", 1025), "PZMRYHGY6LTBEH63ZWAHDORHSYTLO4LEFUIKHWY");} +// @Test // commented out due to time (approx 17.94 seconds) + public void Ax2Pow27() { // 134 MB + tst_CalcBase32FromString(String_.Repeat("A", (int)Math_.Pow(2, 27)), "QNIJO36QDIQREUT3HWK4MDVKD2T6OENAEKYADTQ"); + } + void tst_CalcBase32FromString(String raw, String expd) { + IoStream stream = IoStream_.mem_txt_(Io_url_.Null, raw); + String actl = HashAlgo_.Tth192.CalcHash(ConsoleDlg_.Null, stream); + Tfds.Eq(expd, actl); + } +} +/* + The empty (zero-length) file: + urn:tree:tiger:LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ + + A file with a single zero byte: + urn:tree:tiger:VK54ZIEEVTWNAUI5D5RDFIL37LX2IQNSTAXFKSA + + A file with 1024 'A' characters: + urn:tree:tiger:L66Q4YVNAFWVS23X2HJIRA5ZJ7WXR3F26RSASFA + + A file with 1025 'A' characters: + urn:tree:tiger:PZMRYHGY6LTBEH63ZWAHDORHSYTLO4LEFUIKHWY + + http://open-content.net/specs/draft-jchapweske-thex-02.html + + A file with 134,217,728 'A' characters (2 Pow 27) + urn:tree:tiger:QNIJO36QDIQREUT3HWK4MDVKD2T6OENAEKYADTQ + queried against DC++ 0.698 + */ diff --git a/100_core/src_160_hash/gplx/security/HashDlgWtr_tst.java b/100_core/src_160_hash/gplx/security/HashDlgWtr_tst.java new file mode 100644 index 000000000..6082514e4 --- /dev/null +++ b/100_core/src_160_hash/gplx/security/HashDlgWtr_tst.java @@ -0,0 +1,41 @@ +/* +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.security; import gplx.*; +import org.junit.*; import gplx.ios.*; /*IoStream*/ +public class HashDlgWtr_tst { + @Before public void setup() { + HashAlgo_tth192 algo = HashAlgo_tth192.new_(); + algo.BlockSize_set(10); + calc = algo; + } + @Test public void Basic() { + tst_Status(10, stringAry_(" - hash: 100%")); + tst_Status(11, stringAry_(" - hash: 66%")); + tst_Status(30, stringAry_(" - hash: 40%", " - hash: 60%", " - hash: 100%")); + } + void tst_Status(int count, String[] expdWritten) { + ConsoleDlg_dev dialog = ConsoleDlg_.Dev(); + String data = String_.Repeat("A", count); + IoStream stream = IoStream_.mem_txt_(Io_url_.Null, data); + calc.CalcHash(dialog, stream); + String[] actlWritten = dialog.Written().XtoStrAry(); + Tfds.Eq_ary(actlWritten, expdWritten); + } + String[] stringAry_(String... ary) {return ary;} + HashAlgo calc; +} diff --git a/100_core/src_200_io/gplx/Io_mgr.java b/100_core/src_200_io/gplx/Io_mgr.java new file mode 100644 index 000000000..32448f511 --- /dev/null +++ b/100_core/src_200_io/gplx/Io_mgr.java @@ -0,0 +1,131 @@ +/* +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; +import gplx.ios.*; /*IoItmFil, IoItmDir..*/ +public class Io_mgr { // exists primarily to gather all cmds under gplx namespace; otherwise need to use gplx.ios whenever copying/deleting file + public boolean Exists(Io_url url) {return url.Type_dir() ? ExistsDir(url) : ExistsFil(url);} + public boolean ExistsFil(Io_url url) {return IoEnginePool._.Fetch(url.Info().EngineKey()).ExistsFil_api(url);} + public void ExistsFilOrFail(Io_url url) {if (!ExistsFil(url)) throw Err_.new_("could not find file").Add("url", url);} + public void SaveFilStr(String url, String text) {SaveFilStr_args(Io_url_.new_fil_(url), text).Exec();} + public void SaveFilStr(Io_url url, String text) {SaveFilStr_args(url, text).Exec();} + public IoEngine_xrg_saveFilStr SaveFilStr_args(Io_url url, String text) {return IoEngine_xrg_saveFilStr.new_(url, text);} + public void AppendFilStr(String url, String text) {AppendFilStr(Io_url_.new_fil_(url), text);} + public void AppendFilStr(Io_url url, String text) {SaveFilStr_args(url, text).Append_(true).Exec();} + public void DeleteFil(Io_url url) {DeleteFil_args(url).Exec();} + public IoEngine_xrg_deleteFil DeleteFil_args(Io_url url) {return IoEngine_xrg_deleteFil.new_(url);} + public void MoveFil(Io_url src, Io_url trg) {IoEngine_xrg_xferFil.move_(src, trg).Exec();} + public IoEngine_xrg_xferFil MoveFil_args(Io_url src, Io_url trg, boolean overwrite) {return IoEngine_xrg_xferFil.move_(src, trg).Overwrite_(overwrite);} + public void CopyFil(Io_url src, Io_url trg, boolean overwrite) {IoEngine_xrg_xferFil.copy_(src, trg).Overwrite_(overwrite).Exec();} + public IoEngine_xrg_xferFil CopyFil_args(Io_url src, Io_url trg, boolean overwrite) {return IoEngine_xrg_xferFil.copy_(src, trg).Overwrite_(overwrite);} + public IoRecycleBin RecycleBin() {return recycleBin;} IoRecycleBin recycleBin = IoRecycleBin._; + + public IoStream OpenStreamWrite(Io_url url) {return OpenStreamWrite_args(url).Exec();} + public IoEngine_xrg_openWrite OpenStreamWrite_args(Io_url url) {return IoEngine_xrg_openWrite.new_(url);} + public IoItmFil QueryFil(Io_url url) {return IoEnginePool._.Fetch(url.Info().EngineKey()).QueryFil(url);} + public void UpdateFilAttrib(Io_url url, IoItmAttrib attrib) {IoEnginePool._.Fetch(url.Info().EngineKey()).UpdateFilAttrib(url, attrib);} + public void UpdateFilModifiedTime(Io_url url, DateAdp modified) {IoEnginePool._.Fetch(url.Info().EngineKey()).UpdateFilModifiedTime(url, modified);} + + public boolean ExistsDir(Io_url url) {return IoEnginePool._.Fetch(url.Info().EngineKey()).ExistsDir(url);} + public void CreateDir(Io_url url) {IoEnginePool._.Fetch(url.Info().EngineKey()).CreateDir(url);} + public boolean CreateDirIfAbsent(Io_url url) { + boolean exists = ExistsDir(url); + if (!exists) { + CreateDir(url); + return true; + } + return false; + } + public Io_url[] QueryDir_fils(Io_url dir) {return QueryDir_args(dir).ExecAsUrlAry();} + public IoEngine_xrg_queryDir QueryDir_args(Io_url dir) {return IoEngine_xrg_queryDir.new_(dir);} + public void DeleteDirSubs(Io_url url) {IoEngine_xrg_deleteDir.new_(url).Exec();} + public IoEngine_xrg_deleteDir DeleteDir_cmd(Io_url url) {return IoEngine_xrg_deleteDir.new_(url);} + public void DeleteDirDeep(Io_url url) {IoEngine_xrg_deleteDir.new_(url).Recur_().Exec();} + public void DeleteDirDeep_ary(Io_url... urls) {for (Io_url url : urls) IoEngine_xrg_deleteDir.new_(url).Recur_().Exec();} + public void MoveDirDeep(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.move_(src, trg).Recur_().Exec();} + public IoEngine_xrg_xferDir CopyDir_cmd(Io_url src, Io_url trg) {return IoEngine_xrg_xferDir.copy_(src, trg);} + public void CopyDirSubs(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Exec();} + public void CopyDirDeep(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec();} + public void DeleteDirIfEmpty(Io_url url) { + if (Array_.Len(QueryDir_fils(url)) == 0) + this.DeleteDirDeep(url); + } + public void AliasDir_sysEngine(String srcRoot, String trgRoot) {AliasDir(srcRoot, trgRoot, IoEngine_.SysKey);} + public void AliasDir(String srcRoot, String trgRoot, String engineKey) {IoUrlInfoRegy._.Reg(IoUrlInfo_.alias_(srcRoot, trgRoot, engineKey));} +// public IoStream OpenStreamRead2(Io_url url) {return IoEngine_xrg_openRead.new_(url).ExecAsIoStreamOrFail();} + public IoStream OpenStreamRead(Io_url url) {return OpenStreamRead_args(url).ExecAsIoStreamOrFail();} + public IoEngine_xrg_openRead OpenStreamRead_args(Io_url url) {return IoEngine_xrg_openRead.new_(url);} + public String LoadFilStr(String url) {return LoadFilStr_args(Io_url_.new_fil_(url)).Exec();} + public String LoadFilStr(Io_url url) {return LoadFilStr_args(url).Exec();} + public IoEngine_xrg_loadFilStr LoadFilStr_args(Io_url url) {return IoEngine_xrg_loadFilStr.new_(url);} + public byte[] LoadFilBry(String url) {return LoadFilBry_reuse(Io_url_.new_fil_(url), Bry_.Empty, Int_obj_ref.zero_());} + public byte[] LoadFilBry(Io_url url) {return LoadFilBry_reuse(url, Bry_.Empty, Int_obj_ref.zero_());} + public void LoadFilBryByBfr(Io_url url, Bry_bfr bfr) { + Int_obj_ref len = Int_obj_ref.zero_(); + byte[] bry = LoadFilBry_reuse(url, Bry_.Empty, len); + bfr.Bfr_init(bry, len.Val()); + } + public static final byte[] LoadFilBry_fail = Bry_.Empty; + public byte[] LoadFilBry_reuse(Io_url url, byte[] ary, Int_obj_ref aryLen) { + if (!ExistsFil(url)) {aryLen.Val_(0); return LoadFilBry_fail;} + IoStream stream = IoStream_.Null; + try { + stream = OpenStreamRead(url); + int streamLen = (int)stream.Len(); + aryLen.Val_(streamLen); + if (streamLen > ary.length) + ary = new byte[streamLen]; + stream.ReadAry(ary); + return ary; + } + catch (Exception e) {throw Err_.new_("failed to load file").Add("url", url.Xto_api()).Add("e", Err_.Message_lang(e));} + finally {stream.Rls();} + } + public void AppendFilBfr(Io_url url, Bry_bfr bfr) {AppendFilByt(url, bfr.Bfr(), 0, bfr.Len()); bfr.ClearAndReset();} + public void AppendFilByt(Io_url url, byte[] val) {AppendFilByt(url, val, 0, val.length);} + public void AppendFilByt(Io_url url, byte[] val, int len) {AppendFilByt(url, val, 0, len);} + public void AppendFilByt(Io_url url, byte[] val, int bgn, int len) { + IoStream stream = IoStream_.Null; + try { + stream = OpenStreamWrite_args(url).Mode_(IoStream_.Mode_wtr_append).Exec(); + stream.Write(val, bgn, len); + } finally {stream.Rls();} + } + public void SaveFilBfr(Io_url url, Bry_bfr bfr) {SaveFilBry(url, bfr.Bfr(), bfr.Len()); bfr.Clear();} + public void SaveFilBry(String urlStr, byte[] val) {SaveFilBry(Io_url_.new_fil_(urlStr), val);} + public void SaveFilBry(Io_url url, byte[] val) {SaveFilBry(url, val, val.length);} + public void SaveFilBry(Io_url url, byte[] val, int len) {SaveFilBry(url, val, 0, len);} + public void SaveFilBry(Io_url url, byte[] val, int bgn, int len) { + IoStream stream = IoStream_.Null; + try { + stream = OpenStreamWrite(url); + stream.Write(val, bgn, len); + } finally {stream.Rls();} + } + public IoEngine InitEngine_mem() {return IoEngine_.Mem_init_();} + public IoEngine InitEngine_mem_(String key) { + IoEngine engine = IoEngine_.mem_new_(key); + IoEnginePool._.AddReplace(engine); + IoUrlInfoRegy._.Reg(IoUrlInfo_.mem_(key, key)); + return engine; + } + public boolean DownloadFil(String src, Io_url trg) {return IoEngine_xrg_downloadFil.new_(src, trg).Exec();} + public IoEngine_xrg_downloadFil DownloadFil_args(String src, Io_url trg) {return IoEngine_xrg_downloadFil.new_(src, trg);} + public static final Io_mgr _ = new Io_mgr(); public Io_mgr() {} + public static final int Len_kb = 1024, Len_mb = 1048576, Len_gb = 1073741824, Len_gb_2 = 2147483647; + public static final long Len_null = -1; +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine.java b/100_core/src_200_io/gplx/ios/IoEngine.java new file mode 100644 index 000000000..3dfc0feab --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine.java @@ -0,0 +1,154 @@ +/* +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.ios; import gplx.*; +import gplx.criterias.*; +public interface IoEngine { + String Key(); + boolean ExistsFil_api(Io_url url); + void SaveFilText_api(IoEngine_xrg_saveFilStr args); + String LoadFilStr(IoEngine_xrg_loadFilStr args); + void DeleteFil_api(IoEngine_xrg_deleteFil args); + void CopyFil(IoEngine_xrg_xferFil args); + void MoveFil(IoEngine_xrg_xferFil args); + IoItmFil QueryFil(Io_url url); + void UpdateFilAttrib(Io_url url, IoItmAttrib atr); // will fail if file does not exists + void UpdateFilModifiedTime(Io_url url, DateAdp modified); + IoStream OpenStreamRead(Io_url url); + IoStream OpenStreamWrite(IoEngine_xrg_openWrite args); + void XferFil(IoEngine_xrg_xferFil args); + void RecycleFil(IoEngine_xrg_recycleFil xrg); + + boolean ExistsDir(Io_url url); + void CreateDir(Io_url url); // creates all folder levels (EX: C:\a\b\c\ will create C:\a\ and C:\a\b\). will not fail if called on already existing folders. + void DeleteDir(Io_url url); + void MoveDir(Io_url src, Io_url trg); // will fail if trg exists + void CopyDir(Io_url src, Io_url trg); + IoItmDir QueryDir(Io_url url); + + void DeleteDirDeep(IoEngine_xrg_deleteDir args); + void MoveDirDeep(IoEngine_xrg_xferDir args); // will fail if trg exists + IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args); + void XferDir(IoEngine_xrg_xferDir args); + boolean DownloadFil(IoEngine_xrg_downloadFil xrg); + Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg); +} +class IoEngineUtl { + public int BufferLength() {return bufferLength;} public void BufferLength_set(int v) {bufferLength = v;} int bufferLength = 4096; // 0x1000 + public void DeleteRecycleGplx(IoEngine engine, IoEngine_xrg_recycleFil xrg) { + Io_url recycleUrl = xrg.RecycleUrl(); + if (recycleUrl.Type_fil()) + engine.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true)); + else + engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true)); + } + public void DeleteDirDeep(IoEngine engine, Io_url dirUrl, IoEngine_xrg_deleteDir args) { + ConsoleDlg usrDlg = args.UsrDlg(); + IoItmDir dir = engine.QueryDir(dirUrl); if (!dir.Exists()) return; + for (Object subDirObj : dir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + if (!args.SubDirScanCrt().Matches(subDir)) continue; + if (args.Recur()) DeleteDirDeep(engine, subDir.Url(), args); + } + for (Object subFilObj : dir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + if (!args.MatchCrt().Matches(subFil)) continue; + Io_url subFilUrl = subFil.Url(); + try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(subFilUrl).ReadOnlyFails_(args.ReadOnlyFails()));} + catch (Exception exc) {usrDlg.WriteLineFormat(Err_.Message_lang(exc));} + } + // all subs deleted; now delete dir + if (!args.MatchCrt().Matches(dir)) return; + try {engine.DeleteDir(dir.Url());} + catch (Exception exc) {usrDlg.WriteLineFormat(Err_.Message_lang(exc));} + } + public void XferDir(IoEngine srcEngine, Io_url src, IoEngine trgEngine, Io_url trg, IoEngine_xrg_xferDir args) { + trgEngine.CreateDir(trg); + IoItmDir srcDir = QueryDirDeep(srcEngine, IoEngine_xrg_queryDir.new_(src).Recur_(false)); + for (Object subSrcObj : srcDir.SubDirs()) { + IoItmDir subSrc = (IoItmDir)subSrcObj; + if (!args.SubDirScanCrt().Matches(subSrc)) continue; + if (!args.MatchCrt().Matches(subSrc)) continue; + Io_url subTrg = trg.GenSubDir_nest(subSrc.Url().NameOnly()); //EX: C:\abc\def\ -> C:\123\ + def\ + if (args.Recur()) XferDir(srcEngine, subSrc.Url(), trgEngine, subTrg, args); + } + IoItmList srcFils = IoItmList.list_(src.Info().CaseSensitive()); + for (Object srcFilObj : srcDir.SubFils()) { + IoItmFil srcFil = (IoItmFil)srcFilObj; + if (args.MatchCrt().Matches(srcFil)) srcFils.Add(srcFil); + } + for (Object srcFilObj : srcFils) { + IoItmFil srcFil = (IoItmFil)srcFilObj; + Io_url srcFilPath = srcFil.Url(); + Io_url trgFilPath = trg.GenSubFil(srcFilPath.NameAndExt()); //EX: C:\abc\fil.txt -> C:\123\ + fil.txt + IoEngine_xrg_xferFil xferArgs = args.Type_move() ? IoEngine_xrg_xferFil.move_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()) : IoEngine_xrg_xferFil.copy_(srcFilPath, trgFilPath).Overwrite_(args.Overwrite()); + XferFil(srcEngine, xferArgs); + } + if (args.Type_move()) srcEngine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_(args.Recur()).ReadOnlyFails_(args.ReadOnlyFails()));// this.DeleteDirDeep(srcEngine, src, IoEngine_xrg_deleteItm.new_(src).Recur_(args.Recur()).ReadOnlyIgnored_(args.ReadOnlyIgnored())); + } + public void XferFil(IoEngine srcEngine, IoEngine_xrg_xferFil args) { + Io_url src = args.Src(), trg = args.Trg(); + if (String_.Eq(srcEngine.Key(), trg.Info().EngineKey())) { + if (args.Type_move()) + srcEngine.MoveFil(args); + else + srcEngine.CopyFil(args); + } + else { + TransferStream(src, trg); + if (args.Type_move()) srcEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(src)); + } + } + public IoItmDir QueryDirDeep(IoEngine engine, IoEngine_xrg_queryDir args) { + IoItmDir rv = IoItmDir_.top_(args.Url()); + rv.Exists_set(QueryDirDeepCore(rv, args.Url(), engine, args.Recur(), args.SubDirScanCrt(), args.DirCrt(), args.FilCrt(), args.UsrDlg(), args.DirInclude())); + return rv; + } + static boolean QueryDirDeepCore(IoItmDir ownerDir, Io_url url, IoEngine engine, boolean recur, Criteria subDirScanCrt, Criteria dirCrt, Criteria filCrt, ConsoleDlg usrDlg, boolean dirInclude) { + if (usrDlg.CanceledChk()) return false; + if (usrDlg.Enabled()) usrDlg.WriteTempText(String_.Concat("scan: ", url.Raw())); + IoItmDir scanDir = engine.QueryDir(url); + for (Object subDirObj : scanDir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + if (!subDirScanCrt.Matches(subDir)) continue; + if (dirCrt.Matches(subDir)) { + ownerDir.SubDirs().Add(subDir); // NOTE: always add subDir; do not use dirCrt here, else its subFils will be added to non-existent subDir + } + if (recur) + QueryDirDeepCore(subDir, subDir.Url(), engine, recur, subDirScanCrt, dirCrt, filCrt, usrDlg, dirInclude); + } + for (Object subFilObj : scanDir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + if (filCrt.Matches(subFil)) ownerDir.SubFils().Add(subFil); + } + return scanDir.Exists(); + } + void TransferStream(Io_url src, Io_url trg) { + IoStream srcStream = null; + IoStream trgStream = null; + try { + srcStream = IoEnginePool._.Fetch(src.Info().EngineKey()).OpenStreamRead(src); + trgStream = IoEngine_xrg_openWrite.new_(trg).Exec(); + srcStream.Transfer(trgStream, bufferLength); + } + finally { + if (srcStream != null) srcStream.Rls(); + if (trgStream != null) trgStream.Rls(); + } + } + public static IoEngineUtl new_() {return new IoEngineUtl();} IoEngineUtl() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEnginePool.java b/100_core/src_200_io/gplx/ios/IoEnginePool.java new file mode 100644 index 000000000..513e06a82 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEnginePool.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoEnginePool { + public void AddReplace(IoEngine engine) { + hash.Del(engine.Key()); + hash.Add(engine.Key(), engine); + } + public IoEngine Fetch(String key) { + IoEngine rv = (IoEngine)hash.Fetch(key); + return rv == null ? IoEngine_.Mem : rv; // rv == null when url is null or empty; return Mem which should be a noop; DATE:2013-06-04 + } + HashAdp hash = HashAdp_.new_(); + public static final IoEnginePool _ = new IoEnginePool(); + IoEnginePool() { + this.AddReplace(IoEngine_.Sys); + this.AddReplace(IoEngine_.Mem); + } +} \ No newline at end of file diff --git a/100_core/src_200_io/gplx/ios/IoEngine_.java b/100_core/src_200_io/gplx/ios/IoEngine_.java new file mode 100644 index 000000000..b57ff377c --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_.java @@ -0,0 +1,30 @@ +/* +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.ios; import gplx.*; +public class IoEngine_ { + public static final String SysKey = "sys"; + public static final String MemKey = "mem"; + + public static final IoEngine Sys = IoEngine_system.new_(); + public static final IoEngine_memory Mem = IoEngine_memory.new_(MemKey); + public static IoEngine Mem_init_() { + Mem.Clear(); + return Mem; + } + public static IoEngine mem_new_(String key) {return IoEngine_memory.new_(key);} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_base.java b/100_core/src_200_io/gplx/ios/IoEngine_base.java new file mode 100644 index 000000000..f316e7983 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_base.java @@ -0,0 +1,57 @@ +/* +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.ios; import gplx.*; +public abstract class IoEngine_base implements IoEngine { + public abstract String Key(); + public abstract boolean ExistsFil_api(Io_url url); + public abstract void SaveFilText_api(IoEngine_xrg_saveFilStr args); + public abstract String LoadFilStr(IoEngine_xrg_loadFilStr args); + public abstract void DeleteFil_api(IoEngine_xrg_deleteFil args); + public abstract void CopyFil(IoEngine_xrg_xferFil args); + public abstract void MoveFil(IoEngine_xrg_xferFil args); + public abstract IoItmFil QueryFil(Io_url url); + public abstract void UpdateFilAttrib(Io_url url, IoItmAttrib atr); // will fail if file does not exists + public abstract void UpdateFilModifiedTime(Io_url url, DateAdp modified); + public abstract IoStream OpenStreamRead(Io_url url); + public abstract IoStream OpenStreamWrite(IoEngine_xrg_openWrite args); + public abstract void XferFil(IoEngine_xrg_xferFil args); + + public abstract boolean ExistsDir(Io_url url); + public abstract void CreateDir(Io_url url); // creates all folder levels (EX: C:\a\b\c\ will create C:\a\ and C:\a\b\). will not fail if called on already existing folders. + public abstract void DeleteDir(Io_url url); + public abstract void MoveDir(Io_url src, Io_url trg); // will fail if trg exists + public abstract void CopyDir(Io_url src, Io_url trg); + public abstract IoItmDir QueryDir(Io_url url); + + public abstract void DeleteDirDeep(IoEngine_xrg_deleteDir args); + public abstract void MoveDirDeep(IoEngine_xrg_xferDir args); // will fail if trg exists + public abstract IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args); + public abstract void XferDir(IoEngine_xrg_xferDir args); + public abstract boolean DownloadFil(IoEngine_xrg_downloadFil xrg); + public abstract Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg); + + public void RecycleFil(IoEngine_xrg_recycleFil xrg) { + Io_url recycleUrl = xrg.RecycleUrl(); + if (recycleUrl.Type_fil()) { + this.MoveFil(IoEngine_xrg_xferFil.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true).MissingFails_(xrg.MissingFails())); + IoRecycleBin._.Regy_add(xrg); + } + else + this.MoveDirDeep(IoEngine_xrg_xferDir.move_(xrg.Url(), recycleUrl).Overwrite_(false).ReadOnlyFails_(true)); + } +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_memory.java b/100_core/src_200_io/gplx/ios/IoEngine_memory.java new file mode 100644 index 000000000..1f2cae2d1 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_memory.java @@ -0,0 +1,200 @@ +/* +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.ios; import gplx.*; +public class IoEngine_memory extends IoEngine_base { + @Override public String Key() {return key;} private String key = IoEngine_.MemKey; + @Override public boolean ExistsFil_api(Io_url url) {return FetchFil(url) != IoItmFil_mem.Null;} + @Override public void DeleteFil_api(IoEngine_xrg_deleteFil args) { + Io_url url = args.Url(); + IoItmDir dir = FetchDir(url.OwnerDir()); if (dir == null) return; // url doesn't exist; just exit + IoItmFil fil = IoItmFil_.as_(dir.SubFils().Fetch(url.NameAndExt())); + if (fil != null && fil.ReadOnly() && args.ReadOnlyFails()) throw IoErr.FileIsReadOnly(url); + dir.SubFils().Del(url); + } + void DeleteFil(Io_url url) {DeleteFil_api(IoEngine_xrg_deleteFil.new_(url));} + @Override public void XferFil(IoEngine_xrg_xferFil args) {utl.XferFil(this, args);} + @Override public void MoveFil(IoEngine_xrg_xferFil args) { + Io_url src = args.Src(), trg = args.Trg(); boolean overwrite = args.Overwrite(); + if (String_.Eq(src.Xto_api(), trg.Xto_api())) throw Err_.new_fmt_("move failed; src is same as trg: {0}", src.Raw()); + CheckTransferArgs("move", src, trg, overwrite); + if (overwrite) DeleteFil(trg); + IoItmFil_mem curFil = FetchFil(src); curFil.Name_(trg.NameAndExt()); + AddFilToDir(trg.OwnerDir(), curFil); + DeleteFil(src); + } + @Override public void CopyFil(IoEngine_xrg_xferFil args) { + Io_url src = args.Src(), trg = args.Trg(); boolean overwrite = args.Overwrite(); + CheckTransferArgs("copy", src, trg, overwrite); + if (overwrite) DeleteFil(trg); + IoItmFil_mem srcFil = FetchFil(src); + IoItmFil_mem curFil = srcFil.Clone(); curFil.Name_(trg.NameAndExt()); + AddFilToDir(trg.OwnerDir(), curFil); + } + @Override public IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args) {return utl.QueryDirDeep(this, args);} + @Override public void UpdateFilAttrib(Io_url url, IoItmAttrib atr) {FetchFil(url).ReadOnly_(atr.ReadOnly());} + @Override public void UpdateFilModifiedTime(Io_url url, DateAdp modified) {FetchFil(url).ModifiedTime_(modified);} + @Override public IoItmFil QueryFil(Io_url url) {return FetchFil(url);} + @Override public void SaveFilText_api(IoEngine_xrg_saveFilStr args) { + Io_url url = args.Url(); + IoItmDir dir = FetchDir(url.OwnerDir()); + if (dir != null) { + IoItmFil fil = IoItmFil_.as_(dir.SubFils().Fetch(url.NameAndExt())); + if (fil != null && fil.ReadOnly()) throw IoErr.FileIsReadOnly(url); + } + + if (args.Append()) + AppendFilStr(args); + else + SaveFilStr(args.Url(), args.Text()); + } + @Override public String LoadFilStr(IoEngine_xrg_loadFilStr args) { + return FetchFil(args.Url()).Text(); + } + void SaveFilStr(Io_url url, String text) { + DateAdp time = DateAdp_.Now(); + IoItmFil_mem fil = IoItmFil_mem.new_(url, String_.Len(text), time, text); + AddFilToDir(url.OwnerDir(), fil); + } + void AppendFilStr(IoEngine_xrg_saveFilStr args) { + Io_url url = args.Url(); String text = args.Text(); + if (ExistsFil_api(url)) { + IoItmFil_mem fil = FetchFil(url); + fil.ModifiedTime_(DateAdp_.Now()); + fil.Text_set(fil.Text() + text); + } + else + SaveFilStr(args.Url(), args.Text()); + } + @Override public IoStream OpenStreamRead(Io_url url) { + IoItmFil_mem fil = FetchFil(url); + fil.Stream().Position_set(0); + return fil.Stream(); + } + @Override public IoStream OpenStreamWrite(IoEngine_xrg_openWrite args) { + Io_url url = args.Url(); + IoItmFil_mem fil = FetchFil(url); + if (fil == IoItmFil_mem.Null) { // file doesn't exist; create new one + SaveFilStr(url, ""); + fil = FetchFil(url); + } + else { + if (args.Mode() == IoStream_.Mode_wtr_create) + fil.Text_set(""); // NOTE: clear text b/c it still has pointer to existing stream + } + return fil.Stream(); + } + + @Override public boolean ExistsDir(Io_url url) {return FetchDir(url) != null;} + @Override public void CreateDir(Io_url url) { + IoItmDir dir = FetchDir(url); if (dir != null) return; // dir exists; exit + dir = IoItmDir_.top_(url); + dirs.Add(dir); + IoItmDir ownerDir = FetchDir(url.OwnerDir()); + if (ownerDir == null && !url.OwnerDir().Eq(Io_url_.Null)) { // no owner dir && not "driveDir" -> create + CreateDir(url.OwnerDir()); // recursive + ownerDir = FetchDir(url.OwnerDir()); + } + if (ownerDir != null) + ownerDir.SubDirs().Add(dir); + } + @Override public void DeleteDir(Io_url url) { + FetchDir(url); // force creation if exists? + dirs.Del(url); + IoItmDir ownerDir = FetchDir(url.OwnerDir()); if (ownerDir == null) return; // no ownerDir; no need to unregister + ownerDir.SubDirs().Del(url); + } + @Override public void XferDir(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool._.Fetch(trg.Info().EngineKey()), trg, args);} + @Override public void MoveDirDeep(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool._.Fetch(trg.Info().EngineKey()), trg, args);} + @Override public void MoveDir(Io_url src, Io_url trg) {if (ExistsDir(trg)) throw Err_.new_("trg already exists").Add("trg", trg); + IoItmDir dir = FetchDir(src); dir.Name_(trg.NameAndExt()); + for (Object filObj : dir.SubFils()) { // move all subFiles + IoItmFil fil = (IoItmFil)filObj; + fil.OwnerDir_set(dir); + } + dirs.Add(dir); + DeleteDir(src); + } + @Override public IoItmDir QueryDir(Io_url url) { + IoItmDir dir = FetchDir(url); + IoItmDir rv = IoItmDir_.top_(url); // always return copy b/c caller may add/del itms directly + if (dir == null) { + rv.Exists_set(false); + return rv; + } + for (Object subDirObj : dir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + rv.SubDirs().Add(IoItmDir_.scan_(subDir.Url())); + } + for (Object subFilObj : dir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + rv.SubFils().Add(subFil); + } + return rv; + } + @Override public void DeleteDirDeep(IoEngine_xrg_deleteDir args) {utl.DeleteDirDeep(this, args.Url(), args);} + @Override public void CopyDir(Io_url src, Io_url trg) { + IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec(); + } + void AddFilToDir(Io_url dirPath, IoItmFil fil) { + IoItmDir dir = FetchDir(dirPath); + if (dir == null) { + CreateDir(dirPath); + dir = FetchDir(dirPath); + } + dir.SubFils().Del(fil.Url()); + dir.SubFils().Add(fil); + } + IoItmDir FetchDir(Io_url url) {return IoItmDir_.as_(dirs.Fetch(url));} + IoItmFil_mem FetchFil(Io_url url) { + IoItmDir ownerDir = FetchDir(url.OwnerDir()); + if (ownerDir == null) return IoItmFil_mem.Null; + IoItmFil_mem rv = IoItmFil_mem.as_(ownerDir.SubFils().Fetch(url.NameAndExt())); + if (rv == null) rv = IoItmFil_mem.Null; + return rv; + } + void CheckTransferArgs(String op, Io_url src, Io_url trg, boolean overwrite) { + if (!ExistsFil_api(src)) throw Err_.new_("src does not exist").Add("src", src); + if (ExistsFil_api(trg) && !overwrite) throw Err_.invalid_op_("trg already exists").Add("op", op).Add("overwrite", false).Add("src", src).Add("trg", trg); + } + public void Clear() {dirs.Clear();} + @Override public boolean DownloadFil(IoEngine_xrg_downloadFil xrg) { + Io_url src = Io_url_.mem_fil_(xrg.Src()); + if (!ExistsFil_api(src)) { + xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + return false; + } + XferFil(IoEngine_xrg_xferFil.copy_(src, xrg.Trg()).Overwrite_()); + return true; + } + @Override public Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg) { + Io_url src = Io_url_.mem_fil_(xrg.Src()); + if (!ExistsFil_api(src)) { + xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + return Io_stream_rdr_.Null; + } + byte[] bry = Bry_.new_utf8_(FetchFil(Io_url_.mem_fil_(xrg.Src())).Text()); + return Io_stream_rdr_.mem_(bry); + } + IoItmHash dirs = IoItmHash.new_(); + IoEngineUtl utl = IoEngineUtl.new_(); + @gplx.Internal protected static IoEngine_memory new_(String key) { + IoEngine_memory rv = new IoEngine_memory(); + rv.key = key; + return rv; + } IoEngine_memory() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_system.java b/100_core/src_200_io/gplx/ios/IoEngine_system.java new file mode 100644 index 000000000..67d06f1bd --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_system.java @@ -0,0 +1,628 @@ +/* +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.ios; import gplx.*; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.nio.*; +import java.nio.channels.*; +import java.util.Date; + +import javax.print.FlavorException; +import javax.tools.JavaCompiler; +import gplx.criterias.*; +public class IoEngine_system extends IoEngine_base { + @Override public String Key() {return IoEngine_.SysKey;} + @Override public void DeleteDirDeep(IoEngine_xrg_deleteDir args) {utl.DeleteDirDeep(this, args.Url(), args);} + @Override public void XferDir(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool._.Fetch(trg.Info().EngineKey()), trg, args);} + @Override public void XferFil(IoEngine_xrg_xferFil args) {utl.XferFil(this, args);} + @Override public IoItmDir QueryDirDeep(IoEngine_xrg_queryDir args) {return utl.QueryDirDeep(this, args);} + @Override public void CopyDir(Io_url src, Io_url trg) {IoEngine_xrg_xferDir.copy_(src, trg).Recur_().Exec();} + @Override public void MoveDirDeep(IoEngine_xrg_xferDir args) {Io_url trg = args.Trg(); utl.XferDir(this, args.Src(), IoEnginePool._.Fetch(trg.Info().EngineKey()), trg, args);} + @Override public void DeleteFil_api(IoEngine_xrg_deleteFil args) { + Io_url url = args.Url(); + File fil = Fil_(url); + if (!Fil_Exists(fil)) { + if (args.MissingFails()) throw IoErr.FileNotFound("delete", url); + else return; + } + MarkFileWritable(fil, url, args.ReadOnlyFails(), "DeleteFile"); + DeleteFil_lang(fil, url); + } + @Override public boolean ExistsFil_api(Io_url url) { + return new File(url.Xto_api()).exists(); + } + @Override public void SaveFilText_api(IoEngine_xrg_saveFilStr mpo) { + Io_url url = mpo.Url(); + + // encode string + byte[] textBytes = null; + textBytes = Bry_.new_utf8_(mpo.Text()); + + FileChannel fc = null; FileOutputStream fos = null; + if (!ExistsDir(url.OwnerDir())) CreateDir(url.OwnerDir()); + try { + // open file + try {fos = new FileOutputStream(url.Xto_api(), mpo.Append());} + catch (FileNotFoundException e) {throw Err_Fil_NotFound(e, url);} + fc = fos.getChannel(); + + // write text + try {fc.write(ByteBuffer.wrap(textBytes));} + catch (IOException e) { + Closeable_Close(fc, url, false); + Closeable_Close(fos, url, false); + throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "write data to file failed").Add("url", url.Xto_api()); + } + if (!Op_sys.Cur().Tid_is_drd()) { + File fil = new File(url.Xto_api()); + IoEngine_system_xtn.SetExecutable(fil, true); + } + } + finally { + // cleanup + Closeable_Close(fc, url, false); + Closeable_Close(fos, url, false); + } + } + @SuppressWarnings("resource") + @Override public String LoadFilStr(IoEngine_xrg_loadFilStr args) { + Io_url url = args.Url(); + + // get reader for file + InputStream stream = null; + try {stream = new FileInputStream(url.Xto_api());} + catch (FileNotFoundException e) { + if (args.MissingIgnored()) return ""; + throw Err_Fil_NotFound(e, url); + } + InputStreamReader reader = null; + try {reader = new InputStreamReader(stream, IoEngineArgs._.LoadFilStr_Encoding);} + catch (UnsupportedEncodingException e) { + Closeable_Close(stream, url, false); + throw Err_Text_UnsupportedEncoding(IoEngineArgs._.LoadFilStr_Encoding, "", url, e); + } + + // make other objects + char[] readerBuffer = new char[IoEngineArgs._.LoadFilStr_BufferSize]; + int pos = 0; + StringWriter sw = new StringWriter(); + + // transfer data + while (true) { + try {pos = reader.read(readerBuffer);} + catch (IOException e) { + Closeable_Close(stream, url, false); + Closeable_Close(reader, url, false); + throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "read data from file failed").Add("url", url.Xto_api()).Add("pos", pos); + } + if (pos == -1) break; + sw.write(readerBuffer, 0, pos); + } + + // cleanup + Closeable_Close(stream, url, false); + Closeable_Close(reader, url, false); + return sw.toString(); + } + @Override public boolean ExistsDir(Io_url url) {return new File(url.Xto_api()).exists();} + @Override public void CreateDir(Io_url url) {new File(url.Xto_api()).mkdirs();} + @Override public void DeleteDir(Io_url url) { + File dir = new File(url.Xto_api()); + if (!dir.exists()) return; + boolean rv = dir.delete(); + if (!rv) throw Err_.new_key_(IoEngineArgs._.Err_IoException, "delete dir failed").Add("url", url.Xto_api()); + } + @Override public IoItmDir QueryDir(Io_url url) { + IoItmDir rv = IoItmDir_.scan_(url); + File dirInfo = new File(url.Xto_api()); + if (!dirInfo.exists()) { + rv.Exists_set(false); + return rv; + } + IoUrlInfo urlInfo = url.Info(); + File[] subItmAry = dirInfo.listFiles(); + if (subItmAry == null) return rv; // directory has no files + for (int i = 0; i < subItmAry.length; i++) { + File subItm = subItmAry[i]; + if (subItm.isFile()) { + IoItmFil subFil = QueryMkr_fil(urlInfo, subItm); + rv.SubFils().Add(subFil); + } + else { + IoItmDir subDir = QueryMkr_dir(urlInfo, subItm); + rv.SubDirs().Add(subDir); + } + } + return rv; + } + IoItmFil QueryMkr_fil(IoUrlInfo urlInfo, File apiFil) { + Io_url filUrl = Io_url_.new_inf_(apiFil.getPath(), urlInfo); // NOTE: may throw PathTooLongException when url is > 248 (exception messages states 260) + long fil_len = apiFil.exists() ? apiFil.length() : IoItmFil.Size_Invalid; // NOTE: if file doesn't exist, set len to -1; needed for "boolean Exists() {return size != Size_Invalid;}"; DATE:2014-06-21 + IoItmFil rv = IoItmFil_.new_(filUrl, fil_len, DateAdp_.MinValue, DateAdp_.unixtime_lcl_ms_(apiFil.lastModified())); + rv.ReadOnly_(!apiFil.canWrite()); + return rv; + } + IoItmDir QueryMkr_dir(IoUrlInfo urlInfo, File apiDir) { + Io_url dirUrl = Io_url_.new_inf_(apiDir.getPath() + urlInfo.DirSpr(), urlInfo); // NOTE: may throw PathTooLongException when url is > 248 (exception messages states 260) + return IoItmDir_.scan_(dirUrl); + } + @Override public IoItmFil QueryFil(Io_url url) { + File fil = new File(url.Xto_api()); + return QueryMkr_fil(url.Info(), fil); + } + @Override public void UpdateFilAttrib(Io_url url, IoItmAttrib atr) { + File f = new File(url.Xto_api()); + boolean rv = true; + if (atr.ReadOnly() != Fil_ReadOnly(f)) { + if (atr.ReadOnly()) + rv = f.setReadOnly(); + else { + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetWritable(f, true); + } + if (!rv) throw Err_.new_key_(IoEngineArgs._.Err_IoException, "set file attribute failed") + .Add("attribute", "readOnly").Add("cur", Fil_ReadOnly(f)).Add("new", atr.ReadOnly()).Add("url", url.Xto_api()); + } + if (atr.Hidden() != f.isHidden()) { + //Runtime.getRuntime().exec("attrib +H myHiddenFile.java"); + } + } + @Override public void UpdateFilModifiedTime(Io_url url, DateAdp modified) { + File f = new File(url.Xto_api()); + long timeInt = modified.UnderDateTime().getTimeInMillis(); +// if (timeInt < 0) { +// UsrDlg_._.Notify("{0} {1}", url.Xto_api(), timeInt); +// return; +// } + if (!f.setLastModified(timeInt)) { + if (Fil_ReadOnly(f)) { + boolean success = false; + try { + UpdateFilAttrib(url, IoItmAttrib.normal_()); + success = f.setLastModified(timeInt); + } + finally { + UpdateFilAttrib(url, IoItmAttrib.readOnly_()); + } + if (!success) throw Err_.new_("could not update file modified time").Add("url", url.Xto_api()).Add("modifiedTime", modified.XtoStr_gplx_long()); + } + } + } + @Override public IoStream OpenStreamRead(Io_url url) {return IoStream_base.new_(url, IoStream_.Mode_rdr);} + @Override public IoStream OpenStreamWrite(IoEngine_xrg_openWrite args) { + Io_url url = args.Url(); + if (!ExistsFil_api(url)) SaveFilText_api(IoEngine_xrg_saveFilStr.new_(url, "")); + return IoStream_base.new_(url, args.Mode()); + } + @SuppressWarnings("resource") + @Override public void CopyFil(IoEngine_xrg_xferFil args) { + // TODO:JAVA6 hidden property ignored; 1.6 does not allow OS-independent way of setting isHidden (wnt only possible through jni) + boolean overwrite = args.Overwrite(); + Io_url srcUrl = args.Src(), trgUrl = args.Trg(); + File srcFil = new File(srcUrl.Xto_api()), trgFil = new File(trgUrl.Xto_api()); + if (trgFil.isFile()) { // trgFil exists; check if overwrite set and trgFil is writable + Chk_TrgFil_Overwrite(overwrite, trgUrl); + MarkFileWritable(trgFil, trgUrl, args.ReadOnlyFails(), "copy"); + } + else { // trgFil doesn't exist; must create file first else fileNotFound exception thrown +// if (overwrite) throw Err_ + boolean rv = true; //Exception exc = null; + if (!ExistsDir(trgUrl.OwnerDir())) CreateDir(trgUrl.OwnerDir()); + try { + trgFil.createNewFile(); + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetExecutable(trgFil, true); + } + catch (IOException e) { +// exc = e; + rv = false; + } + if (!rv) + throw Err_.new_("create file failed").Add("trg", trgUrl.Xto_api()); + } + FileInputStream srcStream = null; FileOutputStream trgStream = null; + FileChannel srcChannel = null, trgChannel = null; + try { + // make objects + try {srcStream = new FileInputStream(srcFil);} + catch (FileNotFoundException e) {throw IoErr.FileNotFound("copy", srcUrl);} + try {trgStream = new FileOutputStream(trgFil);} + catch (FileNotFoundException e) { + trgStream = TryToUnHideFile(trgFil, trgUrl); + if (trgStream == null) + throw IoErr.FileNotFound("copy", trgUrl); +// else +// wasHidden = true; + } + srcChannel = srcStream.getChannel(); + trgChannel = trgStream.getChannel(); + + // transfer data + long pos = 0, count = 0, read = 0; + try {count = srcChannel.size();} + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "size failed").Add("src", srcUrl.Xto_api());} + int totalBufferSize = IoEngineArgs._.LoadFilStr_BufferSize; + long transferSize = (count > totalBufferSize) ? totalBufferSize : count; // transfer as much as fileSize, but limit to LoadFilStr_BufferSize + while (pos < count) { + try {read = trgChannel.transferFrom(srcChannel, pos, transferSize);} + catch (IOException e) { + Closeable_Close(srcChannel, srcUrl, false); + Closeable_Close(trgChannel, trgUrl, false); + Closeable_Close(srcStream, srcUrl, false); + Closeable_Close(trgStream, srcUrl, false); + throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "transfer data failed").Add("src", srcUrl.Xto_api()).Add("trg", trgUrl.Xto_api()); + } + if (read == -1) break; + pos += read; + } +// if (wasHidden) +// + } + finally { + // cleanup + Closeable_Close(srcChannel, srcUrl, false); + Closeable_Close(trgChannel, trgUrl, false); + Closeable_Close(srcStream, srcUrl, false); + Closeable_Close(trgStream, srcUrl, false); + } + UpdateFilModifiedTime(trgUrl, QueryFil(srcUrl).ModifiedTime()); // must happen after file is closed + } + FileOutputStream TryToUnHideFile(File trgFil, Io_url trgUrl) { + FileOutputStream trgStream = null; + if (trgFil.exists()) { // WORKAROUND: java fails when writing to hidden files; unmark hidden and try again + Process p = null; + try { + String d = "attrib -H \"" + trgUrl.Xto_api() + "\""; + p = Runtime.getRuntime().exec(d); + } catch (IOException e1) { + e1.printStackTrace(); + } + try { + p.waitFor(); + } catch (InterruptedException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + try {trgStream = new FileOutputStream(trgFil);} + catch (FileNotFoundException e) { + return null; + } + } + return trgStream; + } + @Override public void MoveFil(IoEngine_xrg_xferFil args) { + Io_url srcUrl = args.Src(), trgUrl = args.Trg(); + String src_api = srcUrl.Xto_api(), trg_api = trgUrl.Xto_api(); + if (String_.Eq(src_api, trg_api)) return; // ignore command if src and trg is same; EX: C:\a.txt -> C:\a.txt should be noop + File srcFil = new File(src_api), trgFil = new File(trg_api); + + // if drive is same, then rename file + if (String_.Eq(srcUrl.OwnerRoot().Raw(), trgUrl.OwnerRoot().Raw())) { + boolean overwrite = args.Overwrite(); + if (!srcFil.exists() && args.MissingFails()) throw IoErr.FileNotFound("move", srcUrl); + if (trgFil.exists()) { + Chk_TrgFil_Overwrite(overwrite, trgUrl); + MarkFileWritable(trgFil, trgUrl, args.ReadOnlyFails(), "move"); + DeleteFil_lang(trgFil, args.Trg()); // overwrite is specified and file is writable -> delete + } + if (!ExistsDir(trgUrl.OwnerDir())) CreateDir(trgUrl.OwnerDir()); + srcFil.renameTo(trgFil); + } + // else copy fil and delete + else { + if (!srcFil.exists() && !args.MissingFails()) return; + CopyFil(args); + DeleteFil_lang(srcFil, srcUrl); + } + } + void Chk_TrgFil_Overwrite(boolean overwrite, Io_url trg) { + if (!overwrite) + throw Err_.invalid_op_("trgFile exists but overwriteFlag not set").Add("trg", trg.Xto_api()); + } + @Override public void MoveDir(Io_url src, Io_url trg) { + String srcStr = src.Xto_api(), trgStr = trg.Xto_api(); + File srcFil = new File(srcStr), trgFil = new File(trgStr); + if (trgFil.exists()) {throw Err_.invalid_op_("cannot move dir if trg exists").Add("src", src).Add("trg", trg);} + if (String_.Eq(src.OwnerRoot().Raw(), trg.OwnerRoot().Raw())) { + srcFil.renameTo(trgFil); + } + else { + XferDir(IoEngine_xrg_xferDir.copy_(src, trg)); + } + } + protected static void Closeable_Close(Closeable closeable, Io_url url, boolean throwErr) { + if (closeable == null) return; + try {closeable.close();} + catch (IOException e) { + if (throwErr) + throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "close object failed").Add("class", ClassAdp_.NameOf_obj(closeable)).Add("url", url.Xto_api()); +// else +// UsrDlg_._.Finally("failed to close FileChannel", "url", url, "apiErr", Err_.Message_err_arg(e)); + } + } + + File Fil_(Io_url url) {return new File(url.Xto_api());} + boolean Fil_Exists(File fil) {return fil.exists();} + boolean Fil_ReadOnly(File fil) {return !fil.canWrite();} + boolean Fil_Delete(File fil) {return fil.delete();} + void Fil_Writable(File fil) { + if (!Op_sys.Cur().Tid_is_drd()) + IoEngine_system_xtn.SetWritable(fil, true); + } + Err Err_Text_UnsupportedEncoding(String encodingName, String text, Io_url url, Exception e) { + return Err_.err_key_(e, "gplx.texts.UnsupportedEncodingException", "text is in unsupported encoding").CallLevel_1_() + .Add("encodingName", encodingName) + .Add("text", text) + .Add("url", url.Xto_api()) + ; + } + boolean user_agent_needs_resetting = true; + @Override public Io_stream_rdr DownloadFil_as_rdr(IoEngine_xrg_downloadFil xrg) { + Io_stream_rdr_http rdr = new Io_stream_rdr_http(xrg); + rdr.Open(); + return rdr; + } + @Override public boolean DownloadFil(IoEngine_xrg_downloadFil xrg) { + IoStream trg_stream = null; + java.io.BufferedInputStream src_stream = null; + java.net.URL src_url = null; + HttpURLConnection src_conn = null; + if (user_agent_needs_resetting) {user_agent_needs_resetting = false; System.setProperty("http.agent", "");} + boolean exists = Io_mgr._.ExistsDir(xrg.Trg().OwnerDir()); + Gfo_usr_dlg prog_dlg = null; + String src_str = xrg.Src(); + Io_download_fmt xfer_fmt = xrg.Download_fmt(); + prog_dlg = xfer_fmt.usr_dlg; + if (!Web_access_enabled) { + if (session_fil == null) session_fil = prog_dlg.Log_wtr().Session_dir().GenSubFil("internet.txt"); + if (prog_dlg != null) prog_dlg.Log_wtr().Log_msg_to_url_fmt(session_fil, "download disabled: src='~{0}' trg='~{1}'", xrg.Src(), xrg.Trg().Raw()); + return false; + } + try { + trg_stream = Io_mgr._.OpenStreamWrite(xrg.Trg()); + src_url = new java.net.URL(src_str); + src_conn = (HttpURLConnection)src_url.openConnection(); +// src_conn.setReadTimeout(5000); // do not set; if file does not exist, will wait 5 seconds before timing out; want to fail immediately + String user_agent = xrg.User_agent(); if (user_agent != null) src_conn.setRequestProperty("User-Agent", user_agent); + long content_length = Long_.parse_or_(src_conn.getHeaderField("Content-Length"), IoItmFil.Size_Invalid_int); + xrg.Src_content_length_(content_length); + if (xrg.Src_last_modified_query()) // NOTE: only files will have last modified (api calls will not); if no last_modified, then src_conn will throw get nullRef; avoid nullRef + xrg.Src_last_modified_(DateAdp_.unixtime_lcl_ms_(src_conn.getLastModified())); + if (xrg.Exec_meta_only()) return true; + src_stream = new java.io.BufferedInputStream(src_conn.getInputStream()); + if (!exists) { + Io_mgr._.CreateDir(xrg.Trg().OwnerDir()); // dir must exist for OpenStreamWrite; create dir at last possible moment in case stream does not exist. + } + byte[] download_bfr = new byte[Download_bfr_len]; // NOTE: download_bfr was originally member variable; DATE:2013-05-03 + xfer_fmt.Bgn(content_length); + int count = 0; + while ((count = src_stream.read(download_bfr, 0, Download_bfr_len)) != -1) { + if (xrg.Prog_cancel()) { + src_stream.close(); + trg_stream.Rls(); + Io_mgr._.DeleteFil(xrg.Trg()); + } + xfer_fmt.Prog(count); + trg_stream.Write(download_bfr, 0, count); + } + if (prog_dlg != null) { + xfer_fmt.Term(); + if (session_fil == null) session_fil = prog_dlg.Log_wtr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wtr().Log_msg_to_url_fmt(session_fil, "download pass: src='~{0}' trg='~{1}'", src_str, xrg.Trg().Raw()); + } + return true; + } + catch (Exception exc) { + xrg.Rslt_err_(exc); + if (ClassAdp_.Eq_typeSafe(exc, java.net.UnknownHostException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_host_not_found); + else if (ClassAdp_.Eq_typeSafe(exc, java.io.FileNotFoundException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + else xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_unknown); + if (prog_dlg != null && !xrg.Prog_cancel()) { + if (session_fil == null) session_fil = prog_dlg.Log_wtr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wtr().Log_msg_to_url_fmt(session_fil, "download fail: src='~{0}' trg='~{1}' error='~{2}'", src_str, xrg.Trg().Raw(), Err_.Message_lang(exc)); + } + if (trg_stream != null) { + try { + trg_stream.Rls(); + DeleteFil_api(IoEngine_xrg_deleteFil.new_(xrg.Trg())); + } + catch (Exception e2) {Err_.Noop(e2);} + } + return false; + } + finally { + xrg.Prog_running_(false); + try { + if (src_stream != null) src_stream.close(); + if (src_conn != null) src_conn.disconnect(); + } catch (Exception exc) { + Err_.Noop(exc); + } + if (trg_stream != null) trg_stream.Rls(); + } + } Io_url session_fil; Bry_bfr prog_fmt_bfr; + byte[] download_bfr; static final int Download_bfr_len = Io_mgr.Len_kb * 128; + public static Err Err_Fil_NotFound(Io_url url) { + return Err_.new_key_(IoEngineArgs._.Err_FileNotFound, "file not found").Add("url", url.Xto_api()).CallLevel_1_(); + } + public static Err Err_Fil_NotFound(Exception e, Io_url url) { + return Err_.err_(e, "file not found").Key_(IoEngineArgs._.Err_FileNotFound).Add("url", url.Xto_api()).CallLevel_1_(); + } + void MarkFileWritable(File fil, Io_url url, boolean readOnlyFails, String op) { + if (Fil_ReadOnly(fil)) { + if (readOnlyFails) // NOTE: java will always allow final files to be deleted; programmer api is responsible for check + throw Err_.new_key_(IoEngineArgs._.Err_ReadonlyFileNotWritable, "writable operation attempted on readOnly file").Add("op", op).Add("url", url.Xto_api()).CallLevel_1_(); + else + Fil_Writable(fil); + } + } + void DeleteFil_lang(File fil, Io_url url) { + boolean rv = Fil_Delete(fil); + if (!rv) + throw Err_.new_key_(IoEngineArgs._.Err_IoException, "file not deleted").Add("url", url.Xto_api()); + } + IoEngineUtl utl = IoEngineUtl.new_(); + public static IoEngine_system new_() {return new IoEngine_system();} IoEngine_system() {} + static final String GRP_KEY = "Io_engine"; + public static boolean Web_access_enabled = true; +} +class IoEngineArgs { + public int LoadFilStr_BufferSize = 4096 * 256; + public String LoadFilStr_Encoding = "UTF-8"; + public String Err_ReadonlyFileNotWritable = "gplx.ios.ReadonlyFileNotWritable"; + public String Err_FileNotFound = "gplx.ios.FileNotFound"; + public String Err_IoException = "gplx.ios.IoException"; + public static final IoEngineArgs _ = new IoEngineArgs(); +} +class IoEngine_system_xtn { + // PATCH.DROID:VerifyError if file.setExecutable is referenced directly in IoEngine_system. However, if placed in separate class + public static void SetExecutable(java.io.File file, boolean v) {file.setExecutable(v);} + public static void SetWritable(java.io.File file, boolean v) {file.setWritable(v);} +} +class Io_download_http { + public static boolean User_agent_reset_needed = true; + public static void User_agent_reset() { + User_agent_reset_needed = false; + System.setProperty("http.agent", ""); // need to set http.agent to '' in order for "User-agent" to take effect + } + public static void Save_to_fsys(IoEngine_xrg_downloadFil xrg) { + Io_stream_rdr_http rdr = new Io_stream_rdr_http(xrg); + IoStream trg_stream = null; + try { + boolean exists = Io_mgr._.ExistsDir(xrg.Trg().OwnerDir()); + if (!exists) + Io_mgr._.CreateDir(xrg.Trg().OwnerDir()); // dir must exist for OpenStreamWrite; create dir at last possible moment in case stream does not exist. + trg_stream = Io_mgr._.OpenStreamWrite(xrg.Trg()); + byte[] bfr = new byte[Download_bfr_len]; + rdr.Open(); + while (rdr.Read(bfr, 0, Download_bfr_len) != Read_done) { + } + } + finally { + rdr.Rls(); + if (trg_stream != null) trg_stream.Rls(); + } + if (xrg.Rslt() != IoEngine_xrg_downloadFil.Rslt_pass) + Io_mgr._.DeleteFil_args(xrg.Trg()).MissingFails_off().Exec(); + } + public static final int Read_done = -1; + public static final int Download_bfr_len = Io_mgr.Len_kb * 128; +} +class Io_stream_rdr_http implements Io_stream_rdr { + public Io_stream_rdr_http(IoEngine_xrg_downloadFil xrg) { + this.xrg = xrg; + } private IoEngine_xrg_downloadFil xrg; + public byte Tid() {return Io_stream_.Tid_file;} + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {url = v; return this;} private Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = IoItmFil.Size_Invalid; // NOTE: must default size to -1; DATE:2014-06-21 + private String src_str; private HttpURLConnection src_conn; private java.io.BufferedInputStream src_stream; + private Io_download_fmt xfer_fmt; private Gfo_usr_dlg prog_dlg; + private boolean read_done = true, read_failed = false; + public Io_stream_rdr Open() { + if (Io_download_http.User_agent_reset_needed) Io_download_http.User_agent_reset(); + if (!IoEngine_system.Web_access_enabled) { + read_done = read_failed = true; + if (prog_dlg != null) + prog_dlg.Log_wtr().Log_msg_to_url_fmt(session_fil, "download disabled: src='~{0}' trg='~{1}'", xrg.Src(), xrg.Trg().Raw()); + return this; + } + src_str = xrg.Src(); + xfer_fmt = xrg.Download_fmt(); prog_dlg = xfer_fmt.usr_dlg; + try { + src_conn = (HttpURLConnection)new java.net.URL(src_str).openConnection(); + String user_agent = xrg.User_agent(); + if (user_agent != null) + src_conn.setRequestProperty("User-Agent", user_agent); +// src_conn.setReadTimeout(5000); // do not set; if file does not exist, will wait 5 seconds before timing out; want to fail immediately + long content_length = Long_.parse_or_(src_conn.getHeaderField("Content-Length"), IoItmFil.Size_Invalid_int); + xrg.Src_content_length_(content_length); + this.len = content_length; + if (xrg.Src_last_modified_query()) // NOTE: only files will have last modified (api calls will not); if no last_modified, then src_conn will throw get nullRef; avoid nullRef + xrg.Src_last_modified_(DateAdp_.unixtime_lcl_ms_(src_conn.getLastModified())); + if (xrg.Exec_meta_only()) { + read_done = true; + return this; + } + read_done = false; + src_stream = new java.io.BufferedInputStream(src_conn.getInputStream()); + xfer_fmt.Bgn(content_length); + } + catch (Exception e) {Err_handle(e);} + return this; + } + public void Open_mem(byte[] v) {} + public Object Under() {return src_stream;} + public int Read(byte[] bry, int bgn, int len) { + if (read_done) return Io_download_http.Read_done; + if (xrg.Prog_cancel()) {read_failed = true; return Io_download_http.Read_done;} + try { + int read = src_stream.read(bry, bgn, len); + xfer_fmt.Prog(read); + return read; + } + catch (Exception e) { + Err_handle(e); + return Io_download_http.Read_done; + } + } + private Io_url session_fil = null; + private boolean rls_done = false; + public long Skip(long len) {return 0;} + public void Rls() { + if (rls_done) return; + try { + read_done = true; + if (prog_dlg != null) { + xfer_fmt.Term(); + } + if (session_fil == null && prog_dlg != null) session_fil = prog_dlg.Log_wtr().Session_dir().GenSubFil("internet.txt"); + if (read_failed) { + } + else { + prog_dlg.Log_wtr().Log_msg_to_url_fmt(session_fil, "download pass: src='~{0}' trg='~{1}'", src_str, xrg.Trg().Raw()); + xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_pass); + } + xrg.Prog_running_(false); + } + catch (Exception e) {Err_.Noop(e);} // ignore close errors; also Err_handle calls Rls() so it would be circular + finally { + try {if (src_stream != null) src_stream.close();} + catch (Exception e) {Err_.Noop(e);} // ignore failures when cleaning up + if (src_conn != null) src_conn.disconnect(); + src_stream = null; + src_conn = null; + rls_done = true; + } + } + private void Err_handle(Exception exc) { + read_done = read_failed = true; + len = -1; + xrg.Rslt_err_(exc); + if (ClassAdp_.Eq_typeSafe(exc, java.net.UnknownHostException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_host_not_found); + else if (ClassAdp_.Eq_typeSafe(exc, java.io.FileNotFoundException.class)) xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_file_not_found); + else xrg.Rslt_(IoEngine_xrg_downloadFil.Rslt_fail_unknown); + if (prog_dlg != null && !xrg.Prog_cancel()) { + if (session_fil == null) session_fil = prog_dlg.Log_wtr().Session_dir().GenSubFil("internet.txt"); + prog_dlg.Log_wtr().Log_msg_to_url_fmt(session_fil, "download fail: src='~{0}' trg='~{1}' error='~{2}'", src_str, xrg.Trg().Raw(), Err_.Message_lang(exc)); + } + this.Rls(); + } +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_deleteDir.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_deleteDir.java new file mode 100644 index 000000000..4a2cd381f --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_deleteDir.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import gplx.criterias.*; +public class IoEngine_xrg_deleteDir { + public Io_url Url() {return url;} public IoEngine_xrg_deleteDir Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean Recur() {return recur;} public IoEngine_xrg_deleteDir Recur_() {return Recur_(true);} public IoEngine_xrg_deleteDir Recur_(boolean v) {recur = v; return this;} private boolean recur = false; + public boolean ReadOnlyFails() {return readOnlyFails;} public IoEngine_xrg_deleteDir ReadOnlyFails_off() {return ReadOnlyFails_(false);} public IoEngine_xrg_deleteDir ReadOnlyFails_(boolean v) {readOnlyFails = v; return this;} private boolean readOnlyFails = true; + public boolean MissingIgnored() {return missingIgnored;} public IoEngine_xrg_deleteDir MissingIgnored_() {return MissingIgnored_(true);} public IoEngine_xrg_deleteDir MissingIgnored_(boolean v) {missingIgnored = v; return this;} private boolean missingIgnored = true; + public Criteria MatchCrt() {return matchCrt;} public IoEngine_xrg_deleteDir MatchCrt_(Criteria v) {matchCrt = v; return this;} Criteria matchCrt = Criteria_.All; + public Criteria SubDirScanCrt() {return subDirScanCrt;} public IoEngine_xrg_deleteDir SubDirScanCrt_(Criteria v) {subDirScanCrt = v; return this;} Criteria subDirScanCrt = Criteria_.All; + public ConsoleDlg UsrDlg() {return usrDlg;} public IoEngine_xrg_deleteDir UsrDlg_(ConsoleDlg v) {usrDlg = v; return this;} ConsoleDlg usrDlg = ConsoleDlg_.Null; + public void Exec() {IoEnginePool._.Fetch(url.Info().EngineKey()).DeleteDirDeep(this);} + public static IoEngine_xrg_deleteDir new_(Io_url url) { + IoEngine_xrg_deleteDir rv = new IoEngine_xrg_deleteDir(); + rv.url = url; + return rv; + } IoEngine_xrg_deleteDir() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_deleteFil.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_deleteFil.java new file mode 100644 index 000000000..9358d3eb2 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_deleteFil.java @@ -0,0 +1,30 @@ +/* +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.ios; import gplx.*; +public class IoEngine_xrg_deleteFil extends IoEngine_xrg_fil_affects1_base { + @gplx.New public IoEngine_xrg_deleteFil Url_(Io_url val) {Url_set(val); return this;} + public IoEngine_xrg_deleteFil ReadOnlyFails_off() {return ReadOnlyFails_(false);} public IoEngine_xrg_deleteFil ReadOnlyFails_(boolean v) {ReadOnlyFails_set(v); return this;} + public IoEngine_xrg_deleteFil MissingFails_off() {return MissingFails_(false);} public IoEngine_xrg_deleteFil MissingFails_(boolean v) {MissingFails_set(v); return this;} + @Override public void Exec() {IoEnginePool._.Fetch(this.Url().Info().EngineKey()).DeleteFil_api(this);} + public static IoEngine_xrg_deleteFil proto_() {return new IoEngine_xrg_deleteFil();} + public static IoEngine_xrg_deleteFil new_(Io_url url) { + IoEngine_xrg_deleteFil rv = new IoEngine_xrg_deleteFil(); + rv.Url_set(url); + return rv; + } IoEngine_xrg_deleteFil() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_downloadFil.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_downloadFil.java new file mode 100644 index 000000000..d55cc1821 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_downloadFil.java @@ -0,0 +1,66 @@ +/* +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.ios; import gplx.*; +public class IoEngine_xrg_downloadFil { + public String Src() {return src;} public IoEngine_xrg_downloadFil Src_(String v) {src = v; return this;} private String src; + public Io_url Trg() {return trg;} public IoEngine_xrg_downloadFil Trg_(Io_url v) {trg = v; return this;} Io_url trg; + public byte Rslt() {return rslt;} public IoEngine_xrg_downloadFil Rslt_(byte v) {rslt = v; return this;} private byte rslt = Rslt_pass; + public Exception Rslt_err() {return rslt_err;} public IoEngine_xrg_downloadFil Rslt_err_(Exception v) {rslt_err = v; return this;} Exception rslt_err; + public String User_agent() {return user_agent;} public IoEngine_xrg_downloadFil User_agent_(String v) {user_agent = v; return this;} private String user_agent; + public Gfo_usr_dlg Prog_dlg() {return prog_dlg;} public IoEngine_xrg_downloadFil Prog_dlg_(Gfo_usr_dlg v) {prog_dlg = v; download_fmt.Ctor(prog_dlg); return this;} Gfo_usr_dlg prog_dlg; + public Bry_fmtr Prog_fmtr() {return prog_fmtr;} Bry_fmtr prog_fmtr = Bry_fmtr.new_("~{download_header}: ~{download_read} of ~{download_length} kb;", "download_header", "download_url", "download_read", "download_length"); + public String Prog_fmt_hdr() {return prog_fmt_hdr;} public IoEngine_xrg_downloadFil Prog_fmt_hdr_(String v) {prog_fmt_hdr = v; return this;} private String prog_fmt_hdr = ""; // NOTE: must init to "", else null ref when building String + public boolean Prog_cancel() {return prog_cancel;} public IoEngine_xrg_downloadFil Prog_cancel_y_() {prog_cancel = true; return this;} volatile boolean prog_cancel; + public boolean Prog_running() {return prog_running;} public IoEngine_xrg_downloadFil Prog_running_(boolean v) {prog_running = v; return this;} private boolean prog_running; + public long Src_content_length() {return src_content_length;} public IoEngine_xrg_downloadFil Src_content_length_(long v) {src_content_length = v; return this;} long src_content_length; + public DateAdp Src_last_modified() {return src_last_modified;} public IoEngine_xrg_downloadFil Src_last_modified_(DateAdp v) {src_last_modified = v; return this;} DateAdp src_last_modified; + public boolean Src_last_modified_query() {return src_last_modified_query;} public IoEngine_xrg_downloadFil Src_last_modified_query_(boolean v) {src_last_modified_query = v; return this;} private boolean src_last_modified_query; + public String Trg_engine_key() {return trg_engine_key;} public IoEngine_xrg_downloadFil Trg_engine_key_(String v) {trg_engine_key = v; return this;} private String trg_engine_key = IoEngine_.SysKey; + public Io_download_fmt Download_fmt() {return download_fmt;} Io_download_fmt download_fmt = new Io_download_fmt(); + public boolean Exec() {return IoEnginePool._.Fetch(trg.Info().EngineKey()).DownloadFil(this);} + public Io_stream_rdr Exec_as_rdr() {return IoEnginePool._.Fetch(IoEngine_.SysKey).DownloadFil_as_rdr(this);} + public boolean Exec_meta_only() {return exec_meta_only;} private boolean exec_meta_only; + public byte[] Exec_as_bry(String src) { + this.Src_(src); this.Trg_(trg_mem); + download_fmt.Init(src, prog_fmt_hdr); // NOTE: must set src else NULL error + boolean pass = IoEnginePool._.Fetch(trg_engine_key).DownloadFil(this); + return pass ? Io_mgr._.LoadFilBry(trg_mem) : null; + } Io_url trg_mem = Io_url_.mem_fil_("mem/download.tmp"); + public boolean Exec_meta(String src) { + this.Src_(src); this.Trg_(trg_mem); // NOTE: set Trg_ else error in download proc + download_fmt.Init(src, prog_fmt_hdr); // NOTE: must set src else NULL error + exec_meta_only = true; + boolean rv = IoEnginePool._.Fetch(trg_engine_key).DownloadFil(this); + exec_meta_only = false; + return rv; + } + public void Init(String src, Io_url trg) { + this.src = src; this.trg = trg; + prog_cancel = false; + rslt_err = null; + rslt = Rslt_pass; + prog_running = true; + download_fmt.Init(src, "downloading ~{src_name}: ~{prog_left} left (@ ~{prog_rate}); ~{prog_done} of ~{src_len} (~{prog_pct}%)"); + } + public static IoEngine_xrg_downloadFil new_(String src, Io_url trg) { + IoEngine_xrg_downloadFil rv = new IoEngine_xrg_downloadFil(); + rv.src = src; rv.trg = trg; + return rv; + } IoEngine_xrg_downloadFil() {} + public static final byte Rslt_pass = 0, Rslt_fail_host_not_found = 1, Rslt_fail_file_not_found = 2, Rslt_fail_unknown = 3; +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_fil_affects1_base.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_fil_affects1_base.java new file mode 100644 index 000000000..0122b645c --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_fil_affects1_base.java @@ -0,0 +1,25 @@ +/* +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.ios; import gplx.*; +public class IoEngine_xrg_fil_affects1_base { + public Io_url Url() {return url;} public void Url_set(Io_url v) {url = v;} Io_url url; + public IoEngine_xrg_fil_affects1_base Url_(Io_url v) {url = v; return this;} + public boolean MissingFails() {return missingFails;} public void MissingFails_set(boolean v) {missingFails = v;} private boolean missingFails = true; + public boolean ReadOnlyFails() {return readOnlyFails;} public void ReadOnlyFails_set(boolean v) {readOnlyFails = v;} private boolean readOnlyFails = true; + @gplx.Virtual public void Exec() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_loadFilStr.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_loadFilStr.java new file mode 100644 index 000000000..37b1a57a8 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_loadFilStr.java @@ -0,0 +1,44 @@ +/* +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.ios; import gplx.*; +import gplx.texts.*; +public class IoEngine_xrg_loadFilStr { + public Io_url Url() {return url;} public IoEngine_xrg_loadFilStr Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean MissingIgnored() {return missingIgnored;} public IoEngine_xrg_loadFilStr MissingIgnored_() {return MissingIgnored_(true);} public IoEngine_xrg_loadFilStr MissingIgnored_(boolean v) {missingIgnored = v; return this;} private boolean missingIgnored = false; + public boolean BomUtf8Convert() {return bomUtf8Convert;} public IoEngine_xrg_loadFilStr BomUtf8Convert_(boolean v) {bomUtf8Convert = v; return this;} private boolean bomUtf8Convert = true; + public String Exec() { + String s = IoEnginePool._.Fetch(url.Info().EngineKey()).LoadFilStr(this); + if (bomUtf8Convert && String_.Len(s) > 0 && String_.CodePointAt(s, 0) == Bom_Utf8) { + s = String_.Mid(s, 1); + UsrDlg_._.Warn(UsrMsg.new_("UTF8 BOM removed").Add("url", url.Xto_api())); + } + return s; + } + public String[] ExecAsStrAry() {return String_.Split(Exec(), String_.CrLf);} + public String[] ExecAsStrAryLnx() { + String raw = Exec(); + if (String_.Len(raw) == 0) return String_.Ary_empty; + return String_.Split(raw, Op_sys.Dir_spr_char_lnx, false); + } + int Bom_Utf8 = 65279; // U+FEFF; see http://en.wikipedia.org/wiki/Byte_order_mark + public static IoEngine_xrg_loadFilStr new_(Io_url url) { + IoEngine_xrg_loadFilStr rv = new IoEngine_xrg_loadFilStr(); + rv.url = url; + return rv; + } IoEngine_xrg_loadFilStr() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_openRead.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_openRead.java new file mode 100644 index 000000000..9ceadffca --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_openRead.java @@ -0,0 +1,35 @@ +/* +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.ios; import gplx.*; +public class IoEngine_xrg_openRead { + public Io_url Url() {return url;} Io_url url; + public String ErrMsg() {return errMsg;} private String errMsg; + public IoStream ExecAsIoStreamOrFail() {return IoEnginePool._.Fetch(url.Info().EngineKey()).OpenStreamRead(url);} + public IoStream ExecAsIoStreamOrNull() { + try {return IoEnginePool._.Fetch(url.Info().EngineKey()).OpenStreamRead(url);} + catch (Exception exc) { + errMsg = Err_.Message_lang(exc); + return IoStream_.Null; + } + } + public static IoEngine_xrg_openRead new_(Io_url url) { + IoEngine_xrg_openRead rv = new IoEngine_xrg_openRead(); + rv.url = url; + return rv; + } IoEngine_xrg_openRead() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_openWrite.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_openWrite.java new file mode 100644 index 000000000..3240012a7 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_openWrite.java @@ -0,0 +1,31 @@ +/* +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.ios; import gplx.*; +public class IoEngine_xrg_openWrite { + public Io_url Url() {return url;} public IoEngine_xrg_openWrite Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean ReadOnlyIgnored() {return readOnlyIgnored;} public IoEngine_xrg_openWrite ReadOnlyIgnored_() {return ReadOnlyIgnored_(true);} public IoEngine_xrg_openWrite ReadOnlyIgnored_(boolean v) {readOnlyIgnored = v; return this;} private boolean readOnlyIgnored = false; + public boolean MissingIgnored() {return missingIgnored;} public IoEngine_xrg_openWrite MissingIgnored_() {return MissingIgnored_(true);} public IoEngine_xrg_openWrite MissingIgnored_(boolean v) {missingIgnored = v; return this;} private boolean missingIgnored = false; + public byte Mode() {return mode;} public IoEngine_xrg_openWrite Mode_(byte v) {mode = v; return this;} private byte mode = IoStream_.Mode_wtr_create; + public IoEngine_xrg_openWrite Mode_update_() {return Mode_(IoStream_.Mode_wtr_update);} + public IoStream Exec() {return IoEnginePool._.Fetch(url.Info().EngineKey()).OpenStreamWrite(this);} + public static IoEngine_xrg_openWrite new_(Io_url url) { + IoEngine_xrg_openWrite rv = new IoEngine_xrg_openWrite(); + rv.url = url; + return rv; + } IoEngine_xrg_openWrite() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_queryDir.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_queryDir.java new file mode 100644 index 000000000..b3e7357c4 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_queryDir.java @@ -0,0 +1,55 @@ +/* +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.ios; import gplx.*; +import gplx.criterias.*; +public class IoEngine_xrg_queryDir { + public Io_url Url() {return url;} public IoEngine_xrg_queryDir Url_(Io_url val) {url = val; return this;} Io_url url; + public boolean Recur() {return recur;} public IoEngine_xrg_queryDir Recur_() {return Recur_(true);} public IoEngine_xrg_queryDir Recur_(boolean val) {recur = val; return this;} private boolean recur = false; + public boolean DirInclude() {return dirInclude;} public IoEngine_xrg_queryDir DirInclude_() {return DirInclude_(true);} public IoEngine_xrg_queryDir DirInclude_(boolean val) {dirInclude = val; return this;} private boolean dirInclude = false; + public Criteria FilCrt() {return filCrt;} public IoEngine_xrg_queryDir FilCrt_(Criteria val) {filCrt = val; return this;} Criteria filCrt; + public Criteria DirCrt() {return dirCrt;} public IoEngine_xrg_queryDir DirCrt_(Criteria val) {dirCrt = val; return this;} Criteria dirCrt; + public Criteria SubDirScanCrt() {return subDirScanCrt;} public IoEngine_xrg_queryDir SubDirScanCrt_(Criteria val) {subDirScanCrt = val; return this;} Criteria subDirScanCrt; + public IoEngine_xrg_queryDir DirOnly_() { + DirInclude_(true); + filCrt = Criteria_.None; + return this; + } + + public ConsoleDlg UsrDlg() {return usrDlg;} public IoEngine_xrg_queryDir UsrDlg_(ConsoleDlg val) {usrDlg = val; return this;} ConsoleDlg usrDlg = ConsoleDlg_.Null; + public IoEngine_xrg_queryDir FilPath_(String val) { + Criteria_ioMatch crt = Criteria_ioMatch.parse_(true, val, url.Info().CaseSensitive()); + filCrt = Criteria_wrapper.new_(IoItm_base_.Prop_Path, crt); + return this; + } + public IoItmDir ExecAsDir() {return IoEnginePool._.Fetch(url.Info().EngineKey()).QueryDirDeep(this);} + public Io_url[] ExecAsUrlAry() {return ExecAsItmHash().XtoIoUrlAry();} + public IoItmHash ExecAsItmHash() { + Criteria crt = dirInclude ? Criteria_.All : Criteria_wrapper.new_(IoItm_base_.Prop_Type, Criteria_.eq_(IoItmFil.Type_Fil)); + IoItmHash list = ExecAsDir().XtoIoItmList(crt); + list.SortBy(IoItmBase_comparer_nest._); + return list; + } + public static IoEngine_xrg_queryDir new_(Io_url url) { + IoEngine_xrg_queryDir rv = new IoEngine_xrg_queryDir(); + rv.url = url; + rv.filCrt = Criteria_wrapper.new_(IoItm_base_.Prop_Path, Criteria_.All); + rv.dirCrt = Criteria_wrapper.new_(IoItm_base_.Prop_Path, Criteria_.All); + rv.subDirScanCrt = Criteria_wrapper.new_(IoItm_base_.Prop_Path, Criteria_.All); + return rv; + } IoEngine_xrg_queryDir() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_recycleFil.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_recycleFil.java new file mode 100644 index 000000000..c63601983 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_recycleFil.java @@ -0,0 +1,58 @@ +/* +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.ios; import gplx.*; +public class IoEngine_xrg_recycleFil extends IoEngine_xrg_fil_affects1_base { + public IoEngine_xrg_recycleFil MissingFails_off() {return MissingFails_(false);} public IoEngine_xrg_recycleFil MissingFails_(boolean v) {MissingFails_set(v); return this;} + + public int Mode() {return mode;} public IoEngine_xrg_recycleFil Mode_(int v) {mode = v; return this;} int mode; + public String AppName() {return appName;} public IoEngine_xrg_recycleFil AppName_(String val) {appName = val; return this;} private String appName = "unknown_app"; + public UuidAdp Uuid() {return uuid;} public IoEngine_xrg_recycleFil Uuid_(UuidAdp val) {uuid = val; return this;} UuidAdp uuid; + public boolean Uuid_include() {return uuid_include;} public IoEngine_xrg_recycleFil Uuid_include_() {uuid_include = true; return this;} private boolean uuid_include; + public DateAdp Time() {return time;} public IoEngine_xrg_recycleFil Time_(DateAdp val) {time = val; return this;} DateAdp time; + public ListAdp RootDirNames() {return rootDirNames;} public IoEngine_xrg_recycleFil RootDirNames_(ListAdp val) {rootDirNames = val; return this;} ListAdp rootDirNames; + public Io_url RecycleUrl() { + String dayName = time.XtoStr_fmt("yyyyMMdd"), timeName = time.XtoStr_fmt("hhmmssfff"); + String rootDirStr = ConcatWith_ary(this.Url().Info().DirSpr(), rootDirNames); + Io_url recycleDir = this.Url().OwnerRoot().GenSubDir_nest(rootDirStr, dayName); + String uuidStr = uuid_include ? uuid.XtoStr() : ""; + return recycleDir.GenSubFil_ary(appName, ";", timeName, ";", uuidStr, ";", String_.LimitToFirst(this.Url().NameAndExt(), 128)); + } + String ConcatWith_ary(String separator, ListAdp ary) { + String_bldr sb = String_bldr_.new_(); + int aryLen = ary.Count(); + for (int i = 0; i < aryLen; i++) { + if (i != 0) sb.Add(separator); + Object val = ary.FetchAt(i); + sb.Add_obj(Object_.XtoStr_OrEmpty(val)); + } + return sb.XtoStr(); + } + @Override public void Exec() { + IoEnginePool._.Fetch(this.Url().Info().EngineKey()).RecycleFil(this); + } + public IoEngine_xrg_recycleFil(int v) { + mode = v; + time = DateAdp_.Now(); + uuid = UuidAdp_.random_(); + rootDirNames = ListAdp_.new_(); rootDirNames.Add("z_trash"); + } + public static IoEngine_xrg_recycleFil sysm_(Io_url url) {return new IoEngine_xrg_recycleFil(SysmConst);} + public static IoEngine_xrg_recycleFil gplx_(Io_url url) {IoEngine_xrg_recycleFil rv = new IoEngine_xrg_recycleFil(GplxConst); rv.Url_set(url); return rv;} + public static IoEngine_xrg_recycleFil proto_() {return gplx_(Io_url_.Null);} + public static final int GplxConst = 0, SysmConst = 1; +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_saveFilStr.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_saveFilStr.java new file mode 100644 index 000000000..178c722b6 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_saveFilStr.java @@ -0,0 +1,33 @@ +/* +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.ios; import gplx.*; +import gplx.texts.*; +public class IoEngine_xrg_saveFilStr { + public Io_url Url() {return url;} public IoEngine_xrg_saveFilStr Url_(Io_url val) {url = val; return this;} Io_url url; + public String Text() {return text;} public IoEngine_xrg_saveFilStr Text_(String val) {text = val; return this;} private String text = ""; + public boolean Append() {return append;} public IoEngine_xrg_saveFilStr Append_() {return Append_(true);} public IoEngine_xrg_saveFilStr Append_(boolean val) {append = val; return this;} private boolean append = false; + public void Exec() { + if (String_.Eq(text, "") && append) return; // no change; don't bother writing to disc + IoEnginePool._.Fetch(url.Info().EngineKey()).SaveFilText_api(this); + } + public static IoEngine_xrg_saveFilStr new_(Io_url url, String text) { + IoEngine_xrg_saveFilStr rv = new IoEngine_xrg_saveFilStr(); + rv.url = url; rv.text = text; + return rv; + } IoEngine_xrg_saveFilStr() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_xferDir.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_xferDir.java new file mode 100644 index 000000000..26df8d6dd --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_xferDir.java @@ -0,0 +1,37 @@ +/* +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.ios; import gplx.*; +import gplx.criterias.*; +public class IoEngine_xrg_xferDir { + public boolean Type_move() {return move;} public boolean Type_copy() {return !move;} private boolean move = false; + public Io_url Src() {return src;} public IoEngine_xrg_xferDir Src_(Io_url val) {src = val; return this;} Io_url src; + public Io_url Trg() {return trg;} public IoEngine_xrg_xferDir Trg_(Io_url val) {trg = val; return this;} Io_url trg; + public boolean Recur() {return recur;} public IoEngine_xrg_xferDir Recur_() {recur = true; return this;} private boolean recur = false; + public boolean Overwrite() {return overwrite;} public IoEngine_xrg_xferDir Overwrite_() {return Overwrite_(true);} public IoEngine_xrg_xferDir Overwrite_(boolean v) {overwrite = v; return this;} private boolean overwrite = false; + public boolean ReadOnlyFails() {return readOnlyFails;} public IoEngine_xrg_xferDir ReadOnlyFails_() {return ReadOnlyFails_(true);} public IoEngine_xrg_xferDir ReadOnlyFails_(boolean v) {readOnlyFails = v; return this;} private boolean readOnlyFails = false; + public Criteria MatchCrt() {return matchCrt;} public IoEngine_xrg_xferDir MatchCrt_(Criteria v) {matchCrt = v; return this;} Criteria matchCrt = Criteria_.All; + public Criteria SubDirScanCrt() {return subDirScanCrt;} public IoEngine_xrg_xferDir SubDirScanCrt_(Criteria v) {subDirScanCrt = v; return this;} Criteria subDirScanCrt = Criteria_.All; + public void Exec() {IoEnginePool._.Fetch(src.Info().EngineKey()).XferDir(this);} + public static IoEngine_xrg_xferDir move_(Io_url src, Io_url trg) {return new_(src, trg, true);} + public static IoEngine_xrg_xferDir copy_(Io_url src, Io_url trg) {return new_(src, trg, false);} + static IoEngine_xrg_xferDir new_(Io_url src, Io_url trg, boolean move) { + IoEngine_xrg_xferDir rv = new IoEngine_xrg_xferDir(); + rv.src = src; rv.trg = trg; rv.move = move; + return rv; + } IoEngine_xrg_xferDir() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoEngine_xrg_xferFil.java b/100_core/src_200_io/gplx/ios/IoEngine_xrg_xferFil.java new file mode 100644 index 000000000..54e7f5c11 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoEngine_xrg_xferFil.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoEngine_xrg_xferFil { + public boolean Type_move() {return move;} private boolean move = false; + public Io_url Src() {return src;} Io_url src; + public Io_url Trg() {return trg;} Io_url trg; + public boolean Overwrite() {return overwrite;} public IoEngine_xrg_xferFil Overwrite_() {return Overwrite_(true);} public IoEngine_xrg_xferFil Overwrite_(boolean v) {overwrite = v; return this;} private boolean overwrite = false; + public boolean ReadOnlyFails() {return readOnlyFails;} public IoEngine_xrg_xferFil ReadOnlyFails_off() {return ReadOnlyFails_(false);} public IoEngine_xrg_xferFil ReadOnlyFails_(boolean v) {readOnlyFails = v; return this;} private boolean readOnlyFails = true; + public boolean MissingFails() {return missingFails;} public IoEngine_xrg_xferFil MissingFails_off() {return MissingFails_(false);} public IoEngine_xrg_xferFil MissingFails_(boolean v) {missingFails = v; return this;} private boolean missingFails = true; + public void Exec() {IoEnginePool._.Fetch(src.Info().EngineKey()).XferFil(this);} + public static IoEngine_xrg_xferFil move_(Io_url src, Io_url trg) {return new_(src, trg, true);} + public static IoEngine_xrg_xferFil copy_(Io_url src, Io_url trg) {return new_(src, trg, false);} + static IoEngine_xrg_xferFil new_(Io_url src, Io_url trg, boolean move) { + IoEngine_xrg_xferFil rv = new IoEngine_xrg_xferFil(); + rv.src = src; rv.trg = trg; rv.move = move; + return rv; + } IoEngine_xrg_xferFil() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoErr.java b/100_core/src_200_io/gplx/ios/IoErr.java new file mode 100644 index 000000000..25310d0db --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoErr.java @@ -0,0 +1,30 @@ +/* +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.ios; import gplx.*; +public class IoErr { + public static String Namespace = "gplx.ios."; + public static String FileIsReadOnly_key = Namespace + "FileIsReadOnlyError"; + public static String FileNotFound_key = Namespace + "FileNotFoundError"; + public static Err FileIsReadOnly(Io_url url) { + return Err_.new_key_(FileIsReadOnly_key, "file is read-only").Add("url", url.Xto_api()).CallLevel_1_(); + } + public static Err FileNotFound(String op, Io_url url) { + // file is missing -- op='copy' file='C:\a.txt' copyFile_target='D:\a.txt' + return Err_.new_key_(FileNotFound_key, "file not found").Add("op", op).Add("file", url.Xto_api()); + } +} \ No newline at end of file diff --git a/100_core/src_200_io/gplx/ios/IoItmAttrib.java b/100_core/src_200_io/gplx/ios/IoItmAttrib.java new file mode 100644 index 000000000..1772e57d0 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmAttrib.java @@ -0,0 +1,25 @@ +/* +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.ios; import gplx.*; +public class IoItmAttrib { + public boolean ReadOnly() {return readOnly;} public IoItmAttrib ReadOnly_() {return ReadOnly_(true);} public IoItmAttrib ReadOnly_(boolean val) {readOnly = val; return this;} private boolean readOnly; + public boolean Hidden() {return hidden;} public IoItmAttrib Hidden_() {return Hidden_(true);} public IoItmAttrib Hidden_(boolean val) {hidden = val; return this;} private boolean hidden; + public static IoItmAttrib readOnly_() {return new IoItmAttrib().ReadOnly_();} + public static IoItmAttrib hidden_() {return new IoItmAttrib().Hidden_();} + public static IoItmAttrib normal_() {return new IoItmAttrib().ReadOnly_(false).Hidden_(false);} +} diff --git a/100_core/src_200_io/gplx/ios/IoItmClassXtn.java b/100_core/src_200_io/gplx/ios/IoItmClassXtn.java new file mode 100644 index 000000000..7db2abfbf --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmClassXtn.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoItmClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "ioItemType"; + @Override public Class UnderClass() {return int.class;} + public Object DefaultValue() {return IoItmDir.Type_Dir;} + public boolean Eq(Object lhs, Object rhs) {return ((IoItm_base)lhs).compareTo(rhs) == CompareAble_.Same;} + @Override public Object ParseOrNull(String raw) { + String rawLower = String_.Lower(raw); + if (String_.Eq(rawLower, "dir")) return IoItmDir.Type_Dir; + else if (String_.Eq(rawLower, "fil")) return IoItmFil.Type_Fil; + else throw Err_.unhandled(raw); + } + @Override public Object XtoDb(Object obj) {return Int_.cast_(obj);} + public static final IoItmClassXtn _ = new IoItmClassXtn(); IoItmClassXtn() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoItmDir.java b/100_core/src_200_io/gplx/ios/IoItmDir.java new file mode 100644 index 000000000..e76c6c92a --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmDir.java @@ -0,0 +1,71 @@ +/* +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.ios; import gplx.*; +import gplx.criterias.*; +public class IoItmDir extends IoItm_base { + public boolean Exists() {return exists;} public void Exists_set(boolean v) {exists = v;} private boolean exists = true; + @Override public int TypeId() {return Type_Dir;} @Override public boolean Type_dir() {return true;} @Override public boolean Type_fil() {return false;} public static final int Type_Dir = 1; + @gplx.New public IoItmDir XtnProps_set(String key, Object val) {return (IoItmDir)super.XtnProps_set(key, val);} + public IoItmList SubDirs() {return subDirs;} IoItmList subDirs; + public IoItmList SubFils() {return subFils;} IoItmList subFils; + public IoItmHash XtoIoItmList(Criteria crt) { + IoItmHash rv = IoItmHash.list_(this.Url()); + XtoItmList_recur(rv, this, crt); + return rv; + } + Io_url[] XtoIoUrlAry() { + IoItmHash list = this.XtoIoItmList(Criteria_.All); +//#plat_wce list.Sort(); // NOTE: on wce, subFils retrieved in unexpected order; createTime vs pathString + int count = list.Count(); + Io_url[] rv = new Io_url[count]; + for (int i = 0; i < count; i++) + rv[i] = list.FetchAt(i).Url(); + return rv; + } + public IoItmDir FetchDeepOrNull(Io_url findDirUrl) { + String dirSpr = this.Url().Info().DirSpr(); int dirSprLen = String_.Len(dirSpr); + String currDirStr = this.Url().Raw(); + String findDirStr = findDirUrl.Raw(); + if (!String_.HasAtBgn(findDirStr, currDirStr)) return null; // findUrl must start with currUrl; + String findName = String_.DelEnd(currDirStr, dirSprLen); // seed findName for String_.MidByLen below; + IoItmDir curDir = this; + while (true) { + findDirStr = String_.DelBgn(findDirStr, String_.Len(findName) + dirSprLen); // NOTE: findName will never have trailingDirSpr; subDirs.Fetch() takes NameOnly; ex: "dir" not "dir\" + int nextDirSprPos = String_.FindFwd(findDirStr, dirSpr); if (nextDirSprPos == String_.Find_none) nextDirSprPos = String_.Len(findDirStr); + findName = String_.MidByLen(findDirStr, 0, nextDirSprPos); + if (String_.Eq(findDirStr, "")) return curDir; // findDirStr completely removed; all parts match; return curDir + curDir = IoItmDir_.as_(curDir.subDirs.Fetch(findName)); // try to find dir + if (curDir == null) return null; // dir not found; exit; NOTE: if dir found, loop restarts; with curDir as either findDir, or owner of findDir + } + } + void XtoItmList_recur(IoItmHash list, IoItmDir curDir, Criteria dirCrt) { + for (Object subFilObj : curDir.SubFils()) { + IoItmFil subFil = (IoItmFil)subFilObj; + list.Add(subFil); + } + for (Object subDirObj : curDir.SubDirs()) { + IoItmDir subDir = (IoItmDir)subDirObj; + if (dirCrt.Matches(subDir)) list.Add(subDir); + XtoItmList_recur(list, subDir, dirCrt); + } + } + @gplx.Internal protected IoItmDir(boolean caseSensitive) { + subDirs = IoItmList.new_(this, caseSensitive); + subFils = IoItmList.new_(this, caseSensitive); + } +} diff --git a/100_core/src_200_io/gplx/ios/IoItmDir_.java b/100_core/src_200_io/gplx/ios/IoItmDir_.java new file mode 100644 index 000000000..f9d0b25d3 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmDir_.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoItmDir_ { + public static IoItmDir as_(Object obj) {return obj instanceof IoItmDir ? (IoItmDir)obj : null;} + public static final IoItmDir Null = null_(); + public static IoItmDir top_(Io_url url) {return scan_(url);} + public static IoItmDir scan_(Io_url url) { + IoItmDir rv = new IoItmDir(url.Info().CaseSensitive()); + rv.ctor_IoItmBase_url(url); + return rv; + } + static IoItmDir null_() { + IoItmDir rv = new IoItmDir(true); // TODO: NULL should be removed + rv.ctor_IoItmBase_url(Io_url_.Null); + rv.Exists_set(false); + return rv; + } +} diff --git a/100_core/src_200_io/gplx/ios/IoItmFil.java b/100_core/src_200_io/gplx/ios/IoItmFil.java new file mode 100644 index 000000000..38069bbcb --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmFil.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoItmFil extends IoItm_base { + @Override public int TypeId() {return IoItmFil.Type_Fil;} @Override public boolean Type_dir() {return false;} @Override public boolean Type_fil() {return true;} public static final int Type_Fil = 2; + public boolean Exists() {return size != Size_Invalid;} // NOTE: questionable logic, but preserved for historical reasons; requires that length be set to -1 if !.exists + public DateAdp ModifiedTime() {return modifiedTime;} + public IoItmFil ModifiedTime_(DateAdp val) {modifiedTime = val; return this;} DateAdp modifiedTime; + public IoItmFil ModifiedTime_(String val) {return ModifiedTime_(DateAdp_.parse_gplx(val));} + @gplx.Virtual public long Size() {return size;} public IoItmFil Size_(long val) {size = val; return this;} long size; + public IoItmAttrib Attrib() {return attrib;} public IoItmFil Attrib_(IoItmAttrib val) {attrib = val; return this;} IoItmAttrib attrib = IoItmAttrib.normal_(); + public boolean ReadOnly() {return attrib.ReadOnly();} public IoItmFil ReadOnly_(boolean val) {attrib.ReadOnly_(val); return this;} + @gplx.New public IoItmFil XtnProps_set(String key, Object val) {return (IoItmFil)super.XtnProps_set(key, val);} + + @gplx.Internal protected IoItmFil ctor_IoItmFil(Io_url url, long size, DateAdp modifiedTime) { + ctor_IoItmBase_url(url); this.size = size; this.modifiedTime = modifiedTime; + return this; + } + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, IoItmFil_.Prop_Size)) return size; + else if (ctx.Match(k, IoItmFil_.Prop_Modified)) return modifiedTime; + else return super.Invk(ctx, ikey, k, m); + } + @gplx.Internal protected IoItmFil() {} + public static final long Size_Invalid = -1; + public static final int Size_Invalid_int = -1; +} diff --git a/100_core/src_200_io/gplx/ios/IoItmFil_.java b/100_core/src_200_io/gplx/ios/IoItmFil_.java new file mode 100644 index 000000000..22085ead9 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmFil_.java @@ -0,0 +1,25 @@ +/* +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.ios; import gplx.*; +public class IoItmFil_ { + public static IoItmFil as_(Object obj) {return obj instanceof IoItmFil ? (IoItmFil)obj : null;} + public static final String + Prop_Size = "size" + , Prop_Modified = "modified"; + public static IoItmFil new_(Io_url url, long size, DateAdp created, DateAdp modified) {return new IoItmFil().ctor_IoItmFil(url, size, modified);} +} diff --git a/100_core/src_200_io/gplx/ios/IoItmFil_mem.java b/100_core/src_200_io/gplx/ios/IoItmFil_mem.java new file mode 100644 index 000000000..57f3423a6 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmFil_mem.java @@ -0,0 +1,39 @@ +/* +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.ios; import gplx.*; +import gplx.ios.*; /*IoStream_mem*/ import gplx.texts.*; /*Encoding_*/ +class IoItmFil_mem extends IoItmFil { public static IoItmFil_mem as_(Object obj) {return obj instanceof IoItmFil_mem ? (IoItmFil_mem)obj : null;} + @gplx.Internal protected IoStream_mem Stream() {return stream;} IoStream_mem stream; // NOTE: using stream instead of Text, b/c no events for IoStream.Dispose; ex: stream.OpenStreamWrite; stream.Write("hi"); stream.Dispose(); "hi" would not be saved if Text is member variable + @Override public long Size() {return (int)stream.Len();} + public String Text() {return Text_get();} public void Text_set(String v) {stream = IoStream_mem.rdr_txt_(this.Url(), v);} + String Text_get() { + int len = (int)stream.Len(); + byte[] buffer = new byte[len]; + stream.Position_set(0); + stream.Read(buffer, 0, len); + return String_.new_utf8_(buffer); + } + public IoItmFil_mem Clone() {return new_(this.Url(), this.Size(), this.ModifiedTime(), this.Text());} + public static IoItmFil_mem new_(Io_url filPath, long size, DateAdp modified, String text) { + IoItmFil_mem rv = new IoItmFil_mem(); + rv.ctor_IoItmFil(filPath, size, modified); + rv.stream = IoStream_mem.rdr_txt_(filPath, text); + return rv; + } + public static final IoItmFil_mem Null = new_(Io_url_.Null, 0, DateAdp_.MinValue, ""); +} diff --git a/100_core/src_200_io/gplx/ios/IoItmHash.java b/100_core/src_200_io/gplx/ios/IoItmHash.java new file mode 100644 index 000000000..e254fb520 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmHash.java @@ -0,0 +1,43 @@ +/* +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.ios; import gplx.*; +public class IoItmHash extends OrderedHash_base { + public Io_url Url() {return url;} Io_url url; + public void Add(IoItm_base itm) {Add_base(MakeKey(itm.Url()), itm);} + public void Del(Io_url url) {Del(MakeKey(url));} + public IoItm_base Fetch(Io_url url) {return IoItm_base_.as_(Fetch_base(MakeKey(url)));} + @gplx.New public IoItm_base FetchAt(int i) {return IoItm_base_.as_(FetchAt_base(i));} + public Io_url[] XtoIoUrlAry() { + int count = this.Count(); + Io_url[] rv = new Io_url[count]; + for (int i = 0; i < count; i++) + rv[i] = this.FetchAt(i).Url(); + return rv; + } + String MakeKey(Io_url url) {return url.XtoCaseNormalized();} + public static IoItmHash new_() { + IoItmHash rv = new IoItmHash(); + rv.url = null;//Io_url_.Null; + return rv; + } IoItmHash() {} + public static IoItmHash list_(Io_url url) { + IoItmHash rv = new IoItmHash(); + rv.url = url; + return rv; + } +} diff --git a/100_core/src_200_io/gplx/ios/IoItmList.java b/100_core/src_200_io/gplx/ios/IoItmList.java new file mode 100644 index 000000000..1c001ca43 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItmList.java @@ -0,0 +1,76 @@ +/* +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.ios; import gplx.*; +import gplx.lists.*; /*OrderedHash_base*/ +public class IoItmList extends OrderedHash_base { + public boolean Has(Io_url url) {return Has_base(MakeKey(url));} + public void Add(IoItm_base itm) { + if (ownerDir != null) itm.OwnerDir_set(ownerDir); + Add_base(MakeKey(itm.Url()), itm); + } + public void Del(Io_url url) { + String key = MakeKey(url); + IoItm_base itm = IoItm_base_.as_(Fetch_base(key)); if (itm == null) return; + itm.OwnerDir_set(null); + super.Del(key); + } + public Io_url[] XtoIoUrlAry() { + int count = this.Count(); + Io_url[] rv = new Io_url[count]; + for (int i = 0; i < count; i++) + rv[i] = IoItm_base_.as_(i).Url(); + return rv; + } + @Override public void Sort() {SortBy(IoItmBase_comparer_nest._);} + @Override protected Object Fetch_base(Object keyObj) { + String key = MakeKey((String)keyObj); + return super.Fetch_base(key); + } + @Override public void Del(Object keyObj) { + String key = MakeKey((String)keyObj); + super.Del(key); + } + String MakeKey(Io_url url) { + String itmName = url.Type_dir() ? url.NameOnly() : url.NameAndExt(); + return MakeKey(itmName); + } + String MakeKey(String s) { + return caseSensitive ? s : String_.Lower(s); + } + IoItmDir ownerDir; boolean caseSensitive; + @gplx.Internal protected static IoItmList new_(IoItmDir v, boolean caseSensitive) { + IoItmList rv = new IoItmList(); + rv.ownerDir = v; rv.caseSensitive = caseSensitive; + return rv; + } + @gplx.Internal protected static IoItmList list_(boolean caseSensitive) {return new_(null, caseSensitive);} +} +class IoItmBase_comparer_nest implements ComparerAble { + public int compare(Object lhsObj, Object rhsObj) { + IoItm_base lhsItm = (IoItm_base)lhsObj, rhsItm = (IoItm_base)rhsObj; + Io_url lhsUrl = lhsItm.Url(), rhsUrl = rhsItm.Url(); + return String_.Eq(lhsUrl.OwnerDir().Raw(), rhsUrl.OwnerDir().Raw()) // is same dir + ? CompareAble_.Compare_obj(lhsUrl.NameAndExt(), rhsUrl.NameAndExt()) // same dir: compare name + : CompareAble_.Compare_obj(DepthOf(lhsItm), DepthOf(rhsItm)); // diff dir: compare by depth; ex: c:\fil.txt < c:\dir\fil.txt + } + int DepthOf(IoItm_base itm) { + Io_url url = itm.Url(); + return String_.Count(url.OwnerDir().Raw(), url.Info().DirSpr()); // use OwnerDir, else dir.Raw will return extra dirSeparator + } + public static final IoItmBase_comparer_nest _ = new IoItmBase_comparer_nest(); IoItmBase_comparer_nest() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoItm_base.java b/100_core/src_200_io/gplx/ios/IoItm_base.java new file mode 100644 index 000000000..4f9897758 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItm_base.java @@ -0,0 +1,54 @@ +/* +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.ios; import gplx.*; +public abstract class IoItm_base implements GfoInvkAble, CompareAble { + public abstract int TypeId(); public abstract boolean Type_dir(); public abstract boolean Type_fil(); + public Io_url Url() {return ownerDir == null ? url : ownerDir.Url().GenSubFil(name); /*NOTE: must call .Url*/} Io_url url; + public IoItmDir OwnerDir() {return ownerDir;} IoItmDir ownerDir; + public void OwnerDir_set(IoItmDir v) {if (v == this) throw Err_.new_("dir cannot be its own owner").Add("url", v.url.Raw()); + url = v == null && ownerDir != null + ? ownerDir.url.GenSubFil(name) // create url, since ownerDir will soon be null; NOTE: must call .url + : Io_url_.Null; // delete url, since ownerDir will be avail + ownerDir = v; + } + public String Name() {return name;} private String name; + public IoItm_base Name_(String v) { + name = v; + if (ownerDir == null) url = url.OwnerDir().GenSubFil(name); + return this; + } + public Object XtnProps_get(String key) {return props.Fetch(key);} HashAdp props = HashAdp_.Null; + public IoItm_base XtnProps_set(String key, Object val) { + if (props == HashAdp_.Null) props = HashAdp_.new_(); + props.Del(key); + props.Add(key, val); + return this; + } + public int compareTo(Object comp) {return url.compareTo(((IoItm_base)comp).url);} // NOTE: needed for comic importer (sort done on IoItmHash which contains IoItm_base) +// public Object Data_get(String name) {return GfoInvkAble_.InvkCmd(this, name);} + @gplx.Virtual public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, IoItm_base_.Prop_Type)) return this.TypeId(); + else if (ctx.Match(k, IoItm_base_.Prop_Path)) return this.Url(); + else if (ctx.Match(k, IoItm_base_.Prop_Title)) return this.Url().NameOnly(); // needed for gfio script criteria; + else if (ctx.Match(k, IoItm_base_.Prop_Ext)) return this.Url().Ext(); // needed for gfio script criteria; EX: where "ext LIKE '.java'" + else return GfoInvkAble_.Rv_unhandled; + } + @gplx.Internal protected void ctor_IoItmBase_url(Io_url url) {this.url = url; this.name = url.NameAndExt();} + @gplx.Internal protected void ctor_IoItmBase_name(String name) {this.name = name;} +} + diff --git a/100_core/src_200_io/gplx/ios/IoItm_base_.java b/100_core/src_200_io/gplx/ios/IoItm_base_.java new file mode 100644 index 000000000..39d34b5ce --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoItm_base_.java @@ -0,0 +1,26 @@ +/* +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.ios; import gplx.*; +public class IoItm_base_ { + public static IoItm_base as_(Object obj) {return obj instanceof IoItm_base ? (IoItm_base)obj : null;} + public static final String + Prop_Type = "type" + , Prop_Path = "url" + , Prop_Title = "title" + , Prop_Ext = "ext"; +} diff --git a/100_core/src_200_io/gplx/ios/IoRecycleBin.java b/100_core/src_200_io/gplx/ios/IoRecycleBin.java new file mode 100644 index 000000000..1a3601919 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoRecycleBin.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoRecycleBin { + public void Send(Io_url url) {Send_xrg(url).Exec();} + public IoEngine_xrg_recycleFil Send_xrg(Io_url url) {return IoEngine_xrg_recycleFil.gplx_(url);} + public void Recover(Io_url url) { + String_bldr sb = String_bldr_.new_(); + ListAdp list = Regy_search(url, sb); + int listCount = list.Count(); if (listCount > 1) throw Err_.new_("found more than 1 url").Add("count", list.Count()); + Io_url trgUrl = (Io_url)list.FetchAt(0); + IoEngine_xrg_xferFil.move_(url, trgUrl).ReadOnlyFails_(true).Overwrite_(false).Exec(); + IoEngine_xrg_saveFilStr.new_(FetchRegistryUrl(url), sb.XtoStr()).Exec(); + } + public void Regy_add(IoEngine_xrg_recycleFil xrg) { + Io_url url = xrg.RecycleUrl(); + Io_url regyUrl = FetchRegistryUrl(url); + String text = String_.ConcatWith_any("|", url.NameAndExt_noDirSpr(), xrg.Url().GenRelUrl_orEmpty(url.OwnerRoot()), xrg.Uuid().XtoStr(), xrg.AppName(), xrg.Time()); + IoEngine_xrg_saveFilStr.new_(regyUrl, text).Append_().Exec(); + } + public ListAdp Regy_search(Io_url url, String_bldr sb) { + ListAdp list = ListAdp_.new_(); + Io_url regyUrl = FetchRegistryUrl(url); + String[] lines = IoEngine_xrg_loadFilStr.new_(regyUrl).ExecAsStrAry(); + int linesLen = Array_.Len(lines); + String nameAndExt = url.NameAndExt_noDirSpr() + "|"; + for (int i = linesLen; i > 0; i--) { + String line = lines[i - 1]; + if (String_.HasAtBgn(line, nameAndExt)) { + String[] terms = String_.Split(line, "|"); + Io_url origUrl = url.OwnerRoot().GenSubFil(terms[1]); + list.Add(origUrl); + } + else + sb.Add_str_w_crlf(line); + } + return list; + } + Io_url FetchRegistryUrl(Io_url url) { + String sourceApp = String_.GetStrBefore(url.NameAndExt_noDirSpr(), ";"); + return url.OwnerDir().GenSubFil_ary(sourceApp, ".recycle.csv"); + } + public static final IoRecycleBin _ = new IoRecycleBin(); IoRecycleBin() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoStream.java b/100_core/src_200_io/gplx/ios/IoStream.java new file mode 100644 index 000000000..2cae01fb2 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream.java @@ -0,0 +1,33 @@ +/* +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.ios; import gplx.*; +public interface IoStream extends RlsAble { + Object UnderRdr(); + Io_url Url(); + long Pos(); + long Len(); + + int ReadAry(byte[] array); + int Read(byte[] array, int offset, int count); + long Seek(long pos); + void WriteAry(byte[] ary); + void Write(byte[] array, int offset, int count); + void Transfer(IoStream trg, int bufferLength); + void Flush(); + void Write_and_flush(byte[] bry, int bgn, int end); +} \ No newline at end of file diff --git a/100_core/src_200_io/gplx/ios/IoStream_.java b/100_core/src_200_io/gplx/ios/IoStream_.java new file mode 100644 index 000000000..570fd6491 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream_.java @@ -0,0 +1,180 @@ +/* +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.ios; import gplx.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +public class IoStream_ { + public static final IoStream Null = new IoStream_null(); + public static IoStream mem_txt_(Io_url url, String v) {return IoStream_mem.rdr_txt_(url, v);} + public static IoStream ary_(byte[] v) {return IoStream_mem.rdr_ary_(Io_url_.Null, v);} + public static final byte Mode_rdr = 0, Mode_wtr_create = 1, Mode_wtr_append = 2, Mode_wtr_update = 3; + public static IoStream stream_rdr_() {return new IoStream_stream_rdr();} + public static IoStream stream_input_(Io_url url) {return new IoStream_stream_rdr().UnderRdr_(input_stream_(url));} + public static Object input_stream_(Io_url url) { + try { + return new java.io.FileInputStream(url.Raw()); + } catch (Exception e) {throw Err_.new_fmt_("file not found: {0}", url.Raw());} + } +} +class IoStream_null implements IoStream { + public Object UnderRdr() {return null;} + public Io_url Url() {return Io_url_.Null;} + public long Pos() {return -1;} + public long Len() {return -1;} + public int ReadAry(byte[] array) {return -1;} + public int Read(byte[] array, int offset, int count) {return -1;} + public long Seek(long pos) {return -1;} + public void WriteAry(byte[] ary) {} + public void Write(byte[] array, int offset, int count) {} + public void Transfer(IoStream trg, int bufferLength) {} + public void Flush() {} + public void Write_and_flush(byte[] bry, int bgn, int end) {} + public void Rls() {} +} +class IoStream_base implements IoStream { + @gplx.Virtual public Io_url Url() {return url;} Io_url url = Io_url_.Null; + public void Transfer(IoStream trg, int bufferLength) { + byte[] buffer = new byte[bufferLength]; + int read = -1; + while (read != 0) { + read = this.Read(buffer, 0, bufferLength); + trg.Write(buffer, 0, read); + } + trg.Flush(); + } + public int ReadAry(byte[] ary) {return this.Read(ary, 0, ary.length);} + public void WriteAry(byte[] ary) {this.Write(ary, 0, ary.length);} + @gplx.Virtual public Object UnderRdr() {return under;} + @gplx.Virtual public void UnderRdr_(Object v) {this.under = (RandomAccessFile)v;} + @gplx.Virtual public long Pos() {return pos;} long pos; + @gplx.Virtual public long Len() {return length;} long length; + @gplx.Virtual public int Read(byte[] array, int offset, int count) { + try { + int rv = under.read(array, offset, count); + return rv == -1 ? 0 : rv; // NOTE: fis returns -1 if nothing read; .NET returned 0; Hash will fail if -1 returned (will try to create array of 0 length) + } // NOTE: fis keeps track of offset, only need to pass in array (20110606: this NOTE no longer seems to make sense; deprecate) + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "file read failed").Add("url", url);} + } + public long Seek(long seek_pos) { + try { + under.seek(seek_pos); + pos = under.getFilePointer(); + return pos; + } + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "seek failed").Add("url", url);} + } + @gplx.Virtual public void Write(byte[] array, int offset, int count) {bfr.Add_mid(array, offset, offset + count); this.Flush();} Bry_bfr bfr = Bry_bfr.reset_(16); + public void Write_and_flush(byte[] bry, int bgn, int end) { +// ConsoleAdp._.WriteLine(bry.length +" " + bgn + " " + end); + Flush();// flush anything already in buffer + int buffer_len = Io_mgr.Len_kb * 16; + byte[] buffer = new byte[buffer_len]; + int buffer_bgn = bgn; boolean loop = true; + while (loop) { + int buffer_end = buffer_bgn + buffer_len; + if (buffer_end > end) { + buffer_end = end; + buffer_len = end - buffer_bgn; + loop = false; + } + for (int i = 0; i < buffer_len; i++) + buffer[i] = bry[i + buffer_bgn]; + try {under.write(buffer, 0, buffer_len);} + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "write failed").Add("url", url);} + buffer_bgn = buffer_end; + } +// this.Rls(); +// OutputStream output_stream = null; +// try { +// output_stream = new FileOutputStream(url.Xto_api()); +// bry = ByteAry_.Mid(bry, bgn, end); +// output_stream.write(bry, 0, bry.length); +// } +// catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "write failed").Add("url", url);} +// finally { +// if (output_stream != null) { +// try {output_stream.close();} +// catch (IOException ignore) {} +// } +// } + } + @gplx.Virtual public void Flush() { + try { + if (mode_is_append) under.seek(under.length()); +// else under.seek(0); + } + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "seek failed").Add("url", url);} + try {under.write(bfr.Bfr(), 0, bfr.Len());} + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "write failed").Add("url", url);} + bfr.Clear(); + } + @gplx.Virtual public void Rls() { + IoEngine_system.Closeable_Close(under, url, true); + } + RandomAccessFile under; boolean mode_is_append; byte mode; + public static IoStream_base rdr_wrapper_() {return new IoStream_base();} + public static IoStream_base new_(Io_url url, int mode) { + IoStream_base rv = new IoStream_base(); + rv.url = url; + rv.mode = (byte)mode; + File file = new File(url.Xto_api()); + String ctor_mode = ""; + switch (mode) { // mode; SEE:NOTE_1 + case IoStream_.Mode_wtr_append: + rv.mode_is_append = mode == IoStream_.Mode_wtr_append; + ctor_mode = "rws"; + break; + case IoStream_.Mode_wtr_create: + ctor_mode = "rws"; + break; + case IoStream_.Mode_rdr: + ctor_mode = "r"; + break; + } + try {rv.under = new RandomAccessFile(file, ctor_mode);} + catch (FileNotFoundException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "file open failed").Add("url", url);} + if (mode == IoStream_.Mode_wtr_create) { + try {rv.under.setLength(0);} + catch (IOException e) {throw Err_.err_key_(e, IoEngineArgs._.Err_IoException, "file truncate failed").Add("url", url);} + } + rv.length = file.length(); + return rv; + } + public static IoStream_base new_(Object stream) { + IoStream_base rv = new IoStream_base(); +// rv.stream = (System.IO.Stream)stream; + rv.url = Io_url_.Null; + return rv; + } + } +/* +NOTE_1:stream mode +my understanding of mode +rw: read/write async? +rws: read/write sync; write content + metadata changes +rwd: read/write sync; write content +*/ +//#} \ No newline at end of file diff --git a/100_core/src_200_io/gplx/ios/IoStream_mem.java b/100_core/src_200_io/gplx/ios/IoStream_mem.java new file mode 100644 index 000000000..c1e90da4b --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream_mem.java @@ -0,0 +1,70 @@ +/* +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.ios; import gplx.*; +import gplx.texts.*; /*Encoding_*/ +class IoStream_mem extends IoStream_base { + @Override public Io_url Url() {return url;} Io_url url; + @Override public Object UnderRdr() {throw Err_.not_implemented_();} // NOTE: should not use System.IO.MemoryStream, b/c resized data will not be captured in this instance's buffer + @Override public long Len() {return Array_.Len(buffer);} + public int Position() {return position;} public void Position_set(int v) {position = v;} int position; + public byte[] Buffer() {return buffer;} private byte[] buffer = new byte[0]; + + @Override public int Read(byte[] array, int offset, int count) { + int read = 0; + int len = Array_.Len(buffer); + for (int i = 0; i < count; i++) { + if (position + i >= len) break; + array[offset + i] = buffer[position + i]; + read++; + } + position += read; + return read; + } + @Override public void Write(byte[] array, int offset, int count) { + // expand buffer if needed; necessary to emulate fileStream writing; ex: FileStream fs = new FileStream(); fs.Write(data); where data may be unknown length + int length = (int)position + count + -offset; + int bufLen = Array_.Len(buffer); + if (bufLen < length) buffer = Bry_.Resize_manual(buffer, length); + for (int i = 0; i < count; i++) + buffer[position + i] = array[offset + i]; + position += count +-offset; + } + @Override public long Pos() {return position;} + @Override public long Seek(long pos) { + this.position = (int)pos; + return pos; + } + + @Override public void Flush() {} + @Override public void Rls() {} + + public static IoStream_mem rdr_txt_(Io_url url, String v) {return rdr_ary_(url, Bry_.new_utf8_(v));} + public static IoStream_mem rdr_ary_(Io_url url, byte[] v) { + IoStream_mem rv = new IoStream_mem(); + rv.buffer = v; + rv.url = url; + return rv; + } + public static IoStream_mem wtr_data_(Io_url url, int length) { + IoStream_mem rv = new IoStream_mem(); + rv.buffer = new byte[length]; + rv.url = url; + return rv; + } + IoStream_mem() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoStream_mem_tst.java b/100_core/src_200_io/gplx/ios/IoStream_mem_tst.java new file mode 100644 index 000000000..2ad610373 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream_mem_tst.java @@ -0,0 +1,30 @@ +/* +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.ios; import gplx.*; +import org.junit.*; //using System.IO; /*Stream*/ +public class IoStream_mem_tst { + @Test public void Write() { // confirm that changes written to Stream acquired via .AdpObj are written to IoStream_mem.Buffer + IoStream_mem stream = IoStream_mem.wtr_data_(Io_url_.Null, 0); + byte[] data = Bry_.ints_(1); + stream.Write(data, 0, Array_.Len(data)); + + Tfds.Eq(1L , stream.Len()); + Tfds.Eq((byte)1 , stream.Buffer()[0]); + stream.Rls(); + } +} diff --git a/100_core/src_200_io/gplx/ios/IoStream_mock.java b/100_core/src_200_io/gplx/ios/IoStream_mock.java new file mode 100644 index 000000000..b13385451 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream_mock.java @@ -0,0 +1,45 @@ +/* +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.ios; import gplx.*; +public class IoStream_mock implements IoStream { + public byte[] Data_bry() {return data_bry;} public IoStream_mock Data_bry_(byte[] v) {data_bry = v; data_bry_len = v.length; return this;} private byte[] data_bry; int data_bry_len; + public int Data_bry_pos() {return data_bry_pos;} int data_bry_pos; + public void Reset() {data_bry_pos = 0;} + public IoStream_mock Read_limit_(int v) {read_limit = v; return this;} int read_limit; + public int Read(byte[] bfr, int bfr_bgn, int bfr_len) { + int bytes_read = bfr_len; + if (bytes_read > read_limit) bytes_read = read_limit; // stream may limit maximum read; EX: bfr_len of 16k but only 2k will be filled + int bytes_left = data_bry_len - data_bry_pos; + if (bytes_read > bytes_left) bytes_read = bytes_left; // not enough bytes left in data_bry; bytes_read = whatever is left + Bry_.Copy_by_pos(data_bry, data_bry_pos, data_bry_pos + bytes_read, bfr, bfr_bgn); + data_bry_pos += bytes_read; + return bytes_read; + } + public Object UnderRdr() {return null;} + public Io_url Url() {return Io_url_.Null;} + public long Pos() {return -1;} + public long Len() {return -1;} + public int ReadAry(byte[] array) {return -1;} + public long Seek(long pos) {return -1;} + public void WriteAry(byte[] ary) {} + public void Write(byte[] array, int offset, int count) {} + public void Transfer(IoStream trg, int bufferLength) {} + public void Flush() {} + public void Write_and_flush(byte[] bry, int bgn, int end) {} + public void Rls() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoStream_mock_tst.java b/100_core/src_200_io/gplx/ios/IoStream_mock_tst.java new file mode 100644 index 000000000..a3cc24136 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream_mock_tst.java @@ -0,0 +1,48 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoStream_mock_tst { + @Before public void init() {fxt.Clear();} IoStream_mock_fxt fxt = new IoStream_mock_fxt(); + @Test public void Basic() { + fxt.Init_src_str_("abcde").Init_trg_len_(5).Init_rdr_limit_(2).Init_read_len_(2); + fxt.Test_read("ab").Test_read("cd").Test_read("e"); + } + @Test public void Read_limit() { + fxt.Init_src_str_("abcde").Init_trg_len_(5).Init_rdr_limit_(2).Init_read_len_(4); + fxt.Test_read("ab").Test_read("cd").Test_read("e"); + } +} +class IoStream_mock_fxt { + public void Clear() { + if (rdr == null) + rdr = new IoStream_mock(); + rdr.Reset(); + trg_bgn = 0; + } IoStream_mock rdr; byte[] trg_bry; + public IoStream_mock_fxt Init_src_str_(String v) {rdr.Data_bry_(Bry_.new_ascii_(v)); return this;} + public IoStream_mock_fxt Init_trg_len_(int v) {trg_bry = new byte[v]; return this;} + public IoStream_mock_fxt Init_read_len_(int v) {read_len = v; return this;} int read_len; + public IoStream_mock_fxt Init_rdr_limit_(int v) {rdr.Read_limit_(v); return this;} + public IoStream_mock_fxt Test_read(String expd) { + int bytes_read = rdr.Read(trg_bry, trg_bgn, read_len); + Tfds.Eq(expd, String_.new_ascii_(trg_bry, trg_bgn, trg_bgn + bytes_read)); + trg_bgn += bytes_read; + return this; + } int trg_bgn; +} diff --git a/100_core/src_200_io/gplx/ios/IoStream_stream_rdr.java b/100_core/src_200_io/gplx/ios/IoStream_stream_rdr.java new file mode 100644 index 000000000..59d7895f1 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoStream_stream_rdr.java @@ -0,0 +1,39 @@ +/* +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.ios; import gplx.*; +public class IoStream_stream_rdr implements IoStream { + public int Read(byte[] bfr, int bfr_bgn, int bfr_len) { + try { + return stream.read(bfr, bfr_bgn, bfr_len); + } + catch (Exception e) {throw Err_.err_(e, "failed to read from stream");} + } + public IoStream UnderRdr_(Object v) {this.stream = (java.io.InputStream)v; return this;} java.io.InputStream stream; + public Object UnderRdr() {return stream;} + public Io_url Url() {return Io_url_.Null;} + public long Pos() {return -1;} + public long Len() {return -1;} + public int ReadAry(byte[] array) {return -1;} + public long Seek(long pos) {return -1;} + public void WriteAry(byte[] ary) {} + public void Write(byte[] array, int offset, int count) {} + public void Transfer(IoStream trg, int bufferLength) {} + public void Flush() {} + public void Write_and_flush(byte[] bry, int bgn, int end) {} + public void Rls() {} +} diff --git a/100_core/src_200_io/gplx/ios/IoUrlInfo.java b/100_core/src_200_io/gplx/ios/IoUrlInfo.java new file mode 100644 index 000000000..a2e229d73 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoUrlInfo.java @@ -0,0 +1,244 @@ +/* +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.ios; import gplx.*; +public interface IoUrlInfo { + String Key(); + byte DirSpr_byte(); + String DirSpr(); + boolean CaseSensitive(); + String EngineKey(); + + boolean Match(String raw); + boolean IsDir(String raw); + String Xto_api(String raw); + String OwnerDir(String raw); + String OwnerRoot(String raw); + String NameAndExt(String raw); + String NameOnly(String raw); + String Ext(String raw); + String XtoRootName(String raw, int rawLen); +} +class IoUrlInfo_nil implements IoUrlInfo { + public String Key() {return KeyConst;} public static final String KeyConst = String_.Null_mark; + public String EngineKey() {return "<>";} + public String DirSpr() {return "<>";} + public byte DirSpr_byte() {return Byte_ascii.Slash;} + public String VolSpr() {return "<>";} + public boolean CaseSensitive() {return false;} + public boolean Match(String raw) {return false;} + public boolean IsDir(String raw) {return false;} + public String Xto_api(String raw) {return "";} + public String OwnerDir(String raw) {return IoUrlInfo_base.NullString;} + public String OwnerRoot(String raw) {return IoUrlInfo_base.NullString;} + public String NameAndExt(String raw) {return "";} + public String NameOnly(String raw) {return "";} + public String Ext(String raw) {return "";} + public String XtoRootName(String raw, int rawLen) {return "";} + public static final IoUrlInfo_nil _ = new IoUrlInfo_nil(); IoUrlInfo_nil() {} +} +abstract class IoUrlInfo_base implements IoUrlInfo { + @gplx.Internal protected static final int DirSprLen = 1; + @gplx.Internal protected static final String NullString = "", ExtSeparator = "."; + public abstract String Key(); + public abstract byte DirSpr_byte(); + public abstract String DirSpr(); + public abstract boolean CaseSensitive(); + public abstract boolean Match(String raw); + public abstract String EngineKey(); + public boolean IsDir(String raw) {return String_.HasAtEnd(raw, DirSpr());} + public abstract String XtoRootName(String raw, int rawLen); + @gplx.Virtual public String Xto_api(String raw) { + return IsDir(raw) + ? String_.DelEnd(raw, IoUrlInfo_base.DirSprLen) // if Dir, remove trailing DirSpr, since most api will not expect it (ex: .Delete will malfunction) + : raw; + } + public String OwnerDir(String raw) { + int rawLen = String_.Len(raw); + int ownerDirSprPos = OwnerDirPos(raw, rawLen); + if (ownerDirSprPos <= OwnerDirPos_hasNoOwner) return IoUrlInfo_base.NullString; // no ownerDir found; return Null; only (a) NullUrls (b) RootUrls ("C:\") (c) relative ("fil.txt") + return String_.MidByLen(raw, 0, ownerDirSprPos + 1); // +1 to include backslash + } + @gplx.Virtual public String OwnerRoot(String raw) { + String temp = raw, rv = raw; + while (true) { + temp = OwnerDir(temp); + if (String_.Eq(temp, IoUrlInfo_base.NullString)) break; + rv = temp; + } + return rv; + } + public String NameAndExt(String raw) { // if Dir, will return \ as last char + int rawLen = String_.Len(raw); + int ownerDirSprPos = OwnerDirPos(raw, rawLen); + if (ownerDirSprPos == OwnerDirPos_isNull) return IoUrlInfo_base.NullString; // NullUrl and RootUrl return Null; + return ownerDirSprPos == OwnerDirPos_hasNoOwner || ownerDirSprPos == OwnerDirPos_isRoot + ? raw // no PathSeparator b/c (a) RootDir ("C:\"); (b) relative ("fil.txt") + : String_.DelBgn(raw, ownerDirSprPos + 1); // +1 to skip backslash + } + public String NameOnly(String raw) { + String nameAndExt = NameAndExt(raw); + if (IsDir(raw)) { + String rootName = XtoRootName(raw, String_.Len(raw)); // C:\ -> C; / -> root + return rootName == null + ? String_.DelEnd(nameAndExt, IoUrlInfo_base.DirSprLen) + : rootName; + } + int pos = String_.FindBwd(nameAndExt, IoUrlInfo_base.ExtSeparator); + return pos == String_.Find_none + ? nameAndExt // Ext not found; return entire NameAndExt + : String_.MidByLen(nameAndExt, 0, pos); + } + public String Ext(String raw) { // if Dir, return DirSpr; if Fil, return . as first char; ex: .txt; .png + if (IsDir(raw)) return this.DirSpr(); + String nameAndExt = NameAndExt(raw); + int pos = String_.FindBwd(nameAndExt, IoUrlInfo_base.ExtSeparator); + return pos == String_.Find_none ? "" : String_.DelBgn(nameAndExt, pos); + } + int OwnerDirPos(String raw, int rawLen) { + if (rawLen == 0) return OwnerDirPos_isNull; + else if (XtoRootName(raw, rawLen) != null) return OwnerDirPos_isRoot; + else {// NullUrls and RootUrls have no owners + int posAdj = IsDir(raw) ? IoUrlInfo_base.DirSprLen : 0; // Dir ends with DirSpr, adjust lastIndex by DirSprLen + return String_.FindBwd(raw, this.DirSpr(), rawLen - 1 - posAdj); // -1 to adjust for LastIdx + } + } + static final int + OwnerDirPos_hasNoOwner = -1 // ListAdp_.NotFound + , OwnerDirPos_isNull = -2 + , OwnerDirPos_isRoot = -3; +} +class IoUrlInfo_wnt extends IoUrlInfo_base { + @Override public String Key() {return "wnt";} + @Override public String EngineKey() {return IoEngine_.SysKey;} + @Override public String DirSpr() {return Op_sys.Wnt.Fsys_dir_spr_str();} + @Override public byte DirSpr_byte() {return Byte_ascii.Backslash;} + @Override public boolean CaseSensitive() {return Op_sys.Wnt.Fsys_case_match();} + @Override public boolean Match(String raw) {return String_.Len(raw) > 1 && String_.CharAt(raw, 1) == ':';} // 2nd char is :; assumes 1 letter drives + @Override public String XtoRootName(String raw, int rawLen) { + return rawLen == 3 && String_.CharAt(raw, 1) == ':' // only allow single letter drives; ex: C:\; note, CharAt(raw, 1) to match Match + ? Char_.XtoStr(String_.CharAt(raw, 0)) + : null; + } + public static final IoUrlInfo_wnt _ = new IoUrlInfo_wnt(); IoUrlInfo_wnt() {} +} +class IoUrlInfo_lnx extends IoUrlInfo_base { + @Override public String Key() {return "lnx";} + @Override public String EngineKey() {return IoEngine_.SysKey;} + @Override public String DirSpr() {return DirSprStr;} static final String DirSprStr = Op_sys.Lnx.Fsys_dir_spr_str(); + @Override public byte DirSpr_byte() {return Byte_ascii.Slash;} + @Override public boolean CaseSensitive() {return Op_sys.Lnx.Fsys_case_match();} + @Override public boolean Match(String raw) {return String_.HasAtBgn(raw, DirSprStr);} // anything that starts with / + @Override public String XtoRootName(String raw, int rawLen) { + return rawLen == 1 && String_.Eq(raw, DirSprStr) + ? "root" + : null; + } + @Override public String OwnerRoot(String raw) {return DirSprStr;} // drive is always / + @Override public String Xto_api(String raw) { + return String_.Eq(raw, DirSprStr) // is root + ? DirSprStr + : super.Xto_api(raw); // NOTE: super.Xto_api will strip off last / + } + public static final IoUrlInfo_lnx _ = new IoUrlInfo_lnx(); IoUrlInfo_lnx() {} +} +class IoUrlInfo_rel extends IoUrlInfo_base { + @Override public String Key() {return "rel";} + @Override public String EngineKey() {return IoEngine_.SysKey;} + @Override public String DirSpr() {return info.DirSpr();} + @Override public byte DirSpr_byte() {return info.DirSpr_byte();} + @Override public boolean CaseSensitive() {return info.CaseSensitive();} + @Override public String XtoRootName(String raw, int rawLen) {return info.XtoRootName(raw, rawLen);} + @Override public boolean Match(String raw) {return true;} // relPath is always lastResort; return true + IoUrlInfo info; + public static IoUrlInfo_rel new_(IoUrlInfo info) { + IoUrlInfo_rel rv = new IoUrlInfo_rel(); + rv.info = info; + return rv; + } IoUrlInfo_rel() {} +} +class IoUrlInfo_mem extends IoUrlInfo_base { + @Override public String Key() {return key;} private String key; + @Override public String EngineKey() {return engineKey;} private String engineKey; + @Override public String DirSpr() {return "/";} + @Override public byte DirSpr_byte() {return Byte_ascii.Slash;} + @Override public boolean CaseSensitive() {return false;} + @Override public String XtoRootName(String raw, int rawLen) { + return String_.Eq(raw, key) ? String_.DelEnd(key, 1) : null; + } + @Override public boolean Match(String raw) {return String_.HasAtBgn(raw, key);} + public static IoUrlInfo_mem new_(String key, String engineKey) { + IoUrlInfo_mem rv = new IoUrlInfo_mem(); + rv.key = key; rv.engineKey = engineKey; + return rv; + } IoUrlInfo_mem() {} +} +class IoUrlInfo_alias extends IoUrlInfo_base { + @Override public String Key() {return srcDir;} + @Override public String EngineKey() {return engineKey;} private String engineKey; + @Override public String DirSpr() {return srcDirSpr;} + @Override public byte DirSpr_byte() {return srcDirSpr_byte;} private byte srcDirSpr_byte; + @Override public boolean CaseSensitive() {return false;} + @Override public String XtoRootName(String raw, int rawLen) { + return String_.Eq(raw, srcRootDir) ? srcRootName : null; + } + @Override public boolean Match(String raw) {return String_.HasAtBgn(raw, srcDir);} + @Override public String Xto_api(String raw) { + String rv = String_.Replace(raw, srcDir, trgDir); // replace src with trg + if (!String_.Eq(srcDirSpr, trgDirSpr)) rv = String_.Replace(rv, srcDirSpr, trgDirSpr); // replace dirSprs + return IsDir(raw) + ? String_.DelEnd(rv, IoUrlInfo_base.DirSprLen) // remove trailingSeparator, else Directory.Delete will not work properly + : rv; + } + void SrcDir_set(String v) { + srcDir = v; + boolean lnx = DirSpr_lnx(v); + if (srcDirSpr == null) { + if (lnx) { + srcDirSpr = Op_sys.Lnx.Fsys_dir_spr_str(); + srcDirSpr_byte = Op_sys.Lnx.Fsys_dir_spr_byte(); + } + else { + srcDirSpr = Op_sys.Wnt.Fsys_dir_spr_str(); + srcDirSpr_byte = Op_sys.Wnt.Fsys_dir_spr_byte(); + } + } + if (srcRootName == null) srcRootName = lnx ? "root" : String_.Mid(srcDir, 0, String_.FindFwd(srcDir, ":")); + if (srcRootDir == null) srcRootDir = lnx ? "/" : srcDir; + } + void TrgDir_set(String v) { + trgDir = v; + boolean lnx = DirSpr_lnx(v); + if (trgDirSpr == null) trgDirSpr = lnx ? Op_sys.Lnx.Fsys_dir_spr_str() : Op_sys.Wnt.Fsys_dir_spr_str(); + } + boolean DirSpr_lnx(String s) {return String_.Has(s, Op_sys.Lnx.Fsys_dir_spr_str());} + void EngineKey_set(String v) {engineKey = v;} + String srcDir, trgDir, srcDirSpr, trgDirSpr, srcRootDir, srcRootName; + public static IoUrlInfo_alias new_(String srcDir, String trgDir, String engineKey) { + IoUrlInfo_alias rv = new IoUrlInfo_alias(); + rv.SrcDir_set(srcDir); + rv.TrgDir_set(trgDir); + rv.EngineKey_set(engineKey); + return rv; + } + public static final IoUrlInfo_alias KEYS = new IoUrlInfo_alias(); + public final String + Data_EngineKey = "engineKey" + , Data_SrcDir = "srcDir" + , Data_TrgDir = "trgDir" + ; +} diff --git a/100_core/src_200_io/gplx/ios/IoUrlInfoRegy.java b/100_core/src_200_io/gplx/ios/IoUrlInfoRegy.java new file mode 100644 index 000000000..f82ce7e12 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoUrlInfoRegy.java @@ -0,0 +1,52 @@ +/* +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.ios; import gplx.*; +public class IoUrlInfoRegy implements GfoInvkAble { + public void Reg(IoUrlInfo info) {hash.AddReplace(info.Key(), info);} + public IoUrlInfo Match(String raw) { + if (String_.Len(raw) == 0) return IoUrlInfo_.Nil; + for (int i = hash.Count(); i > 0; i--) { + IoUrlInfo info = (IoUrlInfo)hash.FetchAt(i - 1); + if (info.Match(raw)) return info; + } + throw Err_.new_("could not match ioPathInfo").Add("raw", raw).Add("count", hash.Count()); + } + public void Reset() { + hash.Clear(); + Reg(IoUrlInfo_rel.new_(Op_sys.Cur().Tid_is_wnt() ? (IoUrlInfo)IoUrlInfo_wnt._ : (IoUrlInfo)IoUrlInfo_lnx._)); + Reg(IoUrlInfo_.Mem); + Reg(IoUrlInfo_lnx._); + Reg(IoUrlInfo_wnt._); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Add)) { + String srcDirStr = m.ReadStr("srcDir"); + String trgDirStr = m.ReadStr("trgDir"); + String engineKey = m.ReadStrOr("engineKey", IoEngine_.SysKey); + if (ctx.Deny()) return this; + IoUrlInfo_alias alias = IoUrlInfo_alias.new_(srcDirStr, trgDirStr, engineKey); + IoUrlInfoRegy._.Reg(alias); + } + return this; + } public static final String Invk_Add = "Add"; + OrderedHash hash = OrderedHash_.new_(); + public static final IoUrlInfoRegy _ = new IoUrlInfoRegy(); + IoUrlInfoRegy() { + this.Reset(); + } +} \ No newline at end of file diff --git a/100_core/src_200_io/gplx/ios/IoUrlInfo_.java b/100_core/src_200_io/gplx/ios/IoUrlInfo_.java new file mode 100644 index 000000000..897e320fa --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoUrlInfo_.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoUrlInfo_ { + public static final IoUrlInfo Nil = IoUrlInfo_nil._; + public static final IoUrlInfo Wnt = IoUrlInfo_wnt._; + public static final IoUrlInfo Lnx = IoUrlInfo_lnx._; + public static final IoUrlInfo Mem = IoUrlInfo_mem.new_("mem", IoEngine_.MemKey); + + public static IoUrlInfo mem_(String key, String engineKey) {return IoUrlInfo_mem.new_(key, engineKey);} + public static IoUrlInfo alias_(String srcRoot, String trgRoot, String engineKey) {return IoUrlInfo_alias.new_(srcRoot, trgRoot, engineKey);} +} +/* +wnt C:\dir\fil.txt +wce \dir\fil.txt +lnx /dir/fil.txt +mem mem/dir/fil.txt +alias app:\dir\fil.txt +*/ \ No newline at end of file diff --git a/100_core/src_200_io/gplx/ios/IoUrlTypeRegy.java b/100_core/src_200_io/gplx/ios/IoUrlTypeRegy.java new file mode 100644 index 000000000..701a12a6c --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoUrlTypeRegy.java @@ -0,0 +1,75 @@ +/* +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.ios; import gplx.*; +public class IoUrlTypeRegy implements GfoInvkAble { + public String[] FetchAryOr(String key, String... or) { + IoUrlTypeGrp itm = (IoUrlTypeGrp)hash.Fetch(key); + return itm == null ? or : itm.AsAry(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Get)) { + String key = m.ReadStr(k); + if (ctx.Deny()) return this; + IoUrlTypeGrp itm = (IoUrlTypeGrp)hash.Fetch(key); + if (itm == null) { + itm = new IoUrlTypeGrp(key); + hash.Add(key, itm); + } + return itm; + } + else return GfoInvkAble_.Rv_unhandled; +// return this; + } public static final String Invk_Get = "Get"; + OrderedHash hash = OrderedHash_.new_(); + public static final IoUrlTypeRegy _ = new IoUrlTypeRegy(); IoUrlTypeRegy() {} +} +class IoUrlTypeGrp implements GfoInvkAble { + public String[] AsAry() { + String[] rv = new String[list.Count()]; + for (int i = 0; i < list.Count(); i++) + rv[i] = (String)list.FetchAt(i); + return rv; + } + OrderedHash list = OrderedHash_.new_(); + public IoUrlTypeGrp(String key) {this.key = key;} private String key; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_AddMany)) { + if (ctx.Deny()) return this; + for (int i = 0; i < m.Args_count(); i++) { + String s = m.ReadStr("v"); + if (list.Has(s)) { + ctx.Write_warn(UsrMsg.new_("itm already has filter").Add("key", key).Add("filter", s).XtoStr()); + list.Del(s); + } + list.Add(s, s); + } + } + else if (ctx.Match(k, Invk_Print)) { + if (ctx.Deny()) return this; + String_bldr sb = String_bldr_.new_(); + sb.Add(key).Add("{"); + for (int i = 0; i < list.Count(); i++) + sb.Add_spr_unless_first((String)list.FetchAt(i), " ", i); + sb.Add("}"); + return sb.XtoStr(); + } + else if (ctx.Match(k, Invk_Clear)) {if (ctx.Deny()) return this; list.Clear();} + else return GfoInvkAble_.Rv_unhandled; + return this; + } public static final String Invk_AddMany = "AddMany", Invk_Clear = "Clear", Invk_Print = "Print"; +} diff --git a/100_core/src_200_io/gplx/ios/IoZipWkr.java b/100_core/src_200_io/gplx/ios/IoZipWkr.java new file mode 100644 index 000000000..f3d015f46 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoZipWkr.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import gplx.stores.*; /*GfoNdeRdr_*/ +public class IoZipWkr { + public Io_url ExeUrl() {return (Io_url)GfoRegy._.FetchValOrFail(Regy_ExeUrl);} + public String ExeArgFmt() {return (String)GfoRegy._.FetchValOrFail(Regy_ExeArgFmt);} + public void Expand(Io_url srcUrl, Io_url trgUrl) { + String exeArgs = Expand_genCmdString(srcUrl, trgUrl); + process.Exe_url_(this.ExeUrl()).Args_str_(exeArgs); + process.Run_wait(); + } + @gplx.Internal protected String Expand_genCmdString(Io_url srcUrl, Io_url trgUrl) { + return String_.Format(this.ExeArgFmt(), srcUrl.Xto_api(), trgUrl.Xto_api()); + } + ProcessAdp process = new ProcessAdp(); + public static IoZipWkr regy_() {return new IoZipWkr();} + static final String Regy_ExeUrl = "gplx.ios.IoZipWkr.ExeUrl", Regy_ExeArgFmt = "gplx.ios.IoZipWkr.ExeArgFmt"; + public static IoZipWkr new_(Io_url exeUrl, String expandArgs) { + GfoRegy._.RegObj(Regy_ExeUrl, exeUrl); + GfoRegy._.RegObj(Regy_ExeArgFmt, expandArgs); + IoZipWkr rv = new IoZipWkr(); + return rv; + } +} diff --git a/100_core/src_200_io/gplx/ios/IoZipWkr_tst.java b/100_core/src_200_io/gplx/ios/IoZipWkr_tst.java new file mode 100644 index 000000000..7aa7987e5 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/IoZipWkr_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoZipWkr_tst { + @Test public void Basic() { + wkr = IoZipWkr.new_(Io_url_.Null, "e \"{0}\" -o\"{1}\" -y"); + tst_Expand_genCmdString(Io_url_.wnt_fil_("C:\\fil1.zip"), Io_url_.wnt_dir_("D:\\out\\"), "e \"C:\\fil1.zip\" -o\"D:\\out\" -y"); // NOTE: not "D:\out\" because .Xto_api + } IoZipWkr wkr; + void tst_Expand_genCmdString(Io_url srcUrl, Io_url trgUrl, String expd) { + Tfds.Eq(expd, wkr.Expand_genCmdString(srcUrl, trgUrl)); + } +} diff --git a/100_core/src_200_io/gplx/ios/Io_download_fmt.java b/100_core/src_200_io/gplx/ios/Io_download_fmt.java new file mode 100644 index 000000000..db84f5722 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_download_fmt.java @@ -0,0 +1,92 @@ +/* +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.ios; import gplx.*; +import gplx.brys.*; +public class Io_download_fmt { + public long Time_bgn() {return time_bgn;} long time_bgn; + public long Time_now() {return time_now;} long time_now; + public long Time_dif() {return time_dif;} long time_dif; + public long Time_end() {return time_end;} long time_end; + public String Src_url() {return src_url;} private String src_url; + public String Src_name() {return src_name;} private String src_name; + public long Src_len() {return src_len;} long src_len; + public long Prog_done() {return prog_done;} long prog_done; + public long Prog_rate() {return prog_rate;} long prog_rate; + public long Prog_left() {return prog_left;} long prog_left; + public long Prog_pct() {return prog_pct;} long prog_pct; + public String Prog_msg_hdr() {return prog_msg_hdr;} private String prog_msg_hdr; + public int Prog_num_units() {return prog_num_units;} int prog_num_units = Io_mgr.Len_kb; + public String Prog_num_fmt() {return prog_num_fmt;} private String prog_num_fmt = "#,##0"; + public String Prog_msg() {return prog_msg;} private String prog_msg; + Io_size_fmtr_arg size_fmtr_arg = new Io_size_fmtr_arg(), rate_fmtr_arg = new Io_size_fmtr_arg().Suffix_(Bry_.new_ascii_("ps")); + Bry_fmtr_arg_time prog_left_fmtr_arg = new Bry_fmtr_arg_time(); Bry_fmtr_arg_decimal_int prog_pct_fmtr_arg = new Bry_fmtr_arg_decimal_int().Places_(2); + public void Ctor(Gfo_usr_dlg usr_dlg) { + this.usr_dlg = usr_dlg; + } Gfo_usr_dlg usr_dlg; + public void Init(String src_url, String prog_msg_hdr) { + this.src_url = src_url; + this.src_name = String_.Extract_after_bwd(src_url, "/"); + this.prog_msg_hdr = prog_msg_hdr; + } + public void Bgn(long src_len) { + this.src_len = src_len; + prog_fmtr.Fmt_(prog_msg_hdr).Keys_("src_name", "src_len").Bld_bfr_many_and_set_fmt(src_name, size_fmtr_arg.Val_(src_len)); + prog_fmtr.Keys_("prog_done", "prog_pct", "prog_rate", "prog_left"); + prog_done = 0; + prog_pct = 0; + prog_rate = 0; + prog_left = 0; + time_bgn = time_prv = Env_.TickCount(); + time_checkpoint = 0; + size_checkpoint = size_checkpoint_interval; + } + long time_checkpoint_interval = 250; + long time_checkpoint = 0; + long time_prv = 0; + long size_checkpoint = 0; + long size_checkpoint_interval = 32 * Io_mgr.Len_kb; + public void Prog(int prog_read) { + time_now = Env_.TickCount(); + time_dif = time_now - time_bgn; if (time_dif == 0) time_dif = 1; // avoid div by zero error below + prog_done += prog_read; + time_checkpoint += time_now - time_prv; + time_prv = time_now; + if ((time_checkpoint < time_checkpoint_interval)) return; // NOTE: using time_checkpoint instead of size_checkpoint b/c WMF dump servers transfer in spurts (sends 5 packets, and then waits); + time_checkpoint = 0; + prog_rate = (prog_done * 1000) / (time_dif); + prog_pct = (prog_done * 10000) / src_len; // 100 00 to get 2 decimal places; EX: .1234 -> 1234 -> 12.34% + prog_left = (1000 * (src_len - prog_done)) / prog_rate; + prog_fmtr.Bld_bfr_many(prog_bfr + , size_fmtr_arg.Val_(prog_done) + , prog_pct_fmtr_arg.Val_((int)prog_pct) + , rate_fmtr_arg.Val_(prog_rate) + , prog_left_fmtr_arg.Seconds_(prog_left / 1000) + ); + prog_msg = prog_bfr.XtoStrAndClear(); + if (usr_dlg != null) + usr_dlg.Prog_none(GRP_KEY, "prog", prog_msg); + } private Bry_bfr prog_bfr = Bry_bfr.new_(); Bry_fmtr prog_fmtr = Bry_fmtr.new_().Fail_when_invalid_escapes_(false); // NOTE: prog_fmtr can be passed file_names with ~ which are not easy to escape; DATE:2013-02-19 + public void Term() { + time_end = Env_.TickCount(); +// prog_rate = (prog_done * 1000) / (time_dif); +// prog_pct = (prog_done * 10000) / src_len; // 100 00 to get 2 decimal places; EX: .1234 -> 1234 -> 12.34% +// prog_left = (1000 * (src_len - prog_done)) / prog_rate; +// if (usr_dlg != null) usr_dlg.Prog_none(GRP_KEY, "clear", ""); + } + static final String GRP_KEY = "gplx.download"; +} diff --git a/100_core/src_200_io/gplx/ios/Io_download_fmt_tst.java b/100_core/src_200_io/gplx/ios/Io_download_fmt_tst.java new file mode 100644 index 000000000..5de89cc32 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_download_fmt_tst.java @@ -0,0 +1,73 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class Io_download_fmt_tst { + Io_download_fmt_fxt fxt = new Io_download_fmt_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Fmt() { + fxt.Clear().Ini("downloading ~{src_name}: ~{prog_left} left (@ ~{prog_rate}); ~{prog_done} of ~{src_len} (~{prog_pct}%)", "http://a.org/b.png", Io_mgr.Len_kb * 10); + fxt.Now_add_f(1000).Prog_done_(1 * Io_mgr.Len_kb).Prog_pct_(1 * 1000).Prog_rate_(Io_mgr.Len_kb).Prog_left_(9 * 1000) + .Prog_msg_("downloading b.png: 09s left (@ 1.000 KBps); 1.000 KB of 10.000 KB (10.00%)") + .Download_(Io_mgr.Len_kb); + fxt.Now_add_f(1000).Prog_done_(2 * Io_mgr.Len_kb).Prog_pct_(2 * 1000).Prog_rate_(Io_mgr.Len_kb).Prog_left_(8 * 1000) + .Prog_msg_("downloading b.png: 08s left (@ 1.000 KBps); 2.000 KB of 10.000 KB (20.00%)") + .Download_(Io_mgr.Len_kb) + ; + fxt.Now_add_f(2000).Prog_done_(3 * Io_mgr.Len_kb).Prog_pct_(3 * 1000).Prog_rate_(768).Prog_left_(9333) + .Prog_msg_("downloading b.png: 09s left (@ 768.000 Bps); 3.000 KB of 10.000 KB (30.00%)") + .Download_(Io_mgr.Len_kb) + ; + } + @Test public void Tilde() { + fxt.Clear().Ini("a~b", "http://a.org/b.png", Io_mgr.Len_kb * 10); + } +} +class Io_download_fmt_fxt { + public Io_download_fmt_fxt Clear() { + if (fmt == null) { + fmt = new Io_download_fmt(); + } + Env_.TickCount_Test = 0; + prog_done = prog_rate = prog_pct = prog_left = -1; + prog_msg = null; + return this; + } Io_download_fmt fmt; + public Io_download_fmt_fxt Now_add_f(int v) {Env_.TickCount_Test += v; return this;} + public Io_download_fmt_fxt Prog_done_(int v) {prog_done = v; return this;} long prog_done = -1; + public Io_download_fmt_fxt Prog_pct_ (int v) {prog_pct = v; return this;} long prog_pct = -1; + public Io_download_fmt_fxt Prog_rate_(int v) {prog_rate = v; return this;} long prog_rate = -1; + public Io_download_fmt_fxt Prog_left_(int v) {prog_left = v; return this;} long prog_left = -1; + public Io_download_fmt_fxt Prog_msg_(String v) { + prog_msg = v; return this; + } String prog_msg; + public Io_download_fmt_fxt Download_(int v) { + fmt.Prog(v); + if (prog_done != -1) Tfds.Eq(prog_done, fmt.Prog_done(), "prog_done"); + if (prog_pct != -1) Tfds.Eq(prog_pct , fmt.Prog_pct(), "prog_pct"); + if (prog_rate != -1) Tfds.Eq(prog_rate, fmt.Prog_rate(), "prog_rate"); + if (prog_left != -1) Tfds.Eq(prog_left, fmt.Prog_left(), "prog_left"); + if (prog_msg != null) Tfds.Eq(prog_msg, fmt.Prog_msg(), "prog_msg"); + return this; + } + public Io_download_fmt_fxt Ini(String prog_msg_hdr, String src_url, int src_len) { + fmt.Init(src_url, prog_msg_hdr); + fmt.Bgn(src_len); + return this; + } +} diff --git a/100_core/src_200_io/gplx/ios/Io_size_.java b/100_core/src_200_io/gplx/ios/Io_size_.java new file mode 100644 index 000000000..e3f5260f2 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_size_.java @@ -0,0 +1,111 @@ +/* +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.ios; import gplx.*; +public class Io_size_ { + public static String Xto_str(long val) { + long cur = val; int pow = 0; + while (cur >= 1024) { + cur /= 1024; + pow++; + } + long div = (long)Math_.Pow((long)1024, (long)pow); + DecimalAdp valDecimal = DecimalAdp_.divide_(val, div); + String[] unit = Io_size_.Units[pow]; + return valDecimal.XtoStr("#,###.000") + " " + String_.PadBgn(unit[0], 2, " "); + } + public static long parse_or_(String raw, long or) { + if (raw == null || raw == String_.Empty) return or; + String[] terms = String_.Split(raw, " "); + int termsLen = Array_.Len(terms); if (termsLen > 2) return or; + + DecimalAdp val = null; + try {val = DecimalAdp_.parse_(terms[0]);} catch (Exception exc) {Err_.Noop(exc); return or;} + + int unitPow = 0; + if (termsLen > 1) { + String unitStr = String_.Upper(terms[1]); + unitPow = parse_unitPow_(unitStr); if (unitPow == -1) return or; + } + int curPow = unitPow; + while (curPow > 0) { + val = val.Op_mult(1024); + curPow--; + } + DecimalAdp comp = val.Op_truncate_decimal(); + if (!val.Eq(comp)) return or; + return val.XtoLong(); + } + static int parse_unitPow_(String unitStr) { + int unitLen = Array_.Len(Units); + int unitPow = -1; + for (int i = 0; i < unitLen; i++) { + if (String_.Eq(unitStr, String_.Upper(Units[i][0]))) return i; + if (String_.Eq(unitStr, String_.Upper(Units[i][1]))) return i; + } + return unitPow; + } + static String UnitsXtoStr() { + String_bldr sb = String_bldr_.new_(); + int len = Array_.Len(Units); + for (int i = 0; i < len; i++) { + String[] eny = Units[i]; + sb.Add_fmt("{0},{1};", eny[0], eny[1]); + } + return sb.XtoStr(); + } + static final String[][] Units = new String[][] + { String_.Ary("B", "BYTE") + , String_.Ary("KB", "KILOBYTE") + , String_.Ary("MB", "MEGABYTE") + , String_.Ary("GB", "GIGABYTE") + , String_.Ary("TB", "TERABYTE") + , String_.Ary("PB", "PETABYTE") + , String_.Ary("EB", "EXABYTE") + }; + public static final byte[][] Units_bry = new byte[][] + { Bry_.new_ascii_("B") + , Bry_.new_ascii_("KB") + , Bry_.new_ascii_("MB") + , Bry_.new_ascii_("GB") + , Bry_.new_ascii_("TB") + , Bry_.new_ascii_("PB") + , Bry_.new_ascii_("EB") + }; + public static int Load_int_(GfoMsg m) {return (int)Load_long_(m);} + public static long Load_long_(GfoMsg m) { + String v = m.ReadStr("v"); + long rv = parse_or_(v, Long_.MinValue); if (rv == Long_.MinValue) throw Err_.new_fmt_("invalid val: {0}", v); + return rv; + } +} +class Io_size_fmtr_arg implements Bry_fmtr_arg { + public long Val() {return val;} public Io_size_fmtr_arg Val_(long v) {val = v; return this;} long val; + public byte[] Suffix() {return suffix;} public Io_size_fmtr_arg Suffix_(byte[] v) {suffix = v; return this;} private byte[] suffix; + public void XferAry(Bry_bfr bfr, int idx) { + long cur = val; int pow = 0; + while (cur >= 1024) { + cur /= 1024; + pow++; + } + long div = (long)Math_.Pow((long)1024, (long)pow); + DecimalAdp val_decimal = DecimalAdp_.divide_(val, div); + bfr.Add_str(val_decimal.XtoStr("#,###.000")).Add_byte(Byte_ascii.Space).Add(gplx.ios.Io_size_.Units_bry[pow]); + if (suffix != null) + bfr.Add(suffix); + } +} diff --git a/100_core/src_200_io/gplx/ios/Io_size__tst.java b/100_core/src_200_io/gplx/ios/Io_size__tst.java new file mode 100644 index 000000000..bfd470345 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_size__tst.java @@ -0,0 +1,56 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class Io_size__tst { + @Test public void XtoLong() { + tst_XtoLong("1", 1); + tst_XtoLong("1 KB", 1024); + tst_XtoLong("1 MB", 1024 * 1024); + tst_XtoLong("1 GB", 1024 * 1024 * 1024); + tst_XtoLong("12 kb", 12 * 1024); + tst_XtoLong("1.5 kb", 1024 + 512); // 1536 + tst_XtoLong("1.5 mb", (long)(1024 * 1024 * 1.5)); + tst_XtoLong("-1", -1); // NOTE: negative bytes allowed + + tst_XtoLongFail("1 kilobite"); + tst_XtoLongFail("1 BB"); + tst_XtoLongFail("1.1"); + tst_XtoLongFail("1.51 kb"); + } + void tst_XtoLong(String raw, long expd) {Tfds.Eq(expd, Io_size_.parse_or_(raw, Long_.MinValue));} + void tst_XtoLongFail(String raw) { + long val = Io_size_.parse_or_(raw, Long_.MinValue); + if (val != Long_.MinValue) Tfds.Fail("expd parse failure; raw=" + raw); + } + @Test public void XtoStr() { + tst_XtoStr(1, "1.000 B"); + tst_XtoStr(1024, "1.000 KB"); + tst_XtoStr(1536, "1.500 KB"); + tst_XtoStr(1024 * 1024, "1.000 MB"); + tst_XtoStr(1016, "1,016.000 B"); // NOTE: 1016 is not 1.016 KB + } void tst_XtoStr(long val, String expd) {Tfds.Eq(expd, Io_size_.Xto_str(val));} + @Test public void EqualsTest() { + tst_Equals("1", "1"); + tst_Equals("1 kb", "1 kb"); + tst_Equals("1024", "1 kb"); + tst_Equals("1048576", "1 mb"); + tst_Equals("1024 kb", "1 mb"); + tst_Equals("1.5 kb", "1536 b"); + } void tst_Equals(String lhs, String rhs) {Tfds.Eq(Io_size_.parse_or_(lhs, Long_.MinValue), Io_size_.parse_or_(rhs, Long_.MinValue));} +} diff --git a/100_core/src_200_io/gplx/ios/Io_stream_.java b/100_core/src_200_io/gplx/ios/Io_stream_.java new file mode 100644 index 000000000..d9006d71c --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_stream_.java @@ -0,0 +1,22 @@ +/* +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.ios; import gplx.*; +public class Io_stream_ { // SERIALIZED + public static final byte Tid_null = 0, Tid_file = 1, Tid_zip = 2, Tid_gzip = 3, Tid_bzip2 = 4; + public static final String Ext_zip = ".zip", Ext_gz = ".gz", Ext_bz2 = ".bz2"; +} diff --git a/100_core/src_200_io/gplx/ios/Io_stream_rdr.java b/100_core/src_200_io/gplx/ios/Io_stream_rdr.java new file mode 100644 index 000000000..46884a7c9 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_stream_rdr.java @@ -0,0 +1,29 @@ +/* +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.ios; import gplx.*; +public interface Io_stream_rdr extends RlsAble { + byte Tid(); + Io_url Url(); Io_stream_rdr Url_(Io_url v); + long Len(); Io_stream_rdr Len_(long v); + Io_stream_rdr Open(); + void Open_mem(byte[] v); + Object Under(); + + int Read(byte[] bry, int bgn, int len); + long Skip(long len); +} diff --git a/100_core/src_200_io/gplx/ios/Io_stream_rdr_.java b/100_core/src_200_io/gplx/ios/Io_stream_rdr_.java new file mode 100644 index 000000000..31d880a08 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_stream_rdr_.java @@ -0,0 +1,266 @@ +/* +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.ios; import gplx.*; +public class Io_stream_rdr_ { + public static Io_stream_rdr file_(Io_url url) {return new Io_stream_rdr_file().Url_(url);} + public static Io_stream_rdr file_(java.io.InputStream strm) {return new Io_stream_rdr_file().Under_(strm);} + public static Io_stream_rdr zip_(Io_url url) {return new Io_stream_rdr_zip().Url_(url);} + public static Io_stream_rdr gzip_(Io_url url) {return new Io_stream_rdr_gzip().Url_(url);} + public static Io_stream_rdr bzip2_(Io_url url) {return new Io_stream_rdr_bzip2().Url_(url);} + public static Io_stream_rdr new_by_url_(Io_url url) { + String ext = url.Ext(); + if (String_.Eq(ext, Io_stream_.Ext_zip)) return gplx.ios.Io_stream_rdr_.zip_(url); + else if (String_.Eq(ext, Io_stream_.Ext_gz)) return gplx.ios.Io_stream_rdr_.gzip_(url); + else if (String_.Eq(ext, Io_stream_.Ext_bz2)) return gplx.ios.Io_stream_rdr_.bzip2_(url); + else return gplx.ios.Io_stream_rdr_.file_(url); + } + public static Io_stream_rdr new_by_tid_(byte tid) { + switch (tid) { + case Io_stream_.Tid_file: return new Io_stream_rdr_file(); + case Io_stream_.Tid_zip: return new Io_stream_rdr_zip(); + case Io_stream_.Tid_gzip: return new Io_stream_rdr_gzip(); + case Io_stream_.Tid_bzip2: return new Io_stream_rdr_bzip2(); + default: throw Err_.unhandled(tid); + } + } + public static byte[] Load_all(Io_url url) { + Io_stream_rdr rdr = new_by_url_(url); + Bry_bfr rv = Bry_bfr.new_(); + try { + rdr.Open(); + return Load_all_as_bry(rv, rdr); + } + finally {rdr.Rls();} + } + public static String Load_all_as_str(Io_stream_rdr rdr) {return String_.new_utf8_(Load_all_as_bry(rdr));} + public static byte[] Load_all_as_bry(Io_stream_rdr rdr) {return Load_all_as_bry(Bry_bfr.new_(), rdr);} + public static byte[] Load_all_as_bry(Bry_bfr rv, Io_stream_rdr rdr) { + Load_all_to_bfr(rv, rdr); + return rv.XtoAryAndClear(); + } + public static void Load_all_to_bfr(Bry_bfr rv, Io_stream_rdr rdr) { + try { + byte[] bry = new byte[4096]; + while (true) { + int read = rdr.Read(bry, 0, 4096); + if (read < gplx.ios.Io_stream_rdr_.Read_done_compare) break; + rv.Add_mid(bry, 0, read); + } + } finally {rdr.Rls();} + } + public static final Io_stream_rdr Null = new Io_stream_rdr_null(); + public static Io_stream_rdr mem_(String v) {return mem_(Bry_.new_utf8_(v));} + public static Io_stream_rdr mem_(byte[] v) { + Io_stream_rdr rv = new Io_stream_rdr_adp(Stream_new_mem(v)); + rv.Len_(v.length); + return rv; + } + public static java.io.InputStream Stream_new_mem(byte[] v) { + return new java.io.ByteArrayInputStream(v); + } + public static boolean Stream_close(java.io.InputStream stream) { + try { + if (stream != null) + stream.close(); + return true; + } catch (Exception e) {Err_.Noop(e); return false;} + } + public static int Stream_read_by_parts(java.io.InputStream stream, int part_len, byte[] bry, int bgn, int len) { + /* + NOTE: BZip2CompressorInputStream will fail if large len is used + Instead, make smaller requests and fill bry + */ + try { + int rv = 0; + int end = bgn + len; + int cur = bgn; + while (true) { + int bry_len = part_len; // read in increments of part_len + if (cur + bry_len > end) // if cur + 8 kb > bry_len, trim to end; EX: 9 kb bry passed; 1st pass is 8kb, 2nd pass should be 1kb, not 8 kb; + bry_len = end - cur; + if (cur == end) break; // no more bytes needed; break; EX: 8 kb bry passed; 1st pass is 8kb; 2nd pass is 0 and cur == end + int read = stream.read(bry, cur, bry_len); + if (read == gplx.ios.Io_stream_rdr_.Read_done) // read done; end + break; + rv += read; + cur += read; + } + return rv; + } + catch (Exception exc) { + throw Err_.new_fmt_("read failed: bgn={0} len={1} exc={2}", bgn, len, Err_.Message_gplx(exc)); + } + } + public static final int Read_done = -1; + public static final int Read_done_compare = 1; +} +class Io_stream_rdr_null implements Io_stream_rdr { + public Object Under() {return null;} + public byte Tid() {return Io_stream_.Tid_null;} + public Io_url Url() {return Io_url_.Null;} public Io_stream_rdr Url_(Io_url v) {return this;} + public long Len() {return Io_mgr.Len_null;} public Io_stream_rdr Len_(long v) {return this;} + public void Open_mem(byte[] v) {} + public Io_stream_rdr Open() {return this;} + public int Read(byte[] bry, int bgn, int len) {return Io_stream_rdr_.Read_done;} + public long Skip(long len) {return Io_stream_rdr_.Read_done;} + public void Rls() {} +} +class Io_stream_rdr_adp implements Io_stream_rdr { + private java.io.InputStream strm; + public Io_stream_rdr_adp(java.io.InputStream strm) {this.strm = strm;} + public Object Under() {return strm;} + public byte Tid() {return Io_stream_.Tid_file;} + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {this.url = v; return this;} private Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = Io_mgr.Len_null; + public void Open_mem(byte[] v) {} + public Io_stream_rdr Open() {return this;} + public int Read(byte[] bry, int bgn, int len) { + try {return strm.read(bry, bgn, len);} + catch (Exception exc) {Err_.Noop(exc); throw Err_.new_fmt_("read failed: bgn={0} len={1}", bgn, len);} + } + public long Skip(long len) { + try {return strm.skip(len);} + catch (Exception exc) {Err_.Noop(exc); throw Err_.new_fmt_("skip failed: len={0}", len);} + } + public void Rls() { + try {strm.close();} + catch (Exception exc) {Err_.Noop(exc); throw Err_.new_fmt_("close failed: url={0}", url.Xto_api());} + } +} +abstract class Io_stream_rdr_base implements Io_stream_rdr { + public abstract byte Tid(); + public Object Under() {return stream;} public Io_stream_rdr Under_(java.io.InputStream v) {this.stream = v; return this;} protected java.io.InputStream stream; + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {this.url = v; return this;} protected Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = Io_mgr.Len_null; + public void Open_mem(byte[] v) { + stream = Wrap_stream(new java.io.ByteArrayInputStream(v)); + } + public Io_stream_rdr Open() { + try {stream = Wrap_stream(new java.io.FileInputStream(url.Xto_api()));} + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Xto_api());} + return this; + } + public int Read(byte[] bry, int bgn, int len) { + try {return stream.read(bry, bgn, len);} + catch (Exception exc) {throw Err_.new_fmt_("read failed: bgn={0} len={1}", bgn, len);} + } + public long Skip(long len) { + try {return stream.skip(len);} + catch (Exception exc) {throw Err_.new_fmt_("skip failed: len={0}", len);} + } + public void Rls() { + try {stream.close();} + catch (Exception e) { + throw Err_.new_fmt_("close failed: url={0}", url.Xto_api()); + } + } + public abstract java.io.InputStream Wrap_stream(java.io.InputStream stream); +} +class Io_stream_rdr_file extends Io_stream_rdr_base { + @Override public byte Tid() {return Io_stream_.Tid_file;} + public Io_stream_rdr Open() { + try { + if (!Io_mgr._.Exists(url)) + stream = Wrap_stream(new java.io.ByteArrayInputStream(Bry_.Empty)); + else { + if (url.Info().EngineKey() == gplx.ios.IoEngine_.MemKey) + stream = Wrap_stream(new java.io.ByteArrayInputStream(Io_mgr._.LoadFilBry(url.Xto_api()))); + else + stream = Wrap_stream(new java.io.FileInputStream(url.Xto_api())); + } + } + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Xto_api());} + return this; + } + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) {return stream;} +} +class Io_stream_rdr_zip implements Io_stream_rdr { + @Override public byte Tid() {return Io_stream_.Tid_zip;} + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {this.url = v; return this;} Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len = Io_mgr.Len_null; + public Object Under() {return zip_stream;} private java.util.zip.ZipInputStream zip_stream; + public void Src_bfr_(Bry_bfr v) {this.src_bfr = v;} Bry_bfr src_bfr; + public void Open_mem(byte[] v) { + Wrap_stream(new java.io.ByteArrayInputStream(v)); + } + public Io_stream_rdr Open() { + try {Wrap_stream(new java.io.FileInputStream(url.Xto_api()));} + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Xto_api());} + return this; + } + void Wrap_stream(java.io.InputStream input_stream) {zip_stream = new java.util.zip.ZipInputStream(input_stream);} + public int Read(byte[] bry, int bgn, int len) { + try { + while (true){ + int read = zip_stream.read(bry, bgn, len); + if (read == gplx.ios.Io_stream_rdr_.Read_done) { + if (zip_stream.getNextEntry() == null) + return gplx.ios.Io_stream_rdr_.Read_done; + } + else + return read; + } + } + catch (Exception exc) {throw Err_.new_fmt_("read failed: bgn={0} len={1}", bgn, len);} + } + public long Skip(long len) { + try {return zip_stream.skip(len);} + catch (Exception exc) {throw Err_.new_fmt_("skip failed: len={0}", len);} + } + public void Rls() { + try {zip_stream.close();} + catch (Exception e) { + throw Err_.new_fmt_("close failed: url={0}", url.Xto_api()); + } + } +} +class Io_stream_rdr_gzip extends Io_stream_rdr_base { + @Override public byte Tid() {return Io_stream_.Tid_gzip;} + @Override public int Read(byte[] bry, int bgn, int len) { + try { + int total_read = 0; + while (true) { // NOTE: the gz stream reads partially; (request 100; only get back 10); keep reading until entire bfr is full or -1 + int read = stream.read(bry, bgn, len); + if (read == gplx.ios.Io_stream_rdr_.Read_done) break; + total_read += read; + if (total_read >= len) break; // entire bfr full; stop + bgn += read; // increase bgn by amount read + len -= read; // decrease len by amount read + } + return total_read == 0 ? gplx.ios.Io_stream_rdr_.Read_done : total_read; // gzip seems to allow 0 bytes read (bz2 and zip return -1 instead); normalize return to -1; + } + catch (Exception exc) { + throw Err_.new_fmt_("read failed: bgn={0} len={1}", bgn, len); + } + } + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) { + try {return new java.util.zip.GZIPInputStream(stream);} + catch (Exception exc) {throw Err_.new_fmt_("failed to open gz stream");} + } +} +class Io_stream_rdr_bzip2 extends Io_stream_rdr_base { + @Override public byte Tid() {return Io_stream_.Tid_bzip2;} + @Override public java.io.InputStream Wrap_stream(java.io.InputStream stream) { + try {return new org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream(stream, true);} + catch (Exception exc) {throw Err_.new_fmt_("failed to open bzip2 stream");} + } + @Override public int Read(byte[] bry, int bgn, int len) { + return Io_stream_rdr_.Stream_read_by_parts(stream, Read_len, bry, bgn, len); + } + private static final int Read_len = Io_mgr.Len_mb * 128; +} diff --git a/100_core/src_200_io/gplx/ios/Io_stream_rdr_tst.java b/100_core/src_200_io/gplx/ios/Io_stream_rdr_tst.java new file mode 100644 index 000000000..a34d6c25e --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_stream_rdr_tst.java @@ -0,0 +1,56 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class Io_stream_rdr_tst { + @Before public void init() {fxt.Clear();} private Io_stream_rdr_fxt fxt = new Io_stream_rdr_fxt(); + @After public void term() {fxt.Rls();} + @Test public void Bz2_read() { + fxt .Init_stream("abcd") // read everything at once + .Expd_bytes_read(4).Test_read(0, 4, "abcd"); + fxt .Init_stream("abcd") // read in steps + .Expd_bytes_read(1).Test_read(0, 1, "a") + .Expd_bytes_read(2).Test_read(1, 2, "bc") + .Expd_bytes_read(1).Test_read(3, 1, "d") + ; + } +} +class Io_stream_rdr_fxt { + private java.io.InputStream stream; + private int stream_bry_len; + public void Clear() { + expd_bytes_read = Int_.MinValue; + } + public Io_stream_rdr_fxt Expd_bytes_read(int v) {expd_bytes_read = v; return this;} private int expd_bytes_read = Int_.MinValue; + public Io_stream_rdr_fxt Init_stream(String v) { + byte[] stream_bry = Bry_.new_ascii_(v); + stream_bry_len = stream_bry.length; + stream = Io_stream_rdr_.Stream_new_mem(stream_bry); + return this; + } + public Io_stream_rdr_fxt Test_read(int bgn, int len, String expd_str) { + byte[] bfr = new byte[stream_bry_len]; // allocate whole stream; may not use it all + int actl_bytes_read = Io_stream_rdr_.Stream_read_by_parts(stream, 8, bfr, bgn, len); + Tfds.Eq(expd_bytes_read, actl_bytes_read, "bytes_read"); + Tfds.Eq(expd_str, String_.new_utf8_(bfr, bgn, bgn + actl_bytes_read), "str"); + return this; + } + public void Rls() { + Io_stream_rdr_.Stream_close(stream); + } +} diff --git a/100_core/src_200_io/gplx/ios/Io_stream_wtr.java b/100_core/src_200_io/gplx/ios/Io_stream_wtr.java new file mode 100644 index 000000000..c4c432487 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_stream_wtr.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public interface Io_stream_wtr extends RlsAble { + byte Tid(); + Io_url Url(); Io_stream_wtr Url_(Io_url v); + void Trg_bfr_(Bry_bfr v); + Io_stream_wtr Open(); + byte[] Xto_ary_and_clear(); + + void Write(byte[] bry, int bgn, int len); + void Flush(); +} diff --git a/100_core/src_200_io/gplx/ios/Io_stream_wtr_.java b/100_core/src_200_io/gplx/ios/Io_stream_wtr_.java new file mode 100644 index 000000000..0907706c2 --- /dev/null +++ b/100_core/src_200_io/gplx/ios/Io_stream_wtr_.java @@ -0,0 +1,208 @@ +/* +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.ios; import gplx.*; +public class Io_stream_wtr_ { + public static Io_stream_wtr bzip2_(Io_url url) {return new Io_stream_wtr_bzip2().Url_(url);} + public static Io_stream_wtr gzip_(Io_url url) {return new Io_stream_wtr_gzip().Url_(url);} + public static Io_stream_wtr zip_(Io_url url) {return new Io_stream_wtr_zip().Url_(url);} + public static Io_stream_wtr file_(Io_url url) {return new Io_stream_wtr_file().Url_(url);} + public static Io_stream_wtr new_by_url_(Io_url url) { + String ext = url.Ext(); + if (String_.Eq(ext, Io_stream_.Ext_zip)) return gplx.ios.Io_stream_wtr_.zip_(url); + else if (String_.Eq(ext, Io_stream_.Ext_gz)) return gplx.ios.Io_stream_wtr_.gzip_(url); + else if (String_.Eq(ext, Io_stream_.Ext_bz2)) return gplx.ios.Io_stream_wtr_.bzip2_(url); + else return gplx.ios.Io_stream_wtr_.file_(url); + } + public static Io_stream_wtr new_by_mem(Bry_bfr bfr, byte tid) { + Io_stream_wtr wtr = new_by_tid_(tid).Url_(Io_url_.Null); + wtr.Trg_bfr_(bfr); + return wtr; + } + public static Io_stream_wtr new_by_tid_(byte v) { + switch (v) { + case gplx.ios.Io_stream_.Tid_file : return new Io_stream_wtr_file(); + case gplx.ios.Io_stream_.Tid_zip : return new Io_stream_wtr_zip(); + case gplx.ios.Io_stream_.Tid_gzip : return new Io_stream_wtr_gzip(); + case gplx.ios.Io_stream_.Tid_bzip2 : return new Io_stream_wtr_bzip2(); + default : throw Err_.unhandled(v); + } + } + public static void Save_all(Io_url url, byte[] bry, int bgn, int end) { + Io_stream_wtr wtr = new_by_url_(url); + try { + wtr.Open(); + wtr.Write(bry, bgn, end); + wtr.Flush(); + } + finally {wtr.Rls();} + } + public static void Save_rdr(Io_url url, Io_stream_rdr rdr) { + byte[] bry = new byte[4096]; + Io_stream_wtr wtr = new_by_url_(url); + try { + wtr.Open(); + while (true) { + int read = rdr.Read(bry, 0, 4096); + if (read < gplx.ios.Io_stream_rdr_.Read_done_compare) break; + wtr.Write(bry, 0, read); + } + wtr.Flush(); + } + finally {wtr.Rls(); rdr.Rls();} + } +} +abstract class Io_stream_wtr_base implements Io_stream_wtr { + java.io.OutputStream zip_stream; + public Io_url Url() {return url;} public Io_stream_wtr Url_(Io_url v) {url = v; trg_bfr = null; return this;} Io_url url; + public void Trg_bfr_(Bry_bfr v) {trg_bfr = v;} Bry_bfr trg_bfr; java.io.ByteArrayOutputStream mem_stream; + public byte[] Xto_ary_and_clear() {return trg_bfr.XtoAryAndClear();} + @SuppressWarnings("resource") // rely on OutputStream to close bry_stream + public Io_stream_wtr Open() { + java.io.OutputStream bry_stream = null; + if (trg_bfr == null) { + if (!Io_mgr._.ExistsFil(url)) Io_mgr._.SaveFilStr(url, ""); + try {bry_stream = new java.io.FileOutputStream(url.Raw());} + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Raw());} + } + else { + mem_stream = new java.io.ByteArrayOutputStream(); + bry_stream = mem_stream; + } + zip_stream = Wrap_stream(bry_stream); + return this; + } + public void Write(byte[] bry, int bgn, int len) { + try {zip_stream.write(bry, bgn, len);} + catch (Exception exc) {throw Err_.new_fmt_("write failed: bgn={0} len={1}", bgn, len);} + } + public void Flush() { + if (trg_bfr != null) { + try {zip_stream.close();} catch (Exception exc) {throw Err_.new_fmt_("flush failed");} // must close zip_stream to flush all bytes + trg_bfr.Add(mem_stream.toByteArray()); + } + } + public void Rls() { + try { + if (zip_stream != null) zip_stream.close(); + if (mem_stream != null) mem_stream.close(); + } + catch (Exception e) {throw Err_.new_fmt_("close failed: url={0}", url.Raw());} + } + public abstract java.io.OutputStream Wrap_stream(java.io.OutputStream stream); +} +class Io_stream_wtr_bzip2 extends Io_stream_wtr_base { + @Override public byte Tid() {return Io_stream_.Tid_bzip2;} + @Override public java.io.OutputStream Wrap_stream(java.io.OutputStream stream) { + try {return new org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream(stream);} + catch (Exception exc) {throw Err_.new_fmt_("failed to open bzip2 stream");} + } + static final byte[] Bz2_header = new byte[] {Byte_ascii.Ltr_B, Byte_ascii.Ltr_Z}; +} +class Io_stream_wtr_gzip extends Io_stream_wtr_base { + @Override public byte Tid() {return Io_stream_.Tid_gzip;} + @Override public java.io.OutputStream Wrap_stream(java.io.OutputStream stream) { + try {return new java.util.zip.GZIPOutputStream(stream);} + catch (Exception exc) {throw Err_.new_fmt_("failed to open gz stream");} + } +} +class Io_stream_wtr_zip implements Io_stream_wtr { + private java.util.zip.ZipOutputStream zip_stream; + @Override public byte Tid() {return Io_stream_.Tid_zip;} + public Io_url Url() {return url;} public Io_stream_wtr Url_(Io_url v) {url = v; trg_bfr = null; return this;} private Io_url url = Io_url_.Null; + public void Trg_bfr_(Bry_bfr v) {trg_bfr = v;} private Bry_bfr trg_bfr; private java.io.ByteArrayOutputStream mem_stream; + @SuppressWarnings("resource") // rely on zip_stream to close bry_stream + public Io_stream_wtr Open() { + java.io.OutputStream bry_stream; + if (trg_bfr == null) { + if (!Io_mgr._.ExistsFil(url)) Io_mgr._.SaveFilStr(url, ""); // create file if it doesn't exist + try {bry_stream = new java.io.FileOutputStream(url.Xto_api());} + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Raw());} + } + else { + mem_stream = new java.io.ByteArrayOutputStream(); + bry_stream = mem_stream; + } + zip_stream = new java.util.zip.ZipOutputStream(bry_stream); + java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry("file"); + try {zip_stream.putNextEntry(entry);} + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Raw());} + return this; + } + public void Write(byte[] bry, int bgn, int len) { + try {zip_stream.write(bry, bgn, len);} + catch (Exception exc) {throw Err_.new_fmt_("write failed: url={0} bgn={1} len={2}", url.Raw(), bgn, len);} + } + public void Flush() {// fixed as of DATE:2014-04-15 + try { + zip_stream.closeEntry(); + zip_stream.close(); + if (trg_bfr != null) + trg_bfr.Add(mem_stream.toByteArray()); + zip_stream.flush(); + } + catch (Exception e) {throw Err_.new_fmt_("flush failed: url={0}", url.Raw());} + } + public void Rls() { + try { + if (zip_stream != null) zip_stream.close(); + if (mem_stream != null) mem_stream.close(); + } + catch (Exception e) {throw Err_.new_fmt_("close failed: url={0}", url.Raw());} + } + public byte[] Xto_ary_and_clear() { + byte[] rv = trg_bfr.XtoAryAndClear(); + this.Rls(); + return rv; + } +} +class Io_stream_wtr_file implements Io_stream_wtr { + IoStream bry_stream; + @Override public byte Tid() {return Io_stream_.Tid_file;} + public Io_url Url() {return url;} public Io_stream_wtr Url_(Io_url v) {url = v; return this;} Io_url url; + public void Trg_bfr_(Bry_bfr v) {trg_bfr = v;} private Bry_bfr trg_bfr; java.io.ByteArrayOutputStream mem_stream; + public Io_stream_wtr Open() { + try { + if (trg_bfr == null) + bry_stream = Io_mgr._.OpenStreamWrite(url); + } + catch (Exception exc) {throw Err_.new_fmt_("open failed: url={0}", url.Raw());} + return this; + } + public void Write(byte[] bry, int bgn, int len) { + if (trg_bfr == null) { + try {bry_stream.Write(bry, bgn, len);} + catch (Exception exc) {throw Err_.new_fmt_("write failed: url={0} bgn={1} len={2}", url.Raw(), bgn, len);} + } + else + trg_bfr.Add_mid(bry, bgn, bgn + len); + } + public byte[] Xto_ary_and_clear() { + return trg_bfr == null ? Io_mgr._.LoadFilBry(url) : trg_bfr.XtoAryAndClear(); + } + public void Flush() { + if (trg_bfr == null) + bry_stream.Flush(); + } + public void Rls() { + try { + if (trg_bfr == null) + bry_stream.Rls(); + } + catch (Exception e) {throw Err_.new_fmt_("close failed: url={0}", url.Raw());} + } +} diff --git a/100_core/src_210_env/gplx/ClassAdp_.java b/100_core/src_210_env/gplx/ClassAdp_.java new file mode 100644 index 000000000..ead2e4b12 --- /dev/null +++ b/100_core/src_210_env/gplx/ClassAdp_.java @@ -0,0 +1,45 @@ +/* +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; +public class ClassAdp_ { + public static boolean Eq(Class lhs, Class rhs) { + if (lhs == null && rhs == null) return true; + else if (lhs == null || rhs == null) return false; + else return lhs.equals(rhs); + } + public static boolean Eq_typeSafe(Object o, Class expd) {if (o == null) return false; + Class actl = o.getClass(); + return Object_.Eq(expd, actl); + } + public static boolean IsAssignableFrom(Class lhs, Class rhs) {return lhs.isAssignableFrom(rhs);} + public static boolean Is_array(Class t) {return t.isArray();} + public static Class ClassOf_obj(Object o) {return o.getClass();} + public static Class ClassOf_primitive(Object o) { + Class rv = o.getClass(); + if (rv == Integer.class) rv = int.class; + else if (rv == Long.class) rv = long.class; + else if (rv == Byte.class) rv = byte.class; + else if (rv == Short.class) rv = short.class; + return rv; + } + public static String FullNameOf_obj(Object o) {return FullNameOf_type(o.getClass());} + public static String FullNameOf_type(Class type) {return type.getCanonicalName();} + public static String NameOf_type(Class type) {return type.getName();} + public static String NameOf_obj(Object obj) {return obj == null ? String_.Null_mark : obj.getClass().getName();} + public static final byte Tid_bool = 1, Tid_byte = 2, Tid_int = 3, Tid_long = 4, Tid_float = 5, Tid_double = 6, Tid_char = 7, Tid_str = 8, Tid_date = 9, Tid_decimal = 10; +} diff --git a/100_core/src_210_env/gplx/Env_.java b/100_core/src_210_env/gplx/Env_.java new file mode 100644 index 000000000..3a83c408b --- /dev/null +++ b/100_core/src_210_env/gplx/Env_.java @@ -0,0 +1,96 @@ +/* +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; +import gplx.threads.*; +public class Env_ { + public static void Init(String[] args, String appNameAndExt, Class type) { + mode_testing = false; + mode_debug = String_.In("GPLX_DEBUG_MODE_ENABLED", args); + appArgs = args; + appUrl = JarAdp_.Url_type(type).OwnerDir().GenSubFil(appNameAndExt); + } + public static void Init_swt(String[] args, Class type) { // DATE:2014-06-23 + mode_testing = false; + mode_debug = String_.In("GPLX_DEBUG_MODE_ENABLED", args); + appArgs = args; + appUrl = JarAdp_.Url_type(type); + } + public static void Init_drd(String[] args, Io_url url) { + mode_testing = mode_debug = false; + appArgs = args; + appUrl = url; + } + public static void Init_testing() {mode_testing = true;} + public static boolean Mode_testing() {return mode_testing;} static boolean mode_testing = true; + public static boolean Mode_debug() {return mode_debug;} static boolean mode_debug = false; + public static String[] AppArgs() {return appArgs;} static String[] appArgs; + public static Io_url AppUrl() { + if (mode_testing) return Io_url_.mem_fil_("mem/testing.jar"); + if (appUrl == Io_url_.Null) throw Err_.new_("Env_.Init was not called"); + return appUrl; + } static Io_url appUrl = Io_url_.Null; + public static void Exit() {Exit_code(0);} + public static void Exit_code(int code) {System.exit(code);} + public static String UserName() {return System.getProperty("user.name");} + public static void GarbageCollect() {if (mode_testing) return; System.gc();} + public static long TickCount() {return TickCount_Test >= 0 ? TickCount_Test : System.currentTimeMillis();} + public static int TickCount_elapsed_in_sec(long time_bgn) {return (int)(Env_.TickCount() - time_bgn) / 1000;} + public static int TickCount_elapsed_in_frac(long time_bgn) {return (int)(Env_.TickCount() - time_bgn);} + public static long TickCount_Test = -1; // in milliseconds + public static void TickCount_normal() {TickCount_Test = -1;} + public static long System_memory_free() {return Runtime.getRuntime().freeMemory();} + public static final String LocalHost = "127.0.0.1"; + public static String NewLine_lang() {return mode_testing ? "\n" : "\n";} + public static String GenHdr(boolean forSourceCode, String programName, String hdr_bgn, String hdr_end) { + String newLine = Op_sys.Lnx.Nl_str(); + String lineEnd = Op_sys.Lnx.Nl_str(); + String codeBgn = forSourceCode ? "/*" + newLine : ""; + String codeEnd = forSourceCode ? "*/" + newLine : ""; + String codeHdr = forSourceCode ? "This file is part of {0}." + newLine + newLine : ""; + String fmt = String_.Concat + ( codeBgn + , codeHdr + , hdr_bgn + , "Copyright (c) 2012 gnosygnu@gmail.com", newLine + , newLine + , "This program is free software: you can redistribute it and/or modify", lineEnd + , "it under the terms of the GNU Affero General Public License as", lineEnd + , "published by the Free Software Foundation, either version 3 of the", lineEnd + , "License, or (at your option) any later version.", newLine + , newLine + , "This program is distributed in the hope that it will be useful,", lineEnd + , "but WITHOUT ANY WARRANTY; without even the implied warranty of", lineEnd + , "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", lineEnd + , "GNU Affero General Public License for more details.", newLine + , newLine + , "You should have received a copy of the GNU Affero General Public License", lineEnd + , "along with this program. If not, see .", newLine + , codeEnd + , hdr_end + ); + return String_.Format(fmt, programName); + } + public static String Env_prop__user_language() {return Env_prop(Env_prop_key__user_language);} + public static String Env_prop(String key) { + return System.getProperty(key); + } static final String Env_prop_key__user_language = "user.language"; + public static void Term_add(GfoInvkAble invk, String cmd) { + ThreadAdp thread = ThreadAdp_.invk_(invk, cmd); + Runtime.getRuntime().addShutdownHook(thread.Under_thread()); + } +} diff --git a/100_core/src_210_env/gplx/JarAdp_.java b/100_core/src_210_env/gplx/JarAdp_.java new file mode 100644 index 000000000..1c2134215 --- /dev/null +++ b/100_core/src_210_env/gplx/JarAdp_.java @@ -0,0 +1,33 @@ +/* +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; +public class JarAdp_ { + public static DateAdp ModifiedTime_type(Class type) {if (type == null) throw Err_.null_("type"); + Io_url url = Url_type(type); + return Io_mgr._.QueryFil(url).ModifiedTime(); + } + public static Io_url Url_type(Class type) {if (type == null) throw Err_.null_("type"); + String codeBase = type.getProtectionDomain().getCodeSource().getLocation().getPath(); + if (Op_sys.Cur().Tid_is_wnt()) + codeBase = String_.Mid(codeBase, 1); // codebase always starts with /; remove for wnt + codeBase = String_.Replace(codeBase, "/", Op_sys.Cur().Fsys_dir_spr_str()); // java always returns DirSpr as /; change to Env_.DirSpr to handle windows + try {codeBase = java.net.URLDecoder.decode(codeBase, "UTF-8");} + catch (java.io.UnsupportedEncodingException e) {Err_.Noop(e);} + return Io_url_.new_fil_(codeBase); + } +} diff --git a/100_core/src_210_env/gplx/Op_sys.java b/100_core/src_210_env/gplx/Op_sys.java new file mode 100644 index 000000000..69d997aea --- /dev/null +++ b/100_core/src_210_env/gplx/Op_sys.java @@ -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 . +*/ +package gplx; +public class Op_sys { + Op_sys(byte tid, byte sub_tid, String os_name, byte bitness, String nl_str, byte fsys_dir_spr_byte, boolean fsys_case_match, byte[] fsys_invalid_chars) {this.tid = tid; this.sub_tid = sub_tid; this.os_name = os_name; this.bitness = bitness; this.nl_str = nl_str; this.fsys_dir_spr_byte = fsys_dir_spr_byte; this.fsys_dir_spr_str = Char_.XtoStr((char)fsys_dir_spr_byte); this.fsys_case_match = fsys_case_match; this.fsys_invalid_chars = fsys_invalid_chars;} + public byte Tid() {return tid;} final byte tid; + public byte Sub_tid() {return sub_tid;} final byte sub_tid; + public String Os_name() {return os_name;} private String os_name; + public byte Bitness() {return bitness;} final byte bitness; + public String Nl_str() {return nl_str;} final String nl_str; + public String Fsys_dir_spr_str() {return fsys_dir_spr_str;} final String fsys_dir_spr_str; + public byte Fsys_dir_spr_byte() {return fsys_dir_spr_byte;} final byte fsys_dir_spr_byte; + public String Fsys_http_frag_to_url_str(String raw) {return fsys_dir_spr_byte == Byte_ascii.Slash ? raw : String_.Replace(raw, Lnx.Fsys_dir_spr_str(), fsys_dir_spr_str);} + public boolean Fsys_case_match() {return fsys_case_match;} final boolean fsys_case_match; + public byte[] Fsys_invalid_chars() {return fsys_invalid_chars;} final byte[] fsys_invalid_chars; + public String Fsys_case_match_str(String s) {return String_.CaseNormalize(fsys_case_match, s);} + public boolean Tid_is_wnt() {return tid == Tid_wnt;} + public boolean Tid_is_lnx() {return tid == Tid_lnx;} + public boolean Tid_is_osx() {return tid == Tid_osx;} + public boolean Tid_is_drd() {return tid == Tid_drd;} + public String Xto_str() {return os_name + (bitness == Bitness_32 ? "32" : "64");} + + public static final byte Tid_nil = 0, Tid_wnt = 1, Tid_lnx = 2, Tid_osx = 3, Tid_drd = 4; + public static final byte Sub_tid_unknown = 0, Sub_tid_win_xp = 1, Sub_tid_win_7 = 2, Sub_tid_win_8 = 3; + public static final byte Bitness_32 = 1, Bitness_64 = 2; + public static final char Dir_spr_char_lnx = '\n'; + + public static final Op_sys Lnx = new_unx_flavor_(Tid_lnx, "linux", Bitness_32); + public static final Op_sys Osx = new_unx_flavor_(Tid_osx, "macosx", Bitness_32); + public static final Op_sys Drd = new_unx_flavor_(Tid_drd, "windows", Bitness_32); + public static final Op_sys Wnt = new_wnt_(Sub_tid_unknown, Bitness_32); + public static Op_sys Cur() {return cur_op_sys;} static Op_sys cur_op_sys = new_auto_identify_(); + static Op_sys new_wnt_(byte bitness, byte sub_tid) {return new Op_sys(Tid_wnt , sub_tid , "windows", bitness, "\r\n", Byte_ascii.Backslash , Bool_.N, new byte[] {Byte_ascii.Slash, Byte_ascii.Backslash, Byte_ascii.Lt, Byte_ascii.Gt, Byte_ascii.Colon, Byte_ascii.Pipe, Byte_ascii.Question, Byte_ascii.Asterisk, Byte_ascii.Quote});} + static Op_sys new_unx_flavor_(byte tid, String os_name, byte bitness) {return new Op_sys(tid , Sub_tid_unknown , os_name , bitness, "\n" , Byte_ascii.Slash , Bool_.Y, new byte[] {Byte_ascii.Slash});} + static final String GRP_KEY = "gplx.op_sys"; +// public static Op_sys Cur_() {cur_op_sys = new_auto_identify_(); return cur_op_sys;} + static Op_sys new_auto_identify_() { + String os_name = ""; + try { + String bitness_str = System.getProperty("sun.arch.data.model"); if (bitness_str == null) return Drd; + bitness_str = bitness_str.toLowerCase(); + byte bitness_byte = Bitness_32; + if (String_.Eq(bitness_str, "32")) bitness_byte = Bitness_32; + else if (String_.Eq(bitness_str, "64")) bitness_byte = Bitness_64; + else throw Err_mgr._.fmt_(GRP_KEY, "unknown_bitness", "unknown bitness; expecting 32 or 64; System.getProperty(\"bit.level\") yielded ~{0}", bitness_str); + + os_name = System.getProperty("os.name").toLowerCase(); + if (String_.HasAtBgn(os_name, "win")) { + String os_version = System.getProperty("os.version").toLowerCase();// "Windows 7".equals(osName) && "6.1".equals(osVersion); + byte sub_tid = Sub_tid_unknown; + if (String_.Eq(os_name, "windows xp") && String_.Eq(os_version, "5.1")) sub_tid = Sub_tid_win_xp; + else if (String_.Eq(os_name, "windows 7") && String_.Eq(os_version, "6.1")) sub_tid = Sub_tid_win_7; + else if (String_.Eq(os_name, "windows 8")) sub_tid = Sub_tid_win_8; + return new_wnt_(bitness_byte, sub_tid); + } + else if (String_.Eq(os_name, "linux")) return new_unx_flavor_(Tid_lnx, os_name, bitness_byte); + else if (String_.HasAtBgn(os_name, "mac")) return new_unx_flavor_(Tid_osx, os_name, bitness_byte); // EX:Mac OS X + else throw Err_mgr._.fmt_(GRP_KEY, "unknown_os_name", "unknown os_name; expecting windows, linux, mac; System.getProperty(\"os.name\") yielded ~{0}", os_name); + } catch (Exception exc) {Drd.os_name = os_name; return Drd;} + } + public static void OpSysIsDroid() { + cur_op_sys = Drd; + } + } diff --git a/100_core/src_210_env/gplx/ProcessAdp.java b/100_core/src_210_env/gplx/ProcessAdp.java new file mode 100644 index 000000000..5bf9cfd76 --- /dev/null +++ b/100_core/src_210_env/gplx/ProcessAdp.java @@ -0,0 +1,344 @@ +/* +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; +import gplx.threads.*; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import javax.management.RuntimeErrorException; +public class ProcessAdp implements GfoInvkAble, RlsAble { + public boolean Enabled() {return enabled;} public ProcessAdp Enabled_(boolean v) {enabled = v; return this;} private boolean enabled = true; + public byte Exe_exists() {return exe_exists;} public ProcessAdp Exe_exists_(byte v) {exe_exists = v; return this;} private byte exe_exists = Bool_.__byte; + public Io_url Exe_url() {return exe_url;} public ProcessAdp Exe_url_(Io_url val) {exe_url = val; exe_exists = Bool_.__byte; return this;} Io_url exe_url; + public String Args_str() {return args_str;} public ProcessAdp Args_str_(String val) {args_str = val; return this;} private String args_str = ""; + public Bry_fmtr Args_fmtr() {return args_fmtr;} Bry_fmtr args_fmtr = Bry_fmtr.new_(""); + public byte Run_mode() {return run_mode;} public ProcessAdp Run_mode_(byte v) {run_mode = v; return this;} private byte run_mode = Run_mode_sync_block; + public static final byte Run_mode_async = 0, Run_mode_sync_block = 1, Run_mode_sync_timeout = 2; + public int Exit_code() {return exit_code;} int exit_code; + public boolean Exit_code_pass() {return exit_code == Exit_pass;} + public String Rslt_out() {return rslt_out;} private String rslt_out; + public Io_url Working_dir() {return working_dir;} public ProcessAdp Working_dir_(Io_url v) {working_dir = v; return this;} Io_url working_dir; + public ProcessAdp Cmd_args(String cmd, String args) {this.Exe_url_(Io_url_.new_fil_(cmd)); this.args_fmtr.Fmt_(args); return this;} + public ProcessAdp WhenBgn_add(GfoInvkAbleCmd cmd) {whenBgnList.Add(cmd); return this;} + public ProcessAdp WhenBgn_del(GfoInvkAbleCmd cmd) {whenBgnList.Del(cmd); return this;} + public int Thread_timeout() {return thread_timeout;} public ProcessAdp Thread_timeout_seconds_(int v) {thread_timeout = v * 1000; return this;} int thread_timeout = 0; + public int Thread_interval() {return thread_interval;} public ProcessAdp Thread_interval_(int v) {thread_interval = v; return this;} int thread_interval = 20; + public String Thread_kill_name() {return thread_kill_name;} public ProcessAdp Thread_kill_name_(String v) {thread_kill_name = v; return this;} private String thread_kill_name = ""; + public Io_url Tmp_dir() {return tmp_dir;} @gplx.Virtual public ProcessAdp Tmp_dir_(Io_url v) {tmp_dir = v; return this;} Io_url tmp_dir; + private ProcessAdp WhenBgn_run() {return Invk_cmds(whenBgnList);} ListAdp whenBgnList = ListAdp_.new_(); + public ProcessAdp WhenEnd_add(GfoInvkAbleCmd cmd) {whenEndList.Add(cmd); return this;} + public ProcessAdp WhenEnd_del(GfoInvkAbleCmd cmd) {whenEndList.Del(cmd); return this;} + public Gfo_usr_dlg Prog_dlg() {return prog_dlg;} public ProcessAdp Prog_dlg_(Gfo_usr_dlg v) {prog_dlg = v; return this;} Gfo_usr_dlg prog_dlg; + public String Prog_fmt() {return prog_fmt;} public ProcessAdp Prog_fmt_(String v) {prog_fmt = v; return this;} private String prog_fmt = ""; // NOTE: set to "", else cmds that do not set prog_fmt will fail on fmtr.Fmt(null) + private GfoInvkAble owner; + private ProcessAdp WhenEnd_run() {return Invk_cmds(whenEndList);} ListAdp whenEndList = ListAdp_.new_(); + private ProcessAdp Invk_cmds(ListAdp list) { + for (Object o : list) + ((GfoInvkAbleCmd)o).Invk(); + return this; + } + public ProcessAdp Run(Object... args) { + if (String_.Len_eq_0(exe_url.Raw())) return this; // noop if exe_url is ""; + if (!args_fmtr.Fmt_null()) { + Bry_bfr tmp_bfr = Bry_bfr.new_(); + args_fmtr.Bld_bfr_many(tmp_bfr, args); + args_str = tmp_bfr.XtoStrAndClear(); + } + prog_dlg.Log_many(GRP_KEY, "run", "running process: ~{0} ~{1}", exe_url.Raw(), args_str); + exit_code = Exit_init; + switch (run_mode) { + case Run_mode_async: return Run_async(); + case Run_mode_sync_timeout: return Run_wait(); + case Run_mode_sync_block: return Run_wait_sync(); + default: throw Err_mgr._.unhandled_(run_mode); + } + } + public String[] X_to_process_bldr_args(String... args) { + String args_str = args_fmtr.Bld_str_many(args); + return X_to_process_bldr_args_utl(exe_url, args_str); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return enabled; + else if (ctx.Match(k, Invk_enabled_)) enabled = m.ReadBool("v"); + else if (ctx.Match(k, Invk_cmd)) return exe_url.Raw(); + else if (ctx.Match(k, Invk_cmd_)) this.Exe_url_(Bry_fmtr_eval_mgr_.Eval_url(cmd_url_eval, m.ReadBry("cmd"))); + else if (ctx.Match(k, Invk_args)) return String_.new_utf8_(args_fmtr.Fmt()); + else if (ctx.Match(k, Invk_args_)) args_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_cmd_args_)) {this.Exe_url_(Bry_fmtr_eval_mgr_.Eval_url(cmd_url_eval, m.ReadBry("cmd"))); args_fmtr.Fmt_(m.ReadBry("args"));} + else if (ctx.Match(k, Invk_mode_)) run_mode = m.ReadByte("v"); + else if (ctx.Match(k, Invk_timeout_)) thread_timeout = m.ReadInt("v"); + else if (ctx.Match(k, Invk_tmp_dir_)) tmp_dir = m.ReadIoUrl("v"); + else if (ctx.Match(k, Invk_owner)) return owner; + else return GfoInvkAble_.Rv_unhandled; + return this; + } + static final String Invk_cmd = "cmd", Invk_cmd_ = "cmd_", Invk_args = "args", Invk_args_ = "args_", Invk_cmd_args_ = "cmd_args_", Invk_enabled = "enabled", Invk_enabled_ = "enabled_", Invk_mode_ = "mode_", Invk_timeout_ = "timeout_", Invk_tmp_dir_ = "tmp_dir_", Invk_owner = "owner"; + Bry_fmtr_eval_mgr cmd_url_eval; + public static ProcessAdp ini_(GfoInvkAble owner, Gfo_usr_dlg gui_wtr, ProcessAdp process, Bry_fmtr_eval_mgr cmd_url_eval, byte run_mode, int timeout, String cmd_url_fmt, String args_fmt, String... args_keys) { + process.Run_mode_(run_mode).Thread_timeout_seconds_(timeout); + process.cmd_url_eval = cmd_url_eval; + Io_url cmd_url = Bry_fmtr_eval_mgr_.Eval_url(cmd_url_eval, Bry_.new_utf8_(cmd_url_fmt)); + process.Exe_url_(cmd_url).Tmp_dir_(cmd_url.OwnerDir()); + process.Args_fmtr().Fmt_(args_fmt).Keys_(args_keys); + process.owner = owner; + process.Prog_dlg_(gui_wtr); + return process; // return process for chaining + } + public static String Escape_ampersands_if_process_is_cmd(boolean os_is_wnt, String exe_url, String exe_args) { + return ( os_is_wnt + && String_.Eq(exe_url, "cmd")) + ? String_.Replace(exe_args, "&", "^&") // escape ampersands + : exe_args + ; + } + private Bry_fmtr notify_fmtr = Bry_fmtr.new_("", "process_exe_name", "process_exe_args", "process_seconds"); Bry_bfr notify_bfr = Bry_bfr.reset_(255); + public Process UnderProcess() {return process;} Process process; + public void Rls() {if (process != null) process.destroy();} + public ProcessAdp Run_wait_sync() { + if (Env_.Mode_testing()) return Test_runs_add(); + Process_bgn(); + Process_start(); + Process_run_and_end(); + return this; + } + public ProcessAdp Run_start() { + if (Env_.Mode_testing()) return Test_runs_add(); + Process_bgn(); + Process_start(); + return this; + } + public ProcessAdp Run_async() { + if (Env_.Mode_testing()) return Test_runs_add(); + Process_bgn(); + Thread_ProcessAdp_async thread = new Thread_ProcessAdp_async(this); + thread.start(); + return this; + } + public ProcessAdp Run_wait() { + if (Env_.Mode_testing()) return Test_runs_add(); + int notify_interval = 100; int notify_checkpoint = notify_interval; + int elapsed = 0; + try { + Process_bgn(); + Thread_ProcessAdp_sync thread = new Thread_ProcessAdp_sync(this); + thread.start(); + // thread_timeout = 15000; + boolean thread_run = false; + notify_fmtr.Fmt_(prog_fmt); + while (thread.isAlive()) { + thread_run = true; + long prv = Env_.TickCount(); + ThreadAdp_.Sleep(thread_interval); +// try {thread.join(thread_interval);} +// catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at join");} + long cur = Env_.TickCount(); + int dif = (int)(cur - prv); + elapsed += dif; + if (prog_dlg != null) { + if (elapsed > notify_checkpoint) { + elapsed = notify_checkpoint; + notify_checkpoint += notify_interval; + notify_fmtr.Bld_bfr_many(notify_bfr, exe_url.NameAndExt(), args_str, elapsed / 1000); + prog_dlg.Prog_none(GRP_KEY, "notify.prog", notify_bfr.XtoStrAndClear()); + } + } + if (thread_timeout == 0) break; + if (elapsed > thread_timeout) { + thread.interrupt(); + thread.Cancel(); + try {thread.join();} + catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at timeout");} + break; + } + } + if (!thread_run) { + try {thread.join();} + catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at join 2");} + } + } catch (Exception exc) { + Tfds.Write(Err_.Message_gplx_brief(exc)); + } + if (elapsed != notify_checkpoint) { + notify_fmtr.Bld_bfr_many(notify_bfr, exe_url.NameAndExt(), args_str, elapsed / 1000); + if (prog_dlg != null) prog_dlg.Prog_none(GRP_KEY, "notify.prog", notify_bfr.XtoStrAndClear()); + } + return this; + } + public synchronized void Process_post(String result) { + exit_code = process.exitValue(); + rslt_out = result; + WhenEnd_run(); + process.destroy(); + } + String Kill() { + if (thread_kill_name == String_.Empty) return ""; +// Runtime rt = Runtime.getRuntime(); + String kill_exe = "", kill_args = ""; + if (Op_sys.Cur().Tid_is_wnt()) { + kill_exe = "taskkill"; + kill_args = "/F /IM "; + } + else { + kill_exe = "kill"; + kill_args = "-9 "; + } + kill_args += thread_kill_name; + ProcessAdp kill_process = new ProcessAdp().Exe_url_(Io_url_.new_fil_(kill_exe)).Args_str_(kill_args).Thread_kill_name_(""); + boolean pass = kill_process.Run_wait().Exit_code_pass(); + return "killed|" + kill_exe + "|" + kill_args + "|" + pass + "|" + exe_url.Raw() + "|" + args_str; + } + synchronized void Process_bgn() { + exit_code = Exit_init; + rslt_out = ""; + WhenBgn_run(); + pb = new ProcessBuilder(X_to_process_bldr_args_utl(exe_url, args_str)); + pb.redirectErrorStream(true); // NOTE: need to redirectErrorStream or rdr.readLine() will hang; see inkscape and Ostfriesland Verkehr-de.svg + if (working_dir != null) + pb.directory(new File(working_dir.Xto_api())); + else if (!exe_url.OwnerDir().EqNull()) // only set workingDir if ownerDir is not null; NOTE: workingDir necessary for AdvMame; probably not a bad thing to do + pb.directory(new File(exe_url.OwnerDir().Xto_api())); + } ProcessBuilder pb; + Process Process_start() { + try {process = pb.start();} + catch (IOException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread start failed");} + return process; + } + void Process_run_and_end() { + String_bldr sb = String_bldr_.new_(); + BufferedReader rdr = new BufferedReader(new InputStreamReader(process.getInputStream())); + try { + String line = ""; + while ((line = rdr.readLine()) != null) + sb.Add_str_w_crlf(line); + process.waitFor(); + } + catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at wait_for").Add("exe_url", exe_url.Xto_api()).Add("exeArgs", args_str);} + catch (IOException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "io error").Add("exe_url", exe_url.Xto_api()).Add("exeArgs", args_str);} + exit_code = process.exitValue(); + WhenEnd_run(); + process.destroy(); + rslt_out = sb.XtoStrAndClear(); + } + public void Process_term() { + try { + process.getInputStream().close(); + process.getErrorStream().close(); + } catch (IOException e) {} + process.destroy(); + } + public static void run_wait_(Io_url url) { + ProcessAdp process = new ProcessAdp().Exe_url_(url); + process.Run_start(); + process.Process_run_and_end(); + return; + } + public static final ListAdp Test_runs = ListAdp_.new_(); + private ProcessAdp Test_runs_add() {Test_runs.Add(exe_url.Raw() + " " + args_str); exit_code = Exit_pass; return this;} + public static int run_wait_arg_(Io_url url, String arg) { + ProcessAdp process = new ProcessAdp(); + process.Exe_url_(url).Args_str_(arg).Run_wait(); + return process.Exit_code(); + } + private static final String GRP_KEY = "gplx.process"; + public static final int Exit_pass = 0, Exit_init = -1; + public static String[] X_to_process_bldr_args_utl(Io_url exe_url, String args_str) { + ListAdp list = ListAdp_.new_(); + list.Add(exe_url.Xto_api()); + String_bldr sb = String_bldr_.new_(); + int len = String_.Len(args_str); + boolean in_quotes = false; + for (int i = 0; i < len; i++) { + char c = String_.CharAt(args_str, i); + if (c == ' ' && !in_quotes) { // space encountered; assume arg done + list.Add(sb.XtoStr()); + sb.Clear(); + } + else if (c == '"') // NOTE: ProcessBuilder seems to have issues with quotes; do not call sb.Add() + in_quotes = !in_quotes; + else + sb.Add(c); + } + if (sb.Has_some()) list.Add(sb.XtoStr()); + return list.XtoStrAry(); + } +} +class Thread_ProcessAdp_async extends Thread { + public Thread_ProcessAdp_async(ProcessAdp process_adp) {this.process_adp = process_adp;} ProcessAdp process_adp; + public boolean Done() {return done;} boolean done = false; + public void Cancel() {process_adp.UnderProcess().destroy();} + public void run() { + process_adp.Run_wait(); + } +} +class Thread_ProcessAdp_sync extends Thread { + public Thread_ProcessAdp_sync(ProcessAdp process_adp) {this.process_adp = process_adp;} ProcessAdp process_adp; + public boolean Done() {return done;} boolean done = false; + public void Cancel() { + process_adp.UnderProcess().destroy(); + } + public synchronized void run() { + done = false; + Process process = process_adp.Process_start(); + StreamGobbler input_gobbler = new StreamGobbler("input", process.getInputStream()); + StreamGobbler error_gobbler = new StreamGobbler("error", process.getErrorStream()); + input_gobbler.start(); + error_gobbler.start(); + try {process.waitFor();} + catch (InterruptedException e) { + this.Cancel(); + String kill_rslt = process_adp.Kill(); + process_adp.Process_post(kill_rslt); + done = false; + return; + } + while (input_gobbler.isAlive()) { + try {input_gobbler.join(50);} + catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at input gobbler");} + } + while (error_gobbler.isAlive()) { + try {error_gobbler.join(50);} + catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.ProcessAdp", "thread interrupted at error gobbler");} + } + String result = input_gobbler.Rslt() + "\n" + error_gobbler.Rslt(); + process_adp.Process_post(result); + done = true; + } +} +class StreamGobbler extends Thread { + String name; InputStream stream; + public StreamGobbler (String name, InputStream stream) {this.name = name; this.stream = stream;} + public String Rslt() {return rslt;} String rslt; + public void run () { + try { + String_bldr sb = String_bldr_.new_(); + InputStreamReader isr = new InputStreamReader(stream); + BufferedReader br = new BufferedReader(isr); + while (true) { + String s = br.readLine (); + if (s == null) break; + sb.Add(s); + } + stream.close(); + rslt = sb.XtoStrAndClear(); + } + catch (Exception ex) {throw Err_.new_fmt_("failed reading stream; name={0} ex={1}", name, Err_.Message_lang(ex));} + } +} diff --git a/100_core/src_210_env/gplx/ProcessAdp_tst.java b/100_core/src_210_env/gplx/ProcessAdp_tst.java new file mode 100644 index 000000000..b3d28f380 --- /dev/null +++ b/100_core/src_210_env/gplx/ProcessAdp_tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class ProcessAdp_tst { + private ProcessAdp_fxt fxt = new ProcessAdp_fxt(); + @Test public void Escape_ampersands_if_process_is_cmd() { + fxt.Test_Escape_ampersands_if_process_is_cmd(Bool_.Y, "cmd" , "/c \"http://a.org?b=c&d=e\"", "/c \"http://a.org?b=c^&d=e\""); + fxt.Test_Escape_ampersands_if_process_is_cmd(Bool_.Y, "cmd1", "/c \"http://a.org?b=c&d=e\"", "/c \"http://a.org?b=c&d=e\""); + fxt.Test_Escape_ampersands_if_process_is_cmd(Bool_.N, "cmd" , "/c \"http://a.org?b=c&d=e\"", "/c \"http://a.org?b=c&d=e\""); + } +} +class ProcessAdp_fxt { + public void Test_Escape_ampersands_if_process_is_cmd(boolean os_is_wnt, String exe_url, String exe_args, String expd) { + Tfds.Eq(expd, ProcessAdp.Escape_ampersands_if_process_is_cmd(os_is_wnt, exe_url, exe_args)); + } +} diff --git a/100_core/src_210_env/gplx/threads/ThreadAdp.java b/100_core/src_210_env/gplx/threads/ThreadAdp.java new file mode 100644 index 000000000..1c8c13f27 --- /dev/null +++ b/100_core/src_210_env/gplx/threads/ThreadAdp.java @@ -0,0 +1,49 @@ +/* +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.threads; import gplx.*; +import java.lang.*; +public class ThreadAdp implements Runnable { + private String name; private GfoInvkAble invk; private String cmd; private GfoMsg msg; + @gplx.Internal protected ThreadAdp(String name, GfoInvkAble invk, String cmd, GfoMsg msg) { + this.name = name; this.invk = invk; this.cmd = cmd; this.msg = msg; + this.ctor_ThreadAdp(); + } + public ThreadAdp Start() {thread.start(); return this;} + public void Interrupt() {thread.interrupt();} + public void Join() { + try { + thread.join(); + } + catch (Exception e) { + Err_.Noop(e); + } + } +// public void Stop() {thread.stop();} + public boolean IsAlive() {return thread.isAlive();} + void ctor_ThreadAdp() { + if (name == null) + thread = new Thread(this); + else + thread = new Thread(this, name); + } + @Override public void run() { + invk.Invk(GfsCtx._, 0, cmd, msg); + } + public Thread Under_thread() {return thread;} private Thread thread; + public static final ThreadAdp Null = new ThreadAdp(ThreadAdp_.Name_null, GfoInvkAble_.Null, "", GfoMsg_.Null); +} diff --git a/100_core/src_210_env/gplx/threads/ThreadAdp_.java b/100_core/src_210_env/gplx/threads/ThreadAdp_.java new file mode 100644 index 000000000..6d4719e6d --- /dev/null +++ b/100_core/src_210_env/gplx/threads/ThreadAdp_.java @@ -0,0 +1,31 @@ +/* +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.threads; import gplx.*; +public class ThreadAdp_ { + public static void Sleep(int milliseconds) { + try {Thread.sleep(milliseconds);} catch (InterruptedException e) {throw Err_.err_key_(e, "gplx.Thread", "thread interrupted").Add("milliseconds", milliseconds);} + } + public static ThreadAdp invk_(GfoInvkAble invk, String cmd) {return invk_(Name_null, invk, cmd);} + public static ThreadAdp invk_(String name, GfoInvkAble invk, String cmd) {return new ThreadAdp(name, invk, cmd, GfoMsg_.Null);} + public static ThreadAdp invk_msg_(GfoInvkAble invk, GfoMsg msg) {return invk_msg_(Name_null, invk, msg);} + public static ThreadAdp invk_msg_(String name, GfoInvkAble invk, GfoMsg msg) {return new ThreadAdp(name, invk, msg.Key(), msg);} + public static void Run_invk_msg(String name, GfoInvkAble invk, GfoMsg m) { + ThreadAdp_.invk_msg_(name, invk, m).Start(); + } + public static final String Name_null = null; +} diff --git a/100_core/src_220_console/gplx/ConsoleAdp.java b/100_core/src_220_console/gplx/ConsoleAdp.java new file mode 100644 index 000000000..4b8b0a912 --- /dev/null +++ b/100_core/src_220_console/gplx/ConsoleAdp.java @@ -0,0 +1,68 @@ +/* +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; +public class ConsoleAdp implements GfoInvkAble, ConsoleDlg { + public boolean Enabled() {return true;} + public boolean Canceled() {return canceled;} public void Canceled_set(boolean v) {canceled = v;} private boolean canceled = false; + public boolean CanceledChk() {if (canceled) throw Err_.op_canceled_usr_(); return canceled;} + public int CharsPerLineMax() {return chars_per_line_max;} public void CharsPerLineMax_set(int v) {chars_per_line_max = v;} int chars_per_line_max = 80; + public boolean Backspace_by_bytes() {return backspace_by_bytes;} public ConsoleAdp Backspace_by_bytes_(boolean v) {backspace_by_bytes = v; return this;} private boolean backspace_by_bytes; + public void WriteText(String s) {ClearTempText(); WriteText_lang(s);} + public void WriteLine(String s) {ClearTempText(); WriteLine_lang(s);} + public void WriteLineOnly() {ClearTempText(); WriteLine("");} + public void WriteLineFormat(String format, Object... args) {ClearTempText(); WriteLine_lang(String_.Format(format, args));} + public char ReadKey(String m) {WriteText(m); return ReadKey_lang();} + public String ReadLine(String m) {WriteText(m); return ReadLine_lang();} + public void WriteTempText(String s) { + ClearTempText(); + if (String_.Has(s, "\r")) s = String_.Replace(s, "\r", " "); + if (String_.Has(s, "\n")) s = String_.Replace(s, "\n", " "); + if (String_.Len(s) >= chars_per_line_max) s = String_.Mid(s, 0, chars_per_line_max - String_.Len("...") - 1) + "..."; // NOTE: >= and -1 needed b/c line needs to be 1 less than max; ex: default cmd is 80 width, but writing 80 chars will automatically create lineBreak + tempText = s; + WriteText_lang(s); + } String tempText; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} + void ClearTempText() { + if (tempText == null) return; + if (Env_.Mode_debug()) {WriteText_lang(String_.CrLf); return;} + int count = backspace_by_bytes ? Bry_.new_utf8_(tempText).length : String_.Len(tempText); + String moveBack = String_.Repeat("\b", count); + this.WriteText_lang(moveBack); // move cursor back to beginning of line + this.WriteText_lang(String_.Repeat(" ", count)); // overwrite tempText with space + this.WriteText_lang(moveBack); // move cursor back to beginning of line (so next Write will start at beginning) + tempText = null; + } + void WriteText_lang(String s) {System.out.print(s);} + void WriteLine_lang(String s) {System.out.println(s);} + String ReadLine_lang() {return System.console() == null ? "" : System.console().readLine();} + char ReadKey_lang() { + String text = ReadLine_lang(); + return String_.Len(text) == 0 ? '\0' : String_.CharAt(text, 0); + } + public void WriteLine_utf8(String s) { + java.io.PrintStream ps; + try {ps = new java.io.PrintStream(System.out, true, "UTF-8");} + catch (java.io.UnsupportedEncodingException e) {throw Err_.new_("unsupported exception");} + ps.println(s); + } + public static final ConsoleAdp _ = new ConsoleAdp(); + public ConsoleAdp() { + if (Op_sys.Cur().Tid_is_lnx()) + backspace_by_bytes = true; // bash shows UTF8 by default; backspace in bytes, else multi-byte characters don't show; DATE:2014-03-04 + } +} diff --git a/100_core/src_220_console/gplx/ConsoleDlg.java b/100_core/src_220_console/gplx/ConsoleDlg.java new file mode 100644 index 000000000..74a79d3ea --- /dev/null +++ b/100_core/src_220_console/gplx/ConsoleDlg.java @@ -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 . +*/ +package gplx; +public interface ConsoleDlg { + boolean Enabled(); // optimization; allows Write to be skipped (since Write may Concat strings or generate arrays) + boolean CanceledChk(); + int CharsPerLineMax(); void CharsPerLineMax_set(int v); + void WriteText(String s); + void WriteLineFormat(String s, Object... args); + void WriteTempText(String s); + char ReadKey(String msg); + String ReadLine(String msg); +} diff --git a/100_core/src_220_console/gplx/ConsoleDlg_.java b/100_core/src_220_console/gplx/ConsoleDlg_.java new file mode 100644 index 000000000..39db2eaad --- /dev/null +++ b/100_core/src_220_console/gplx/ConsoleDlg_.java @@ -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 . +*/ +package gplx; +public class ConsoleDlg_ { + public static final ConsoleDlg Null = new ConsoleDlg_null(); + public static ConsoleDlg_dev Dev() {return new ConsoleDlg_dev();} +} +class ConsoleDlg_null implements ConsoleDlg { + public boolean Enabled() {return false;} + public boolean CanceledChk() {return false;} + public int CharsPerLineMax() {return 80;} public void CharsPerLineMax_set(int v) {} + public void WriteText(String s) {} + public void WriteLineFormat(String s, Object... args) {} + public void WriteTempText(String s) {} + public char ReadKey(String msg) {return '\0';} + public String ReadLine(String msg) {return "";} +} diff --git a/100_core/src_220_console/gplx/ConsoleDlg_dev.java b/100_core/src_220_console/gplx/ConsoleDlg_dev.java new file mode 100644 index 000000000..5c5405321 --- /dev/null +++ b/100_core/src_220_console/gplx/ConsoleDlg_dev.java @@ -0,0 +1,49 @@ +/* +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; +public class ConsoleDlg_dev implements ConsoleDlg { + public boolean Enabled() {return true;} + public boolean CanceledChk() {return false;} + public int CharsPerLineMax() {return 80;} public void CharsPerLineMax_set(int v) {} + public ConsoleDlg_dev Ignore_add(String s) {ignored.AddKeyVal(s); return this;} + public void WriteText(String s) {WriteString(s);} + public void WriteLineFormat(String s, Object... args) {WriteString(String_.Format(s, args) + String_.CrLf);} + public void WriteTempText(String s) {WriteString(s);} + public String ReadLine(String msg) {return "";} + public char ReadKey(String msg) {return '\0';} + public ConsoleDlg_dev CancelWhenTextWritten(String val) { + cancelVal = val; + return this; + } + void WriteString(String s) { + if (ignored.Has(s)) return; + written.Add(s); + if (cancelVal != null && String_.Has(s, cancelVal)) throw Err_.new_("canceled " + s + " " + cancelVal); + } + String cancelVal; + + public ListAdp Written() {return written;} + public void tst_WrittenStr(String... expd) { + String[] actl = new String[written.Count()]; + int actlLength = Array_.Len(actl); + for (int i = 0; i < actlLength; i++) + actl[i] = written.FetchAt(i).toString(); + Tfds.Eq_ary(actl, expd); + } + ListAdp written = ListAdp_.new_(), erased = ListAdp_.new_(); HashAdp ignored = HashAdp_.new_(); +} diff --git a/100_core/src_300_classXtn/gplx/BoolClassXtn.java b/100_core/src_300_classXtn/gplx/BoolClassXtn.java new file mode 100644 index 000000000..f1136fc78 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/BoolClassXtn.java @@ -0,0 +1,41 @@ +/* +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; +public class BoolClassXtn extends ClassXtn_base implements ClassXtn { + public static final String Key_const = "bo" + "ol"; + public String Key() {return Key_const;} + @Override public Class UnderClass() {return boolean.class;} + public Object DefaultValue() {return false;} + public boolean Eq(Object lhs, Object rhs) {try {return Bool_.cast_(lhs) == Bool_.cast_(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) { + if ( String_.Eq(raw, "true") + || String_.Eq(raw, "True") // needed for Store_Wtr() {boolVal.toString();} + || String_.Eq(raw, "1") // needed for db; gplx field for boolean is int; need simple way to convert from dbInt to langBool + ) + return true; + else if + ( String_.Eq(raw, "false") + || String_.Eq(raw, "False") + || String_.Eq(raw, "0") + ) + return false; + throw Err_.parse_type_(boolean.class, raw); + } + @Override public Object XtoDb(Object obj) {return obj;} + public static final BoolClassXtn _ = new BoolClassXtn(); BoolClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_300_classXtn/gplx/ByteClassXtn.java b/100_core/src_300_classXtn/gplx/ByteClassXtn.java new file mode 100644 index 000000000..e3d48b4fe --- /dev/null +++ b/100_core/src_300_classXtn/gplx/ByteClassXtn.java @@ -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 . +*/ +package gplx; +public class ByteClassXtn extends ClassXtn_base implements ClassXtn { + public static final String Key_const = "byte"; + public String Key() {return Key_const;} + @Override public Class UnderClass() {return byte.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Byte_.cast_(lhs) == Byte_.cast_(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return raw == null ? (Object)null : Byte_.parse_(raw);} + @Override public Object XtoDb(Object obj) {return Byte_.cast_(obj);} + public static final ByteClassXtn _ = new ByteClassXtn(); ByteClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_300_classXtn/gplx/ClassXtn.java b/100_core/src_300_classXtn/gplx/ClassXtn.java new file mode 100644 index 000000000..3732eb6e6 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/ClassXtn.java @@ -0,0 +1,29 @@ +/* +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; +public interface ClassXtn { + String Key(); + Class UnderClass(); + Object DefaultValue(); + Object ParseOrNull(String raw); + Object XtoDb(Object obj); + String XtoUi(Object obj, String fmt); + boolean MatchesClass(Object obj); + boolean Eq(Object lhs, Object rhs); + int compareTo(Object lhs, Object rhs); +} diff --git a/100_core/src_300_classXtn/gplx/ClassXtnPool.java b/100_core/src_300_classXtn/gplx/ClassXtnPool.java new file mode 100644 index 000000000..bd3584d4b --- /dev/null +++ b/100_core/src_300_classXtn/gplx/ClassXtnPool.java @@ -0,0 +1,39 @@ +/* +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; +import gplx.lists.*; +public class ClassXtnPool extends HashAdp_base { + public void Add(ClassXtn typx) {Add_base(typx.Key(), typx);} + public ClassXtn FetchOrFail(String key) {return (ClassXtn)FetchOrFail_base(key);} + + public static final ClassXtnPool _ = new ClassXtnPool(); + public static final String Format_null = ""; + public static ClassXtnPool new_() {return new ClassXtnPool();} + ClassXtnPool() { + Add(ObjectClassXtn._); + Add(StringClassXtn._); + Add(IntClassXtn._); + Add(BoolClassXtn._); + Add(ByteClassXtn._); + Add(DateAdpClassXtn._); + Add(TimeSpanAdpClassXtn._); + Add(IoUrlClassXtn._); + Add(DecimalAdpClassXtn._); + Add(FloatClassXtn._); + } +} diff --git a/100_core/src_300_classXtn/gplx/ClassXtn_base.java b/100_core/src_300_classXtn/gplx/ClassXtn_base.java new file mode 100644 index 000000000..2835d4355 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/ClassXtn_base.java @@ -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 . +*/ +package gplx; +public abstract class ClassXtn_base { + public abstract Class UnderClass(); + public abstract Object ParseOrNull(String raw); + @gplx.Virtual public Object XtoDb(Object obj) {return obj;} + @gplx.Virtual public String XtoUi(Object obj, String fmt) {return Object_.XtoStr_OrNullStr(obj);} + @gplx.Virtual public boolean MatchesClass(Object obj) {if (obj == null) throw Err_.null_("obj"); + return ClassAdp_.Eq_typeSafe(obj, UnderClass()); + } + @gplx.Virtual public int compareTo(Object lhs, Object rhs) {return CompareAble_.Compare_obj(lhs, rhs);} +} diff --git a/100_core/src_300_classXtn/gplx/DateAdpClassXtn.java b/100_core/src_300_classXtn/gplx/DateAdpClassXtn.java new file mode 100644 index 000000000..dfadd0e85 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/DateAdpClassXtn.java @@ -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 . +*/ +package gplx; +public class DateAdpClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "datetime"; + public boolean Eq(Object lhs, Object rhs) {try {return DateAdp_.cast_(lhs).Eq(DateAdp_.cast_(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Class UnderClass() {return DateAdp.class;} + public Object DefaultValue() {return DateAdp_.MinValue;} + @Override public Object ParseOrNull(String raw) {return DateAdp_.parse_gplx(raw);} + @Override public Object XtoDb(Object obj) {return DateAdp_.cast_(obj).XtoStr_gplx_long();} + @Override public String XtoUi(Object obj, String fmt) {return DateAdp_.cast_(obj).XtoStr_fmt(fmt);} + public static final DateAdpClassXtn _ = new DateAdpClassXtn(); DateAdpClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src_300_classXtn/gplx/DateAdpClassXtn_tst.java b/100_core/src_300_classXtn/gplx/DateAdpClassXtn_tst.java new file mode 100644 index 000000000..53204409e --- /dev/null +++ b/100_core/src_300_classXtn/gplx/DateAdpClassXtn_tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class DateAdpClassXtn_tst { + @Test public void XtoDb() { + tst_XtoDb("20091115 220102.999", "2009-11-15 22:01:02.999"); + } + void tst_XtoDb(String val, String expdRaw) { + String actlRaw = (String)DateAdpClassXtn._.XtoDb(DateAdp_.parse_gplx(val)); + Tfds.Eq(expdRaw, actlRaw); + } +} diff --git a/100_core/src_300_classXtn/gplx/DecimalAdpClassXtn.java b/100_core/src_300_classXtn/gplx/DecimalAdpClassXtn.java new file mode 100644 index 000000000..909846dc4 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/DecimalAdpClassXtn.java @@ -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 . +*/ +package gplx; +public class DecimalAdpClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "decimal"; // current dsv files reference "decimal" + @Override public Class UnderClass() {return DecimalAdp.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return DecimalAdp_.cast_(lhs).Eq(DecimalAdp_.cast_(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return DecimalAdp_.parse_(raw);} + @Override public String XtoUi(Object obj, String fmt) {return DecimalAdp_.cast_(obj).XtoStr();} + public static final DecimalAdpClassXtn _ = new DecimalAdpClassXtn(); DecimalAdpClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src_300_classXtn/gplx/DoubleClassXtn.java b/100_core/src_300_classXtn/gplx/DoubleClassXtn.java new file mode 100644 index 000000000..0bcc42366 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/DoubleClassXtn.java @@ -0,0 +1,26 @@ +/* +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; +public class DoubleClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "double"; + @Override public Class UnderClass() {return double.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Double_.cast_(lhs) == Double_.cast_(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return Double_.parse_(raw);} + public static final DoubleClassXtn _ = new DoubleClassXtn(); DoubleClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_300_classXtn/gplx/FloatClassXtn.java b/100_core/src_300_classXtn/gplx/FloatClassXtn.java new file mode 100644 index 000000000..094b193e6 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/FloatClassXtn.java @@ -0,0 +1,26 @@ +/* +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; +public class FloatClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "float"; + @Override public Class UnderClass() {return float.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Float_.cast_(lhs) == Float_.cast_(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return Float_.parse_(raw);} + public static final FloatClassXtn _ = new FloatClassXtn(); FloatClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_300_classXtn/gplx/IntClassXtn.java b/100_core/src_300_classXtn/gplx/IntClassXtn.java new file mode 100644 index 000000000..565241c55 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/IntClassXtn.java @@ -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 . +*/ +package gplx; +public class IntClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "int"; + @Override public Class UnderClass() {return Integer.class;} + public Object DefaultValue() {return 0;} + @Override public Object ParseOrNull(String raw) {return raw == null ? (Object)null : Int_.parse_(raw);} + public boolean Eq(Object lhs, Object rhs) {try {return Int_.cast_(lhs) == Int_.cast_(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object XtoDb(Object obj) {return Int_.cast_(obj);} // necessary for enums + + public static final IntClassXtn _ = new IntClassXtn(); IntClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src_300_classXtn/gplx/IoUrlClassXtn.java b/100_core/src_300_classXtn/gplx/IoUrlClassXtn.java new file mode 100644 index 000000000..280d41786 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/IoUrlClassXtn.java @@ -0,0 +1,29 @@ +/* +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; +public class IoUrlClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "ioPath"; + @Override public Class UnderClass() {return Io_url.class;} + public Object DefaultValue() {return Io_url_.Null;} + @Override public Object ParseOrNull(String raw) {return Io_url_.new_any_(raw);} + @Override public Object XtoDb(Object obj) {return Io_url_.cast_(obj).Raw();} + @Override public String XtoUi(Object obj, String fmt) {return Io_url_.cast_(obj).Raw();} + @Override public boolean MatchesClass(Object obj) {return Io_url_.as_(obj) != null;} + public boolean Eq(Object lhs, Object rhs) {try {return Io_url_.cast_(lhs).Eq(Io_url_.cast_(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + public static final IoUrlClassXtn _ = new IoUrlClassXtn(); IoUrlClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src_300_classXtn/gplx/LongClassXtn.java b/100_core/src_300_classXtn/gplx/LongClassXtn.java new file mode 100644 index 000000000..5981cd311 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/LongClassXtn.java @@ -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 . +*/ +package gplx; +public class LongClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "long"; + @Override public Class UnderClass() {return long.class;} + public Object DefaultValue() {return 0;} + public boolean Eq(Object lhs, Object rhs) {try {return Long_.cast_(lhs) == Long_.cast_(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + @Override public Object ParseOrNull(String raw) {return raw == null ? (Object)null : Long_.parse_(raw);} + @Override public Object XtoDb(Object obj) {return Long_.cast_(obj);} // necessary for enums + public static final LongClassXtn _ = new LongClassXtn(); LongClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_300_classXtn/gplx/ObjectClassXtn.java b/100_core/src_300_classXtn/gplx/ObjectClassXtn.java new file mode 100644 index 000000000..878c16adc --- /dev/null +++ b/100_core/src_300_classXtn/gplx/ObjectClassXtn.java @@ -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 . +*/ +package gplx; +public class ObjectClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "Object"; + @Override public Class UnderClass() {return Object.class;} + public Object DefaultValue() {return null;} + @Override public Object ParseOrNull(String raw) {throw Err_.not_implemented_();} + @Override public Object XtoDb(Object obj) {throw Err_.not_implemented_();} + public boolean Eq(Object lhs, Object rhs) {return lhs == rhs;} + public static final ObjectClassXtn _ = new ObjectClassXtn(); ObjectClassXtn() {} // added to ClassXtnPool by default +} diff --git a/100_core/src_300_classXtn/gplx/StringClassXtn.java b/100_core/src_300_classXtn/gplx/StringClassXtn.java new file mode 100644 index 000000000..03e9aca95 --- /dev/null +++ b/100_core/src_300_classXtn/gplx/StringClassXtn.java @@ -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 . +*/ +package gplx; +public class StringClassXtn extends ClassXtn_base implements ClassXtn { + public static final String Key_const = "string"; + public String Key() {return Key_const;} + @Override public Class UnderClass() {return String.class;} + public Object DefaultValue() {return "";} + @Override public Object ParseOrNull(String raw) {return raw;} + @Override public String XtoUi(Object obj, String fmt) {return String_.as_(obj);} + public boolean Eq(Object lhs, Object rhs) {try {return String_.Eq(String_.cast_(lhs), String_.cast_(rhs));} catch (Exception e) {Err_.Noop(e); return false;}} + public static final StringClassXtn _ = new StringClassXtn(); StringClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_300_classXtn/gplx/TimeSpanAdpClassXtn.java b/100_core/src_300_classXtn/gplx/TimeSpanAdpClassXtn.java new file mode 100644 index 000000000..014aa7a6d --- /dev/null +++ b/100_core/src_300_classXtn/gplx/TimeSpanAdpClassXtn.java @@ -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 . +*/ +package gplx; +public class TimeSpanAdpClassXtn extends ClassXtn_base implements ClassXtn { + public String Key() {return Key_const;} public static final String Key_const = "timeSpan"; + @Override public Class UnderClass() {return TimeSpanAdp.class;} + public Object DefaultValue() {return TimeSpanAdp_.Zero;} + @Override public Object ParseOrNull(String raw) {return TimeSpanAdp_.parse_(raw);} + @Override public Object XtoDb(Object obj) {return TimeSpanAdp_.cast_(obj).TotalSecs();} + @Override public String XtoUi(Object obj, String fmt) {return TimeSpanAdp_.cast_(obj).XtoStr(fmt);} + public boolean Eq(Object lhs, Object rhs) {try {return TimeSpanAdp_.cast_(lhs).Eq(rhs);} catch (Exception e) {Err_.Noop(e); return false;}} + public static final TimeSpanAdpClassXtn _ = new TimeSpanAdpClassXtn(); TimeSpanAdpClassXtn() {} // added to ClassXtnPool by default +} \ No newline at end of file diff --git a/100_core/src_310_gfoNde/gplx/GfoFld.java b/100_core/src_310_gfoNde/gplx/GfoFld.java new file mode 100644 index 000000000..74c808609 --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoFld.java @@ -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 . +*/ +package gplx; +public class GfoFld { + public String Key() {return key;} private String key; + public ClassXtn Type() {return type;} ClassXtn type; + public static final GfoFld Null = new_(String_.Null_mark, ObjectClassXtn._); + public static GfoFld new_(String key, ClassXtn c) { + GfoFld rv = new GfoFld(); + rv.key = key; rv.type = c; + return rv; + } +} diff --git a/100_core/src_310_gfoNde/gplx/GfoFldList.java b/100_core/src_310_gfoNde/gplx/GfoFldList.java new file mode 100644 index 000000000..f38f6b6d5 --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoFldList.java @@ -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 . +*/ +package gplx; +public interface GfoFldList { + int Count(); + boolean Has(String key); + int IndexOf(String key); + GfoFld FetchAt(int i); + GfoFld FetchOrNull(String key); + GfoFldList Add(String key, ClassXtn c); + String XtoStr(); +} diff --git a/100_core/src_310_gfoNde/gplx/GfoFldList_.java b/100_core/src_310_gfoNde/gplx/GfoFldList_.java new file mode 100644 index 000000000..c66de4efc --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoFldList_.java @@ -0,0 +1,62 @@ +/* +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; +public class GfoFldList_ { + public static final GfoFldList Null = new GfoFldList_null(); + public static GfoFldList new_() {return new GfoFldList_base();} + public static GfoFldList str_(String... names) { + GfoFldList rv = new GfoFldList_base(); + for (String name : names) + rv.Add(name, StringClassXtn._); + return rv; + } +} +class GfoFldList_base implements GfoFldList { + public int Count() {return hash.Count();} + public boolean Has(String key) {return hash.Has(key);} + public int IndexOf(String key) { + Object rv = idxs.Fetch(key); + return rv == null ? ListAdp_.NotFound : Int_.cast_(rv); + } + public GfoFld FetchAt(int i) {return (GfoFld)hash.FetchAt(i);} + public GfoFld FetchOrNull(String key) {return (GfoFld)hash.Fetch(key);} + public GfoFldList Add(String key, ClassXtn c) { + GfoFld fld = GfoFld.new_(key, c); + hash.Add(key, fld); + idxs.Add(key, idxs.Count()); + return this; + } + public String XtoStr() { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < hash.Count(); i++) { + GfoFld fld = this.FetchAt(i); + sb.Add(fld.Key()).Add("|"); + } + return sb.XtoStr(); + } + OrderedHash hash = OrderedHash_.new_(); HashAdp idxs = HashAdp_.new_(); // PERF: idxs used for IndexOf; need to recalc if Del ever added +} +class GfoFldList_null implements GfoFldList { + public int Count() {return 0;} + public boolean Has(String key) {return false;} + public int IndexOf(String key) {return ListAdp_.NotFound;} + public GfoFld FetchAt(int i) {return GfoFld.Null;} + public GfoFld FetchOrNull(String key) {return null;} + public GfoFldList Add(String key, ClassXtn typx) {return this;} + public String XtoStr() {return "<>";} +} \ No newline at end of file diff --git a/100_core/src_310_gfoNde/gplx/GfoNde.java b/100_core/src_310_gfoNde/gplx/GfoNde.java new file mode 100644 index 000000000..672da927f --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoNde.java @@ -0,0 +1,71 @@ +/* +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; +public class GfoNde implements GfoInvkAble { + public GfoFldList Flds() {return flds;} GfoFldList flds; + public HashAdp EnvVars() {return envVars;} HashAdp envVars = HashAdp_.new_(); + public String Name() {return name;} public GfoNde Name_(String v) {name = v; return this;} private String name; + public Object ReadAt(int i) {ChkIdx(i); return ary[i];} + public void WriteAt(int i, Object val) {ChkIdx(i); ary[i] = val;} + public Object Read(String key) {int i = IndexOfOrFail(key); return ary[i];} + public void Write(String key, Object val) {int i = IndexOfOrFail(key); ary[i] = val;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return Read(k);} + + public GfoNdeList Subs() {return subs;} GfoNdeList subs = GfoNdeList_.new_(); + public GfoFldList SubFlds() {return subFlds;} GfoFldList subFlds = GfoFldList_.new_(); + public void XtoStr_wtr(DataWtr wtr) {XtoStr_wtr(this, wtr);}// TEST + public String XtoStr() { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < aryLen; i++) { + String key = i >= flds.Count() ? "<< NULL " + i + " >>" : flds.FetchAt(i).Key(); + String val = i >= aryLen ? "<< NULL " + i + " >>" : Object_.XtoStr_OrNullStr(ary[i]); + sb.Add(key).Add("=").Add(val); + } + return sb.XtoStr(); + } + int IndexOfOrFail(String key) { + int i = flds.IndexOf(key); + if ((i < 0 || i >= aryLen)) throw Err_.new_("field name not found").Add("name", key).Add("index", i).Add("count", this.Flds().Count()); + return i; + } + boolean ChkIdx(int i) {if (i < 0 || i >= aryLen) throw Err_.missing_idx_(i, aryLen); return true;} + Object[] ary; int type; int aryLen; + @gplx.Internal protected GfoNde(int type, String name, GfoFldList flds, Object[] ary, GfoFldList subFlds, GfoNde[] subAry) { + this.type = type; this.name = name; this.flds = flds; this.ary = ary; aryLen = Array_.Len(ary); this.subFlds = subFlds; + for (GfoNde sub : subAry) + subs.Add(sub); + } + static void XtoStr_wtr(GfoNde nde, DataWtr wtr) { + if (nde.type == GfoNde_.Type_Leaf) { + wtr.WriteLeafBgn("flds"); + for (int i = 0; i < nde.ary.length; i++) + wtr.WriteData(nde.Flds().FetchAt(i).Key(), nde.ReadAt(i)); + wtr.WriteLeafEnd(); + } + else { + if (nde.type == GfoNde_.Type_Node) // never write node info for root + wtr.WriteTableBgn(nde.Name(), nde.SubFlds()); + for (int i = 0; i < nde.Subs().Count(); i++) { + GfoNde sub = nde.Subs().FetchAt_asGfoNde(i); + XtoStr_wtr(sub, wtr); + } + if (nde.type == GfoNde_.Type_Node) + wtr.WriteNodeEnd(); + } + } +} diff --git a/100_core/src_310_gfoNde/gplx/GfoNdeFxt.java b/100_core/src_310_gfoNde/gplx/GfoNdeFxt.java new file mode 100644 index 000000000..d0cdb9300 --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoNdeFxt.java @@ -0,0 +1,35 @@ +/* +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; +public class GfoNdeFxt { + public GfoNde root_(GfoNde... subs) {return GfoNde_.root_(subs);} + public GfoNde tbl_(String name, GfoNde... rows) {return GfoNde_.tbl_(name, GfoFldList_.Null, rows);} + public GfoNde nde_(String name, GfoFldList flds, GfoNde... subs) {return GfoNde_.tbl_(name, flds, subs);} + public GfoNde row_(GfoFldList flds, Object... vals) {return GfoNde_.vals_(flds, vals);} + public GfoNde row_vals_(Object... vals) {return GfoNde_.vals_(GfoFldList_by_count_(vals.length), vals);} + public GfoNde csv_dat_(GfoNde... rows) {return GfoNde_.tbl_("", GfoFldList_.Null, rows);} + public GfoNde csv_hdr_(GfoFldList flds, GfoNde... rows) {return GfoNde_.tbl_("", flds, rows);} + public static GfoNdeFxt new_() {return new GfoNdeFxt();} + + static GfoFldList GfoFldList_by_count_(int count) { + GfoFldList rv = GfoFldList_.new_(); + for (int i = 0; i < count; i++) + rv.Add("fld" + Int_.XtoStr(i), StringClassXtn._); + return rv; + } +} diff --git a/100_core/src_310_gfoNde/gplx/GfoNdeList.java b/100_core/src_310_gfoNde/gplx/GfoNdeList.java new file mode 100644 index 000000000..a23f6760c --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoNdeList.java @@ -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 . +*/ +package gplx; +import gplx.lists.*; /*ComparerAble*/ +public interface GfoNdeList { + int Count(); + GfoNde FetchAt_asGfoNde(int index); + void Add(GfoNde rcd); + void Del(GfoNde rcd); + void Clear(); + void SortBy(ComparerAble comparer); +} diff --git a/100_core/src_310_gfoNde/gplx/GfoNdeList_.java b/100_core/src_310_gfoNde/gplx/GfoNdeList_.java new file mode 100644 index 000000000..6b73aa6b3 --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoNdeList_.java @@ -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 . +*/ +package gplx; +import gplx.lists.*; /*ComparerAble*/ +public class GfoNdeList_ { + public static final GfoNdeList Null = new GfoNdeList_null(); + public static GfoNdeList new_() {return new GfoNdeList_base();} +} +class GfoNdeList_base implements GfoNdeList { + public int Count() {return list.Count();} + public GfoNde FetchAt_asGfoNde(int i) {return (GfoNde)list.FetchAt(i);} + public void Add(GfoNde rcd) {list.Add(rcd);} + public void Del(GfoNde rcd) {list.Del(rcd);} + public void Clear() {list.Clear();} + public void SortBy(ComparerAble comparer) {list.SortBy(comparer);} + ListAdp list = ListAdp_.new_(); +} +class GfoNdeList_null implements GfoNdeList { + public int Count() {return 0;} + public GfoNde FetchAt_asGfoNde(int index) {return null;} + public void Add(GfoNde rcd) {} + public void Del(GfoNde rcd) {} + public void Clear() {} + public void SortBy(ComparerAble comparer) {} +} diff --git a/100_core/src_310_gfoNde/gplx/GfoNde_.java b/100_core/src_310_gfoNde/gplx/GfoNde_.java new file mode 100644 index 000000000..b9b256390 --- /dev/null +++ b/100_core/src_310_gfoNde/gplx/GfoNde_.java @@ -0,0 +1,46 @@ +/* +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; +public class GfoNde_ { + public static final GfoNde[] Ary_empty = new GfoNde[0]; + public static GfoNde[] ary_(GfoNde... ary) {return ary;} + public static GfoNde as_(Object obj) {return obj instanceof GfoNde ? (GfoNde)obj : null;} + public static GfoNde root_(GfoNde... subs) {return new GfoNde(GfoNde_.Type_Root, "RootName", GfoFldList_.Null, Object_.Ary_empty, GfoFldList_.Null, subs);} + public static GfoNde tbl_(String name, GfoFldList flds, GfoNde... rows) {return new GfoNde(GfoNde_.Type_Node, name, flds, Object_.Ary_empty, flds, rows);} + public static GfoNde vals_(GfoFldList flds, Object[] ary) {return new GfoNde(GfoNde_.Type_Leaf, "row", flds, ary, GfoFldList_.Null, Ary_empty);} + public static GfoNde vals_params_(GfoFldList flds, Object... ary) {return new GfoNde(GfoNde_.Type_Leaf, "row", flds, ary, GfoFldList_.Null, Ary_empty);} + public static GfoNde nde_(String name, Object[] ary, GfoNde... subs) {return new GfoNde(GfoNde_.Type_Node, name, GfoFldList_.Null, ary, GfoFldList_.Null, subs);} + public static GfoNde rdr_(DataRdr rdr) { + try { + ListAdp rows = ListAdp_.new_(); + GfoFldList flds = GfoFldList_.new_(); + int fldLen = rdr.FieldCount(); + for (int i = 0; i < fldLen; i++) + flds.Add(rdr.KeyAt(i), ObjectClassXtn._); + while (rdr.MoveNextPeer()) { + Object[] valAry = new Object[fldLen]; + for (int i = 0; i < fldLen; i++) + valAry[i] = rdr.ReadAt(i); + rows.Add(GfoNde_.vals_(flds, valAry)); + } + return GfoNde_.tbl_("", flds, (GfoNde[])rows.XtoAry(GfoNde.class)); + } + finally {rdr.Rls();} + } + @gplx.Internal protected static final int Type_Leaf = 1, Type_Node = 2, Type_Root = 3; +} diff --git a/100_core/src_311_gfoObj/gplx/GfoEvMgr.java b/100_core/src_311_gfoObj/gplx/GfoEvMgr.java new file mode 100644 index 000000000..3a1aa7fc3 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoEvMgr.java @@ -0,0 +1,131 @@ +/* +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; +import gplx.lists.*; +public class GfoEvMgr { + @gplx.Internal protected void AddSub(GfoEvMgrOwner pub, String pubEvt, GfoEvObj sub, String subPrc) { + GfoEvLnk lnk = new GfoEvLnk(pub, pubEvt, sub, subPrc); + if (subsRegy == null) subsRegy = OrderedHash_.new_(); + AddInList(subsRegy, pubEvt, lnk); + sub.EvMgr().AddPub(pubEvt, lnk); + } + @gplx.Internal protected void Lnk(GfoEvMgrOwner pub) { + if (pub.EvMgr().lnks == null) pub.EvMgr().lnks = ListAdp_.new_(); + pub.EvMgr().lnks.Add(this); + } ListAdp lnks; + void AddInList(OrderedHash regy, String key, GfoEvLnk lnk) { + GfoEvLnkList list = (GfoEvLnkList)regy.Fetch(key); + if (list == null) { + list = new GfoEvLnkList(key); + regy.Add(key, list); + } + list.Add(lnk); + } + @gplx.Internal protected void AddPub(String pubEvt, GfoEvLnk lnk) { + if (pubsRegy == null) pubsRegy = OrderedHash_.new_(); + AddInList(pubsRegy, pubEvt, lnk); + } + @gplx.Internal protected void Pub(GfsCtx ctx, String evt, GfoMsg m) { + ctx.MsgSrc_(sender); + GfoEvLnkList subs = subsRegy == null ? null : (GfoEvLnkList)subsRegy.Fetch(evt); + if (subs != null) { + for (int i = 0; i < subs.Count(); i++) { + GfoEvLnk lnk = (GfoEvLnk)subs.FetchAt(i); + lnk.Sub().Invk(ctx, 0, lnk.SubPrc(), m); // NOTE: itm.Key() needed for Subscribe_diff() + } + } + if (lnks != null) { + for (int i = 0; i < lnks.Count(); i++) { + GfoEvMgr lnk = (GfoEvMgr)lnks.FetchAt(i); + lnk.Pub(ctx, evt, m); + } + } + } + @gplx.Internal protected void RlsSub(GfoEvMgrOwner eobj) { + RlsRegyObj(pubsRegy, eobj, true); + RlsRegyObj(subsRegy, eobj, false); + } + @gplx.Internal protected void RlsPub(GfoEvMgrOwner eobj) { + RlsRegyObj(pubsRegy, eobj, true); + RlsRegyObj(subsRegy, eobj, false); + } + @gplx.Internal protected void RlsRegyObj(OrderedHash regy, GfoEvMgrOwner eobj, boolean pub) { + if (regy == null) return; + ListAdp delList = ListAdp_.new_(); + for (int i = 0; i < regy.Count(); i++) { + GfoEvLnkList pubsList = (GfoEvLnkList)regy.FetchAt(i); + delList.Clear(); + for (int j = 0; j < pubsList.Count(); j++) { + GfoEvLnk lnk = (GfoEvLnk)pubsList.FetchAt(j); + if (lnk.End(!pub) == eobj) delList.Add(lnk); + } + for (int j = 0; j < delList.Count(); j++) { + GfoEvLnk del = (GfoEvLnk)delList.FetchAt(j); + del.End(pub).EvMgr().RlsLnk(!pub, pubsList.Key(), del.End(!pub)); + pubsList.Del(del); + } + } + } + @gplx.Internal protected void RlsLnk(boolean pubEnd, String key, GfoEvMgrOwner endObj) { + OrderedHash regy = pubEnd ? pubsRegy : subsRegy; + GfoEvLnkList list = (GfoEvLnkList)regy.Fetch(key); + ListAdp delList = ListAdp_.new_(); + for (int i = 0; i < list.Count(); i++) { + GfoEvLnk lnk = (GfoEvLnk)list.FetchAt(i); + if (lnk.End(pubEnd) == endObj) delList.Add(lnk); + } + for (int i = 0; i < delList.Count(); i++) { + GfoEvLnk lnk = (GfoEvLnk)delList.FetchAt(i); + list.Del(lnk); + } + delList.Clear(); + } + + Object sender; OrderedHash subsRegy, pubsRegy; + public static GfoEvMgr new_(Object sender) { + GfoEvMgr rv = new GfoEvMgr(); + rv.sender = sender; + return rv; + } GfoEvMgr() {} +} +class GfoEvLnkList { + public String Key() {return key;} private String key; + public int Count() {return list.Count();} + public void Add(GfoEvLnk lnk) {list.Add(lnk);} + public void Del(GfoEvLnk lnk) {list.Del(lnk);} + public GfoEvLnk FetchAt(int i) {return (GfoEvLnk)list.FetchAt(i);} + public GfoEvLnkList(String key) {this.key = key;} + ListAdp list = ListAdp_.new_(); +} +class GfoEvLnk { + public GfoEvMgrOwner Pub() {return pub;} GfoEvMgrOwner pub; + public String PubEvt() {return pubEvt;} private String pubEvt; + public GfoEvObj Sub() {return sub;} GfoEvObj sub; + public String SubPrc() {return subPrc;} private String subPrc; + public GfoEvMgrOwner End(boolean pubEnd) {return pubEnd ? pub : sub;} + public GfoEvLnk(GfoEvMgrOwner pub, String pubEvt, GfoEvObj sub, String subPrc) {this.pub = pub; this.pubEvt = pubEvt; this.sub = sub; this.subPrc = subPrc;} +} +class GfoEvItm { + public String Key() {return key;} private String key; + public GfoInvkAble InvkAble() {return invkAble;} GfoInvkAble invkAble; + public static GfoEvItm new_(GfoInvkAble invkAble, String key) { + GfoEvItm rv = new GfoEvItm(); + rv.invkAble = invkAble; rv.key = key; + return rv; + } +} diff --git a/100_core/src_311_gfoObj/gplx/GfoEvMgrOwner.java b/100_core/src_311_gfoObj/gplx/GfoEvMgrOwner.java new file mode 100644 index 000000000..d2cb940a2 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoEvMgrOwner.java @@ -0,0 +1,21 @@ +/* +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; +public interface GfoEvMgrOwner { + GfoEvMgr EvMgr(); +} diff --git a/100_core/src_311_gfoObj/gplx/GfoEvMgr_.java b/100_core/src_311_gfoObj/gplx/GfoEvMgr_.java new file mode 100644 index 000000000..512584222 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoEvMgr_.java @@ -0,0 +1,45 @@ +/* +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; +public class GfoEvMgr_ { + public static void Sub(GfoEvMgrOwner pub, String pubEvt, GfoEvObj sub, String subEvt) {pub.EvMgr().AddSub(pub, pubEvt, sub, subEvt);} + public static void SubSame(GfoEvMgrOwner pub, String evt, GfoEvObj sub) {pub.EvMgr().AddSub(pub, evt, sub, evt);} + public static void SubSame_many(GfoEvMgrOwner pub, GfoEvObj sub, String... evts) { + int len = evts.length; + for (int i = 0; i < len; i++) { + String evt = evts[i]; + pub.EvMgr().AddSub(pub, evt, sub, evt); + } + } + public static void Pub(GfoEvMgrOwner pub, String pubEvt) {pub.EvMgr().Pub(GfsCtx.new_(), pubEvt, GfoMsg_.new_cast_(pubEvt));} + public static void PubObj(GfoEvMgrOwner pub, String pubEvt, String key, Object v) {pub.EvMgr().Pub(GfsCtx.new_(), pubEvt, msg_(pubEvt, KeyVal_.new_(key, v)));} + public static void PubVal(GfoEvMgrOwner pub, String pubEvt, Object v) {pub.EvMgr().Pub(GfsCtx.new_(), pubEvt, msg_(pubEvt, KeyVal_.new_("v", v)));} + public static void PubVals(GfoEvMgrOwner pub, String pubEvt, KeyVal... ary) {pub.EvMgr().Pub(GfsCtx.new_(), pubEvt, msg_(pubEvt, ary));} + public static void PubMsg(GfoEvMgrOwner pub, GfsCtx ctx, String pubEvt, GfoMsg m) {pub.EvMgr().Pub(ctx, pubEvt, m);} + public static void Lnk(GfoEvMgrOwner pub, GfoEvMgrOwner sub) {sub.EvMgr().Lnk(pub);} + public static void RlsPub(GfoEvMgrOwner pub) {pub.EvMgr().RlsPub(pub);} + public static void RlsSub(GfoEvMgrOwner sub) {sub.EvMgr().RlsSub(sub);} + static GfoMsg msg_(String evt, KeyVal... kvAry) { + GfoMsg m = GfoMsg_.new_cast_(evt); + for (int i = 0; i < kvAry.length; i++) { + KeyVal kv = kvAry[i]; + m.Add(kv.Key(), kv.Val()); + } + return m; + } +} diff --git a/100_core/src_311_gfoObj/gplx/GfoEvMgr_tst.java b/100_core/src_311_gfoObj/gplx/GfoEvMgr_tst.java new file mode 100644 index 000000000..0763b0024 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoEvMgr_tst.java @@ -0,0 +1,69 @@ +/* +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; +import org.junit.*; +public class GfoEvMgr_tst { + @Before public void setup() { + pub = make_(); sub = make_(); + } MockEvObj pub, sub; + @Test public void Basic() { + GfoEvMgr_.SubSame(pub, "ev1", sub); + GfoEvMgr_.PubVal(pub, "ev1", "val1"); + sub.tst_Handled("val1"); + } + @Test public void None() {// make sure no subscribers does not cause exception + GfoEvMgr_.SubSame(pub, "ev1", sub); + GfoEvMgr_.PubVal(pub, "ev2", "val1"); //ev2 does not exist + sub.tst_Handled(); + } + @Test public void Lnk() { + MockEvObj mid = make_(); + mid.EvMgr().Lnk(pub); + GfoEvMgr_.SubSame(mid, "ev1", sub); + GfoEvMgr_.PubVal(pub, "ev1", "val1"); + sub.tst_Handled("val1"); + } + @Test public void RlsSub() { + this.Basic(); + + GfoEvMgr_.RlsSub(sub); + GfoEvMgr_.PubVal(pub, "ev1", "val1"); + sub.tst_Handled(); + } + @Test public void RlsPub() { + this.Basic(); + + GfoEvMgr_.RlsSub(pub); + GfoEvMgr_.PubVal(pub, "ev1", "val1"); + sub.tst_Handled(); + } + MockEvObj make_() {return new MockEvObj();} +} +class MockEvObj implements GfoEvObj { + public GfoEvMgr EvMgr() {return eventMgr;} GfoEvMgr eventMgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + handled.Add(m.ReadStr("v")); + return this; + } + ListAdp handled = ListAdp_.new_(); + public void tst_Handled(String... expd) { + Tfds.Eq_ary_str(expd, handled.XtoStrAry()); + handled.Clear(); + } + public MockEvObj(){eventMgr = GfoEvMgr.new_(this);} +} diff --git a/100_core/src_311_gfoObj/gplx/GfoEvObj.java b/100_core/src_311_gfoObj/gplx/GfoEvObj.java new file mode 100644 index 000000000..2efe9ba3d --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoEvObj.java @@ -0,0 +1,19 @@ +/* +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; +public interface GfoEvObj extends GfoInvkAble, GfoEvMgrOwner {} diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkAble.java b/100_core/src_311_gfoObj/gplx/GfoInvkAble.java new file mode 100644 index 000000000..5b734be57 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkAble.java @@ -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 . +*/ +package gplx; +public interface GfoInvkAble { + Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m); +} +/* + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_set)) {} + else return GfoInvkAble_.Rv_unhandled; + return this; + } private static final String Invk_set = "set"; +*/ diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkAbleCmd.java b/100_core/src_311_gfoObj/gplx/GfoInvkAbleCmd.java new file mode 100644 index 000000000..fb85e50a2 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkAbleCmd.java @@ -0,0 +1,37 @@ +/* +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; +public class GfoInvkAbleCmd { + public GfoInvkAble InvkAble() {return invkAble;} GfoInvkAble invkAble; + public String Cmd() {return cmd;} private String cmd; + public Object Arg() {return arg;} Object arg; + public Object Invk() { + if (this == null) return GfoInvkAble_.Rv_unhandled; + return invkAble.Invk(GfsCtx._, 0, cmd, m); + } + GfoMsg m; + + public static final GfoInvkAbleCmd Null = new GfoInvkAbleCmd(); + public static GfoInvkAbleCmd new_(GfoInvkAble invkAble, String cmd) {return arg_(invkAble, cmd, null);} + public static GfoInvkAbleCmd arg_(GfoInvkAble invkAble, String cmd, Object arg) { + GfoInvkAbleCmd rv = new GfoInvkAbleCmd(); + rv.invkAble = invkAble; rv.cmd = cmd; rv.arg = arg; + rv.m = (arg == null) ? GfoMsg_.Null : GfoMsg_.new_parse_(cmd).Add("v", arg); + return rv; + } GfoInvkAbleCmd() {} +} diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkAble_.java b/100_core/src_311_gfoObj/gplx/GfoInvkAble_.java new file mode 100644 index 000000000..03cbf3893 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkAble_.java @@ -0,0 +1,36 @@ +/* +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; +public class GfoInvkAble_ { + public static GfoInvkAble as_(Object obj) {return obj instanceof GfoInvkAble ? (GfoInvkAble)obj : null;} + public static GfoInvkAble cast_(Object obj) {try {return (GfoInvkAble)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, GfoInvkAble.class, obj);}} + public static final String_obj_val Rv_unhandled = String_obj_val.new_("Unhandled"), Rv_handled = String_obj_val.new_("Handled"), Rv_host = String_obj_val.new_("Host") + , Rv_cancel = String_obj_val.new_("Cancel"), Rv_error = String_obj_val.new_("Error"); + + public static Object InvkCmd(GfoInvkAble invk, String k) {return InvkCmd_msg(invk, k, GfoMsg_.Null);} + public static Object InvkCmd_val(GfoInvkAble invk, String k, Object v) {return InvkCmd_msg(invk, k, GfoMsg_.new_cast_(k).Add("v", v));} + public static Object InvkCmd_msg(GfoInvkAble invk, String k, GfoMsg m) { + Object rv = invk.Invk(GfsCtx._, 0, k, m); + if (rv == GfoInvkAble_.Rv_unhandled) throw Err_.new_("invkable did not handle message").Add("key", k); + return rv; + } + public static final GfoInvkAble Null = new GfoInvkAble_null(); +} +class GfoInvkAble_null implements GfoInvkAble { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} +} diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkCmdMgr.java b/100_core/src_311_gfoObj/gplx/GfoInvkCmdMgr.java new file mode 100644 index 000000000..88a27ff79 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkCmdMgr.java @@ -0,0 +1,67 @@ +/* +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; +public class GfoInvkCmdMgr { + public GfoInvkCmdMgr Add_cmd_many(GfoInvkAble invk, String... keys) { + for (String key : keys) + list.Add(GfoInvkCmdItm.new_(key, invk)); + return this; + } + public GfoInvkCmdMgr Add_cmd(String key, GfoInvkAble invk) { + list.Add(GfoInvkCmdItm.new_(key, invk)); + return this; + } + public GfoInvkCmdMgr Add_mgr(String key, GfoInvkAble invk) { + list.Add(GfoInvkCmdItm.new_(key, invk).Type_isMgr_(true)); + return this; + } + public GfoInvkCmdMgr Add_xtn(GfoInvkAble xtn) { + list.Add(GfoInvkCmdItm.new_("xtn", xtn).Type_isXtn_(true)); + return this; + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m, Object host) { + for (int i = 0; i < list.Count(); i++) { + GfoInvkCmdItm itm = (GfoInvkCmdItm)list.FetchAt(i); + if (itm.Type_isXtn()) { + Object invkVal = itm.Invk().Invk(ctx, ikey, k, m); + if (invkVal != GfoInvkAble_.Rv_unhandled) return invkVal; + } + if (!ctx.Match(k, itm.Key())) continue; + if (itm.Type_isMgr()) return itm.Invk(); + Object rv = null; + m.Add("host", host); + rv = itm.Invk().Invk(ctx, ikey, k, m); + return rv == GfoInvkAble_.Rv_host ? host : rv; // if returning "this" return host + } + return Unhandled; + } + public static final String_obj_val Unhandled = String_obj_val.new_("GfoInvkCmdMgr Unhandled"); + ListAdp list = ListAdp_.new_(); + public static GfoInvkCmdMgr new_() {return new GfoInvkCmdMgr();} GfoInvkCmdMgr() {} +} +class GfoInvkCmdItm { + public String Key() {return key;} private String key; + public GfoInvkAble Invk() {return invk;} GfoInvkAble invk; + public boolean Type_isMgr() {return type_isMgr;} public GfoInvkCmdItm Type_isMgr_(boolean v) {type_isMgr = v; return this;} private boolean type_isMgr; + public boolean Type_isXtn() {return type_isXtn;} public GfoInvkCmdItm Type_isXtn_(boolean v) {type_isXtn = v; return this;} private boolean type_isXtn; + public static GfoInvkCmdItm new_(String key, GfoInvkAble invk) { + GfoInvkCmdItm rv = new GfoInvkCmdItm(); + rv.key = key; rv.invk = invk; + return rv; + } GfoInvkCmdItm() {} +} diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkCmdMgrOwner.java b/100_core/src_311_gfoObj/gplx/GfoInvkCmdMgrOwner.java new file mode 100644 index 000000000..37cbb9acc --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkCmdMgrOwner.java @@ -0,0 +1,21 @@ +/* +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; +public interface GfoInvkCmdMgrOwner { + GfoInvkCmdMgr InvkMgr(); +} diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkRootWkr.java b/100_core/src_311_gfoObj/gplx/GfoInvkRootWkr.java new file mode 100644 index 000000000..2c2189ce1 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkRootWkr.java @@ -0,0 +1,21 @@ +/* +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; +public interface GfoInvkRootWkr { + Object Run_str_for(GfoInvkAble invk, GfoMsg msg); +} diff --git a/100_core/src_311_gfoObj/gplx/GfoInvkXtoStr.java b/100_core/src_311_gfoObj/gplx/GfoInvkXtoStr.java new file mode 100644 index 000000000..e1985edf5 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoInvkXtoStr.java @@ -0,0 +1,43 @@ +/* +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; +public class GfoInvkXtoStr { + public static GfoMsg ReadMsg(GfoInvkAble invk, String k) { + GfsCtx ctx = GfsCtx.wtr_(); + GfoMsg m = GfoMsg_.rdr_(k); + invk.Invk(ctx, 0, k, m); + String invkKey = GfsCore._.FetchKey(invk); + GfoMsg root = GfoMsg_.new_cast_(invkKey); + root.Subs_add(m); + return root; + } + public static GfoMsg WriteMsg(GfoInvkAble invk, String k, Object... ary) {return WriteMsg(GfsCore._.FetchKey(invk), invk, k, ary);} + public static GfoMsg WriteMsg(String invkKey, GfoInvkAble invk, String k, Object... ary) { + GfsCtx ctx = GfsCtx.wtr_(); + GfoMsg m = GfoMsg_.wtr_(); + invk.Invk(ctx, 0, k, m); + GfoMsg rv = GfoMsg_.new_cast_(k); + for (int i = 0; i < m.Args_count(); i++) { + KeyVal kv = m.Args_getAt(i); + rv.Add(kv.Key(), ary[i]); + } + GfoMsg root = GfoMsg_.new_cast_(invkKey); + root.Subs_add(rv); + return root; + } +} diff --git a/100_core/src_311_gfoObj/gplx/GfoMsg.java b/100_core/src_311_gfoObj/gplx/GfoMsg.java new file mode 100644 index 000000000..8263bf1f9 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoMsg.java @@ -0,0 +1,70 @@ +/* +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; +public interface GfoMsg { + String Key(); + GfoMsg CloneNew(); + String XtoStr(); + GfoMsg Clear(); + GfoMsg Parse_(boolean v); + + int Args_count(); + KeyVal Args_getAt(int i); + GfoMsg Args_ovr(String k, Object v); + void Args_reset(); + GfoMsg Add(String k, Object v); + int Subs_count(); + GfoMsg Subs_getAt(int i); + GfoMsg Subs_add(GfoMsg m); + GfoMsg Subs_(GfoMsg... ary); + + boolean ReadBool(String k); + boolean ReadBoolOr(String k, boolean or); + boolean ReadBoolOrFalse(String k); + boolean ReadBoolOrTrue(String k); + int ReadInt(String k); + int ReadIntOr(String k, int or); + long ReadLong(String k); + long ReadLongOr(String k, long or); + float ReadFloat(String k); + float ReadFloatOr(String k, float or); + double ReadDouble(String k); + double ReadDoubleOr(String k, double or); + DateAdp ReadDate(String k); + DateAdp ReadDateOr(String k, DateAdp or); + DecimalAdp ReadDecimal(String k); + DecimalAdp ReadDecimalOr(String k, DecimalAdp or); + String ReadStr(String k); + String ReadStrOr(String k, String or); + Io_url ReadIoUrl(String k); + Io_url ReadIoUrlOr(String k, Io_url url); + boolean ReadYn(String k); + boolean ReadYn_toggle(String k, boolean cur); + boolean ReadYnOrY(String k); + byte ReadByte(String k); + byte[] ReadBry(String k); + byte[] ReadBryOr(String k, byte[] or); + Object ReadObj(String k); + Object ReadObj(String k, ParseAble parseAble); + Object ReadObjOr(String k, ParseAble parseAble, Object or); + String[]ReadStrAry(String k, String spr); + String[]ReadStrAryIgnore(String k, String spr, String ignore); + Object ReadValAt(int i); + Object CastObj(String k); + Object CastObjOr(String k, Object or); +} diff --git a/100_core/src_311_gfoObj/gplx/GfoMsgUtl.java b/100_core/src_311_gfoObj/gplx/GfoMsgUtl.java new file mode 100644 index 000000000..0e5e65dec --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoMsgUtl.java @@ -0,0 +1,25 @@ +/* +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; +public class GfoMsgUtl { + public static int SetInt(GfsCtx ctx, GfoMsg m, int cur) {return ctx.Deny() ? cur : m.ReadIntOr("v", cur);} + public static boolean SetBool(GfsCtx ctx, GfoMsg m, boolean cur) {return ctx.Deny() ? cur : m.ReadBoolOr("v", cur);} + public static String SetStr(GfsCtx ctx, GfoMsg m, String cur) {return ctx.Deny() ? cur : m.ReadStrOr("v", cur);} + public static Io_url SetIoUrl(GfsCtx ctx, GfoMsg m, Io_url cur) {return ctx.Deny() ? cur : m.ReadIoUrlOr("v", cur);} + public static DecimalAdp SetDecimal(GfsCtx ctx, GfoMsg m, DecimalAdp cur) {return ctx.Deny() ? cur : m.ReadDecimalOr("v", cur);} +} diff --git a/100_core/src_311_gfoObj/gplx/GfoMsg_.java b/100_core/src_311_gfoObj/gplx/GfoMsg_.java new file mode 100644 index 000000000..7752dbe9a --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoMsg_.java @@ -0,0 +1,268 @@ +/* +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; +public class GfoMsg_ { + public static GfoMsg as_(Object obj) {return obj instanceof GfoMsg ? (GfoMsg)obj : null;} + public static final GfoMsg Null = new GfoMsg_base().ctor_("<>", false); + public static GfoMsg new_parse_(String key) {return new GfoMsg_base().ctor_(key, true);} + public static GfoMsg new_cast_(String key) {return new GfoMsg_base().ctor_(key, false);} + public static GfoMsg srl_(GfoMsg owner, String key) { + GfoMsg rv = new_parse_(key); + owner.Subs_add(rv); + return rv; + } + public static GfoMsg root_(String... ary) {return root_leafArgs_(ary);} + public static GfoMsg root_leafArgs_(String[] ary, KeyVal... kvAry) { + int len = Array_.Len(ary); if (len == 0) throw Err_arg.cannotBe_("== 0", "@len", len); + GfoMsg root = new GfoMsg_base().ctor_(ary[0], false); + GfoMsg owner = root; + for (int i = 1; i < len; i++) { + String key = ary[i]; + GfoMsg cur = new GfoMsg_base().ctor_(key, false); + owner.Subs_add(cur); + owner = cur; + } + for (int i = 0; i < kvAry.length; i++) { + KeyVal kv = kvAry[i]; + owner.Add(kv.Key(), kv.Val()); + } + return root; + } + public static GfoMsg chain_(GfoMsg owner, String key) { + GfoMsg sub = owner; + ListAdp list = ListAdp_.new_(); + list.Add(sub.Key()); + while (sub != null) { + if (sub.Subs_count() == 0) break; + sub = (GfoMsg)sub.Subs_getAt(0); + list.Add(sub.Key()); + } + list.Add(key); + + GfoMsg root = GfoMsg_.new_parse_((String)list.FetchAt(0)); + GfoMsg cur = root; + for (int i = 1; i < list.Count(); i++) { + String k = (String)list.FetchAt(i); + GfoMsg mm = GfoMsg_.new_parse_(k); + cur.Subs_add(mm); + cur = mm; + } + return root; + } + public static GfoMsg wtr_() {return new GfoMsg_wtr().ctor_("", false);} + public static GfoMsg rdr_(String cmd) {return new GfoMsg_rdr().ctor_(cmd, false);} + public static GfoMsg basic_(String cmd, Object... vals) { + GfoMsg rv = new_cast_(cmd); + int len = vals.length; + for (int i = 0; i < len; i++) + rv.Add("", vals[i]); + return rv; + } +} +class GfoMsg_wtr extends GfoMsg_base { + @Override protected Object ReadOr(String k, Object defaultOr) { + if (args == null) args = ListAdp_.new_(); + args.Add(KeyVal_.new_(k, null)); + return defaultOr; + } +} +class GfoMsg_rdr extends GfoMsg_base { + @Override protected Object ReadOr(String k, Object defaultOr) { + if (args == null) args = ListAdp_.new_(); + args.Add(KeyVal_.new_(k, defaultOr)); + return defaultOr; + } +} +class GfoMsg_base implements GfoMsg { + public String Key() {return key;} private String key; + public int Subs_count() {return subs == null ? 0 : subs.Count();} + public GfoMsg Subs_getAt(int i) {return subs == null ? null : (GfoMsg)subs.FetchAt(i);} + public GfoMsg Subs_add(GfoMsg m) {if (subs == null) subs = ListAdp_.new_(); subs.Add(m); return this;} + public GfoMsg Subs_(GfoMsg... ary) {for (GfoMsg m : ary) Subs_add(m); return this;} + public int Args_count() {return args == null ? 0 : args.Count();} + public void Args_reset() { + counter = 0; + Args_reset(this); + } + public GfoMsg Clear() { + this.Args_reset(); + if (subs != null) subs.Clear(); + if (args != null) args.Clear(); + return this; + } + static void Args_reset(GfoMsg owner) { + int len = owner.Subs_count(); + for (int i = 0; i < len; i++) { + GfoMsg sub = owner.Subs_getAt(i); + sub.Args_reset(); + } + } + public KeyVal Args_getAt(int i) {return args == null ? null : (KeyVal)args.FetchAt(i);} + public GfoMsg Args_ovr(String k, Object v) { + if (args == null) args = ListAdp_.new_(); + for (int i = 0; i < args.Count(); i++) { + KeyVal kv = (KeyVal)args.FetchAt(i); + if (String_.Eq(k, kv.Key())) { + kv.Val_(v); + return this; + } + } + args.Add(new KeyVal(KeyVal_.Key_tid_str, k, v)); + return this; + } + public GfoMsg Parse_(boolean v) {parse = v; return this;} + public GfoMsg Add(String k, Object v) { + if (args == null) args = ListAdp_.new_(); + args.Add(new KeyVal(KeyVal_.Key_tid_str, k, v)); + return this; + } + public boolean ReadBool(String k) {Object rv = ReadOr(k,false); if (rv == Nil) ThrowNotFound(k); return parse ? Yn.parse_or_((String)rv, false) : Bool_.cast_(rv);} + public int ReadInt(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Int_.parse_((String)rv) : Int_.cast_(rv);} + public byte ReadByte(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Byte_.parse_((String)rv) : Byte_.cast_(rv);} + public long ReadLong(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Long_.parse_((String)rv) : Long_.cast_(rv);} + public float ReadFloat(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Float_.parse_((String)rv) : Float_.cast_(rv);} + public double ReadDouble(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? Double_.parse_((String)rv) : Double_.cast_(rv);} + public DecimalAdp ReadDecimal(String k) {Object rv = ReadOr(k, 0) ; if (rv == Nil) ThrowNotFound(k); return parse ? DecimalAdp_.parse_((String)rv) : DecimalAdp_.cast_(rv);} + public String ReadStr(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return (String)rv;} + public DateAdp ReadDate(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return parse ? DateAdp_.parse_gplx((String)rv) : DateAdp_.cast_(rv);} + public Io_url ReadIoUrl(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return parse ? Io_url_.new_any_((String)rv) : Io_url_.cast_(rv);} + public Object CastObj(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return rv;} + public boolean ReadBoolOr(String k, boolean or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Yn.parse_or_((String)rv, or) : Bool_.cast_(rv);} + public int ReadIntOr(String k, int or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Int_.parse_((String)rv) : Int_.cast_(rv);} + public long ReadLongOr(String k, long or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Long_.parse_((String)rv) : Long_.cast_(rv);} + public float ReadFloatOr(String k, float or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Float_.parse_((String)rv) : Float_.cast_(rv);} + public double ReadDoubleOr(String k,double or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Double_.parse_((String)rv) : Double_.cast_(rv);} + public DecimalAdp ReadDecimalOr(String k,DecimalAdp or) {Object rv = ReadOr(k, or); if (rv == Nil) return or ; return parse ? DecimalAdp_.parse_((String)rv) : DecimalAdp_.cast_(rv);} + public String ReadStrOr(String k, String or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return (String)rv;} + public DateAdp ReadDateOr(String k, DateAdp or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? DateAdp_.parse_gplx((String)rv) : DateAdp_.cast_(rv);} + public Io_url ReadIoUrlOr(String k, Io_url or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? Io_url_.new_any_((String)rv) : Io_url_.cast_(rv);} + public boolean ReadBoolOrFalse(String k) {Object rv = ReadOr(k,false); if (rv == Nil) return false ; return parse ? Yn.parse_or_((String)rv, false) : Bool_.cast_(rv);} + public boolean ReadBoolOrTrue(String k) {Object rv = ReadOr(k, true); if (rv == Nil) return true ; return parse ? Yn.parse_or_((String)rv, true) : Bool_.cast_(rv);} + public boolean ReadYnOrY(String k) {Object rv = ReadOr(k, true); if (rv == Nil) return true ; return parse ? Yn.parse_or_((String)rv, true) : Bool_.cast_(rv);} + public boolean ReadYn(String k) {Object rv = ReadOr(k,false); if (rv == Nil) ThrowNotFound(k); return parse ? Yn.parse_or_((String)rv, false) : Yn.coerce_(rv);} + public boolean ReadYn_toggle(String k, boolean cur) { + Object rv = ReadOr(k, "!"); + if (rv == Nil) ThrowNotFound(k); + if (!parse) throw Err_.new_("only parse supported"); + String rv_str = (String)rv; + return (String_.Eq(rv_str, "!")) ? !cur : Yn.parse_(rv_str); + } + public byte[] ReadBry(String k) {Object rv = ReadOr(k,false); if (rv == Nil) ThrowNotFound(k); return parse ? Bry_.new_utf8_((String)rv) : (byte[])rv;} + public byte[] ReadBryOr(String k, byte[] or) {Object rv = ReadOr(k, or); if (rv == Nil) return or; return parse ? Bry_.new_utf8_((String)rv) : (byte[])rv;} + public Object CastObjOr(String k, Object or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return rv;} + public Object ReadObj(String k) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return rv;} + public Object ReadObj(String k, ParseAble parseAble) {Object rv = ReadOr(k, null); if (rv == Nil) ThrowNotFound(k); return parse ? parseAble.ParseAsObj((String)rv) : rv;} + public Object ReadObjOr(String k, ParseAble parseAble, Object or) {Object rv = ReadOr(k, or) ; if (rv == Nil) return or ; return parse ? parseAble.ParseAsObj((String)rv) : rv;} + public String[] ReadStrAry(String k, String spr) {return String_.Split(ReadStr(k), spr);} + public String[] ReadStrAryIgnore(String k, String spr, String ignore) {return String_.Split(String_.Replace(ReadStr(k), ignore, ""), spr);} + public Object ReadValAt(int i) {return Args_getAt(i).Val();} + @gplx.Virtual protected Object ReadOr(String k, Object defaultOr) { + if (args == null) return Nil; // WORKAROUND.gfui: args null for DataBndr_whenEvt_execCmd + if (!String_.Eq(k, "")) { + for (int i = 0; i < args.Count(); i++) { + KeyVal kv = (KeyVal)args.FetchAt(i); + if (String_.Eq(k, kv.Key())) return kv.Val(); + } + } + if (counter >= args.Count()) return Nil; + for (int i = 0; i < args.Count(); i++) { + KeyVal kv = (KeyVal)args.FetchAt(i); + if (String_.Eq(kv.Key(), "") && i >= counter) { + counter++; + return kv.Val(); + } + } + return Nil; + } int counter = 0; + void ThrowNotFound(String k) {throw Err_.new_("arg not found in msg").Add("k", k).Add("counter", counter).Add("args", args);} + String ArgsXtoStr() { + if (this.Args_count() == 0) return "<>"; + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < this.Args_count(); i++) { + KeyVal rv = (KeyVal)this.Args_getAt(i); + sb.Add_fmt("{0};", rv.Key()); + } + return sb.XtoStr(); + } + public GfoMsg CloneNew() { + GfoMsg_base rv = new GfoMsg_base().ctor_(key, parse); + if (args != null) { + rv.args = ListAdp_.new_(); + for (int i = 0; i < args.Count(); i++) + rv.args.Add(args.FetchAt(i)); + } + if (subs != null) { + rv.subs = ListAdp_.new_(); + for (int i = 0; i < args.Count(); i++) { + GfoMsg sub = (GfoMsg)args.FetchAt(i); + rv.subs.Add(sub.CloneNew()); // NOTE: recursion + } + } + return rv; + } + + protected ListAdp args; + ListAdp subs; + public String XtoStr() { + String_bldr sb = String_bldr_.new_(); + XtoStr(sb, new XtoStrWkr_gplx(), this); + return sb.XtoStrAndClear(); + } + void XtoStr(String_bldr sb, XtoStrWkr wkr, GfoMsg m) { + sb.Add(m.Key()); + if (m.Subs_count() == 0) { + sb.Add(":"); + boolean first = true; + for (int i = 0; i < m.Args_count(); i++) { + KeyVal kv = m.Args_getAt(i); + if (kv.Val() == null) continue; + if (!first) sb.Add(" "); + sb.Add(kv.Key()); + sb.Add("='"); + sb.Add(wkr.XtoStr(kv.Val())); + sb.Add("'"); + first = false; + } + sb.Add(";"); + } + else { + sb.Add("."); + XtoStr(sb, wkr, m.Subs_getAt(0)); + } + } + + public GfoMsg_base ctor_(String key, boolean parse) {this.key = key; this.parse = parse; return this;} private boolean parse; + @gplx.Internal protected GfoMsg_base(){} + static final String_obj_val Nil = String_obj_val.new_("<>"); +} +interface XtoStrWkr { + String XtoStr(Object o); +} +class XtoStrWkr_gplx implements XtoStrWkr { + public String XtoStr(Object o) { + if (o == null) return "<>"; + Class type = ClassAdp_.ClassOf_obj(o); + String rv = null; + if (type == String.class) rv = String_.cast_(o); + else if (Int_.TypeMatch(type)) return Int_.XtoStr(Int_.cast_(o)); + else if (Bool_.TypeMatch(type)) return Yn.X_to_str(Bool_.cast_(o)); + else if (type == DateAdp.class) return DateAdp_.cast_(o).XtoStr_gplx(); + else rv = Object_.XtoStr_OrEmpty(o); + return String_.Replace(rv, "'", "''"); + } +} diff --git a/100_core/src_311_gfoObj/gplx/GfoMsg_tst.java b/100_core/src_311_gfoObj/gplx/GfoMsg_tst.java new file mode 100644 index 000000000..060679301 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoMsg_tst.java @@ -0,0 +1,51 @@ +/* +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; +import org.junit.*; +public class GfoMsg_tst { + @Before public void setup() { + GfsCore._.AddObj(new Mok(), "Mok"); + } + @Test public void Write1() { + GfoMsg m = GfoMsg_.root_leafArgs_(String_.Ary("a", "b"), KeyVal_.new_("int0", 1)); + tst_Msg(m, "a.b:int0='1';"); + } + @Test public void Write() { + Mok mok = new Mok(); + tst_Msg(GfoInvkXtoStr.WriteMsg(mok, Mok.Invk_Cmd0, true, 1, "a"), "Mok.Cmd0:bool0='y' int0='1' str0='a';"); + mok.Int0 = 2; + mok.Bool0 = true; + mok.Str0 = "b"; + tst_Msg(GfoInvkXtoStr.ReadMsg(mok, Mok.Invk_Cmd0), "Mok.Cmd0:bool0='y' int0='2' str0='b';"); + } + void tst_Msg(GfoMsg m, String expd) {Tfds.Eq(expd, m.XtoStr());} + class Mok implements GfoInvkAble { + public boolean Bool0; + public int Int0; + public String Str0; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_Cmd0)) { + Bool0 = m.ReadBoolOr("bool0", Bool0); + Int0 = m.ReadIntOr("int0", Int0); + Str0 = m.ReadStrOr("str0", Str0); + if (ctx.Deny()) return this; + } + return this; + } public static final String Invk_Cmd0 = "Cmd0"; + } +} diff --git a/100_core/src_311_gfoObj/gplx/GfoTemplate.java b/100_core/src_311_gfoObj/gplx/GfoTemplate.java new file mode 100644 index 000000000..fb301e880 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoTemplate.java @@ -0,0 +1,21 @@ +/* +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; +public interface GfoTemplate { + Object NewCopy(GfoTemplate template); +} diff --git a/100_core/src_311_gfoObj/gplx/GfoTemplateFactory.java b/100_core/src_311_gfoObj/gplx/GfoTemplateFactory.java new file mode 100644 index 000000000..3841966a9 --- /dev/null +++ b/100_core/src_311_gfoObj/gplx/GfoTemplateFactory.java @@ -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 . +*/ +package gplx; +public class GfoTemplateFactory implements GfoInvkAble { + public void Reg(String key, GfoTemplate template) {hash.Add(key, template);} + public Object Make(String key) { + GfoTemplate template = (GfoTemplate)hash.Fetch(key); + return template.NewCopy(template); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + ctx.Match(k, k); + Object o = hash.Fetch(k); + return o == null ? GfoInvkAble_.Rv_unhandled : o; + } + public static final GfoTemplateFactory _ = new GfoTemplateFactory(); GfoTemplateFactory() {} + HashAdp hash = HashAdp_.new_(); +} diff --git a/100_core/src_330_store/gplx/DataRdr.java b/100_core/src_330_store/gplx/DataRdr.java new file mode 100644 index 000000000..6f626581c --- /dev/null +++ b/100_core/src_330_store/gplx/DataRdr.java @@ -0,0 +1,50 @@ +/* +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; +public interface DataRdr extends SrlMgr, RlsAble { + String NameOfNode(); String XtoStr(); + Io_url Uri(); void Uri_set(Io_url s); + HashAdp EnvVars(); + boolean Parse(); void Parse_set(boolean v); + + int FieldCount(); + String KeyAt(int i); + Object ReadAt(int i); + KeyVal KeyValAt(int i); + + Object Read(String key); + String ReadStr(String key); String ReadStrOr(String key, String or); + byte[] ReadBryByStr(String key); byte[] ReadBryByStrOr(String key, byte[] or); + byte[] ReadBry(String key); byte[] ReadBryOr(String key, byte[] or); + char ReadChar(String key); char ReadCharOr(String key, char or); + int ReadInt(String key); int ReadIntOr(String key, int or); + boolean ReadBool(String key); boolean ReadBoolOr(String key, boolean or); + long ReadLong(String key); long ReadLongOr(String key, long or); + double ReadDouble(String key); double ReadDoubleOr(String key, double or); + float ReadFloat(String key); float ReadFloatOr(String key, float or); + byte ReadByte(String key); byte ReadByteOr(String key, byte or); + DecimalAdp ReadDecimal(String key); DecimalAdp ReadDecimalOr(String key, DecimalAdp or); + DateAdp ReadDate(String key); DateAdp ReadDateOr(String key, DateAdp or); + gplx.ios.Io_stream_rdr ReadRdr(String key); + + boolean MoveNextPeer(); + DataRdr Subs(); + DataRdr Subs_byName(String name); + DataRdr Subs_byName_moveFirst(String name); + void XtoStr_gfml(String_bldr sb); +} diff --git a/100_core/src_330_store/gplx/DataRdr_.java b/100_core/src_330_store/gplx/DataRdr_.java new file mode 100644 index 000000000..70a7d155c --- /dev/null +++ b/100_core/src_330_store/gplx/DataRdr_.java @@ -0,0 +1,67 @@ +/* +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; +public class DataRdr_ { + public static final DataRdr Null = new DataRdr_null(); + public static DataRdr as_(Object obj) {return obj instanceof DataRdr ? (DataRdr)obj : null;} + public static DataRdr cast_(Object obj) {try {return (DataRdr)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, DataRdr.class, obj);}} +} +class DataRdr_null implements DataRdr { + public String NameOfNode() {return XtoStr();} public String XtoStr() {return "<< NULL READER >>";} + public boolean Type_rdr() {return true;} + public HashAdp EnvVars() {return HashAdp_.Null;} + public Io_url Uri() {return Io_url_.Null;} public void Uri_set(Io_url s) {} + public boolean Parse() {return parse;} public void Parse_set(boolean v) {parse = v;} private boolean parse; + public int FieldCount() {return 0;} + public String KeyAt(int i) {return XtoStr();} + public Object ReadAt(int i) {return null;} + public KeyVal KeyValAt(int i) {return KeyVal_.new_(this.KeyAt(i), this.ReadAt(i));} + public Object Read(String name) {return null;} + public String ReadStr(String key) {return String_.Empty;} public String ReadStrOr(String key, String or) {return or;} + public byte[] ReadBryByStr(String key) {return Bry_.Empty;} public byte[] ReadBryByStrOr(String key, byte[] or) {return or;} + public byte[] ReadBry(String key) {return Bry_.Empty;} public byte[] ReadBryOr(String key, byte[] or) {return or;} + public char ReadChar(String key) {return Char_.Null;} public char ReadCharOr(String key, char or) {return or;} + public int ReadInt(String key) {return Int_.MinValue;} public int ReadIntOr(String key, int or) {return or;} + public boolean ReadBool(String key) {return false;} public boolean ReadBoolOr(String key, boolean or) {return or;} + public long ReadLong(String key) {return Long_.MinValue;} public long ReadLongOr(String key, long or) {return or;} + public double ReadDouble(String key) {return Double_.NaN;} public double ReadDoubleOr(String key, double or) {return or;} + public float ReadFloat(String key) {return Float_.NaN;} public float ReadFloatOr(String key, float or) {return or;} + public byte ReadByte(String key) {return Byte_.MinValue;} public byte ReadByteOr(String key, byte or) {return or;} + public DecimalAdp ReadDecimal(String key) {return DecimalAdp_.Zero;}public DecimalAdp ReadDecimalOr(String key, DecimalAdp or) {return or;} + public DateAdp ReadDate(String key) {return DateAdp_.MinValue;} public DateAdp ReadDateOr(String key, DateAdp or) {return or;} + public gplx.ios.Io_stream_rdr ReadRdr(String key) {return gplx.ios.Io_stream_rdr_.Null;} + public boolean MoveNextPeer() {return false;} + public DataRdr Subs() {return this;} + public DataRdr Subs_byName(String name) {return this;} + public DataRdr Subs_byName_moveFirst(String name) {return this;} + public Object StoreRoot(SrlObj root, String key) {return null;} + public boolean SrlBoolOr(String key, boolean v) {return v;} + public byte SrlByteOr(String key, byte v) {return v;} + public int SrlIntOr(String key, int or) {return or;} + public long SrlLongOr(String key, long or) {return or;} + public String SrlStrOr(String key, String or) {return or;} + public DateAdp SrlDateOr(String key, DateAdp or) {return or;} + public DecimalAdp SrlDecimalOr(String key, DecimalAdp or) {return or;} + public double SrlDoubleOr(String key, double or) {return or;} + public Object SrlObjOr(String key, Object or) {return or;} + public void SrlList(String key, ListAdp list, SrlObj proto, String itmKey) {} + public void TypeKey_(String v) {} + public void XtoStr_gfml(String_bldr sb) {sb.Add_str_w_crlf("NULL:;");} + public SrlMgr SrlMgr_new(Object o) {return this;} + public void Rls() {} +} diff --git a/100_core/src_330_store/gplx/DataWtr.java b/100_core/src_330_store/gplx/DataWtr.java new file mode 100644 index 000000000..dd829febd --- /dev/null +++ b/100_core/src_330_store/gplx/DataWtr.java @@ -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 . +*/ +package gplx; +public interface DataWtr extends SrlMgr { + HashAdp EnvVars(); + + void InitWtr(String key, Object val); + void WriteTableBgn(String name, GfoFldList fields); + void WriteNodeBgn(String nodeName); + void WriteLeafBgn(String leafName); + void WriteData(String name, Object val); + void WriteNodeEnd(); + void WriteLeafEnd(); + + void Clear(); + String XtoStr(); +} diff --git a/100_core/src_330_store/gplx/DataWtr_.java b/100_core/src_330_store/gplx/DataWtr_.java new file mode 100644 index 000000000..cc6e543c5 --- /dev/null +++ b/100_core/src_330_store/gplx/DataWtr_.java @@ -0,0 +1,48 @@ +/* +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; +import gplx.stores.*; /*DsvDataWtr_*/ +public class DataWtr_ { + public static final DataWtr Null = new DataWtr_null(); +} +class DataWtr_null implements DataWtr { + public boolean Type_rdr() {return false;} + public HashAdp EnvVars() {return envVars;} HashAdp envVars = HashAdp_.new_(); + public void InitWtr(String key, Object val) {} + public void WriteTableBgn(String name, GfoFldList fields) {} + public void WriteNodeBgn(String nodeName) {} + public void WriteLeafBgn(String leafName) {} + public void WriteData(String name, Object val) {} + public void WriteNodeEnd() {} + public void WriteLeafEnd() {} + public void Clear() {} + public String XtoStr() {return "";} + public Object StoreRoot(SrlObj root, String key) {return null;} + public boolean SrlBoolOr(String key, boolean v) {return v;} + public byte SrlByteOr(String key, byte v) {return v;} + public int SrlIntOr(String key, int or) {return or;} + public long SrlLongOr(String key, long or) {return or;} + public String SrlStrOr(String key, String or) {return or;} + public DateAdp SrlDateOr(String key, DateAdp or) {return or;} + public DecimalAdp SrlDecimalOr(String key, DecimalAdp or) {return or;} + public double SrlDoubleOr(String key, double or) {return or;} + public Object SrlObjOr(String key, Object or) {return or;} + public void SrlList(String key, ListAdp list, SrlObj proto, String itmKey) {} + public void TypeKey_(String v) {} + public SrlMgr SrlMgr_new(Object o) {return this;} +} diff --git a/100_core/src_330_store/gplx/DataWtr_base.java b/100_core/src_330_store/gplx/DataWtr_base.java new file mode 100644 index 000000000..341f93eba --- /dev/null +++ b/100_core/src_330_store/gplx/DataWtr_base.java @@ -0,0 +1,52 @@ +/* +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; +public abstract class DataWtr_base implements SrlMgr { + @gplx.Virtual public HashAdp EnvVars() {return envVars;} HashAdp envVars = HashAdp_.new_(); + public boolean Type_rdr() {return false;} + public abstract void WriteData(String key, Object o); + public abstract void WriteNodeBgn(String nodeName); + public abstract void WriteNodeEnd(); + @gplx.Virtual public void SrlList(String key, ListAdp list, SrlObj proto, String itmKey) { + this.WriteNodeBgn(key); + for (Object itmObj : list) { + SrlObj itm = (SrlObj)itmObj; + this.WriteNodeBgn(itmKey); + itm.SrlObj_Srl(this); + this.WriteNodeEnd(); + } + this.WriteNodeEnd(); + } + @gplx.Virtual public Object StoreRoot(SrlObj root, String key) { + this.WriteNodeBgn(key); + root.SrlObj_Srl(this); + this.WriteNodeEnd(); + return root; + } + public boolean SrlBoolOr(String key, boolean v) {WriteData(key, v); return v;} + public byte SrlByteOr(String key, byte v) {WriteData(key, v); return v;} + public int SrlIntOr(String key, int or) {WriteData(key, or); return or;} + public long SrlLongOr(String key, long or) {WriteData(key, or); return or;} + public String SrlStrOr(String key, String or) {WriteData(key, or); return or;} + public DateAdp SrlDateOr(String key, DateAdp or) {WriteData(key, or.XtoStr_gplx()); return or;} + public DecimalAdp SrlDecimalOr(String key, DecimalAdp or) {WriteData(key, or.XtoDecimal()); return or;} + public double SrlDoubleOr(String key, double or) {WriteData(key, or); return or;} + public Object SrlObjOr(String key, Object or) {throw Err_.not_implemented_();} + public void TypeKey_(String v) {} + public abstract SrlMgr SrlMgr_new(Object o); +} diff --git a/100_core/src_330_store/gplx/SrlMgr.java b/100_core/src_330_store/gplx/SrlMgr.java new file mode 100644 index 000000000..bb87432a8 --- /dev/null +++ b/100_core/src_330_store/gplx/SrlMgr.java @@ -0,0 +1,35 @@ +/* +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; +public interface SrlMgr { + boolean Type_rdr(); + Object StoreRoot(SrlObj root, String key); + + boolean SrlBoolOr(String key, boolean v); + byte SrlByteOr(String key, byte v); + int SrlIntOr(String key, int v); + long SrlLongOr(String key, long v); + String SrlStrOr(String key, String v); + double SrlDoubleOr(String key, double v); + DecimalAdp SrlDecimalOr(String key, DecimalAdp v); + DateAdp SrlDateOr(String key, DateAdp v); + Object SrlObjOr(String key, Object v); + void SrlList(String key, ListAdp list, SrlObj proto, String itmKey); + void TypeKey_(String v); + SrlMgr SrlMgr_new(Object o); +} diff --git a/100_core/src_330_store/gplx/SrlObj.java b/100_core/src_330_store/gplx/SrlObj.java new file mode 100644 index 000000000..a2297a79d --- /dev/null +++ b/100_core/src_330_store/gplx/SrlObj.java @@ -0,0 +1,22 @@ +/* +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; +public interface SrlObj { + SrlObj SrlObj_New(Object o); + void SrlObj_Srl(SrlMgr mgr); +} diff --git a/100_core/src_330_store/gplx/stores/DataRdr_base.java b/100_core/src_330_store/gplx/stores/DataRdr_base.java new file mode 100644 index 000000000..eb8cd19ef --- /dev/null +++ b/100_core/src_330_store/gplx/stores/DataRdr_base.java @@ -0,0 +1,213 @@ +/* +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.stores; import gplx.*; +public abstract class DataRdr_base implements SrlMgr { + public boolean Parse() {return parse;} public void Parse_set(boolean v) {parse = v;} private boolean parse; + public Io_url Uri() {return uri;} public void Uri_set(Io_url s) {uri = s;} Io_url uri = Io_url_.Null; + public abstract String NameOfNode(); + public boolean Type_rdr() {return true;} + public HashAdp EnvVars() {return envVars;} HashAdp envVars = HashAdp_.new_(); + public abstract Object Read(String key); + public abstract int FieldCount(); + public abstract String KeyAt(int i); + public abstract Object ReadAt(int i); + @gplx.Virtual public KeyVal KeyValAt(int idx) {return KeyVal_.new_(this.KeyAt(idx), ReadAt(idx));} + public String ReadStr(String key) { + Object val = Read(key); + try {return (String)val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(String.class, key, val, exc);} + } + public String ReadStrOr(String key, String or) { + Object val = Read(key); if (val == null) return or; + try {return (String)val;} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, String.class, key, val, or); return or;} + } + public byte[] ReadBryByStr(String key) {return Bry_.new_utf8_(ReadStr(key));} + public byte[] ReadBryByStrOr(String key, byte[] or) { + Object val = Read(key); if (val == null) return or; + try {return Bry_.new_utf8_((String)val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, byte[].class, key, val, or); return or;} + } + @gplx.Virtual public void SrlList(String key, ListAdp list, SrlObj proto, String itmKey) { + list.Clear(); + DataRdr subRdr = this.Subs_byName_moveFirst(key); // collection node + subRdr = subRdr.Subs(); + while (subRdr.MoveNextPeer()) { + SrlObj itm = proto.SrlObj_New(null); + itm.SrlObj_Srl(subRdr); + list.Add(itm); + } + } + @gplx.Virtual public Object StoreRoot(SrlObj root, String key) { + SrlObj clone = root.SrlObj_New(null); + clone.SrlObj_Srl(this); + return clone; + } + public abstract DataRdr Subs_byName_moveFirst(String name); + + public int ReadInt(String key) { + Object val = Read(key); + try {return (parse) ? Int_.parse_(String_.as_(val)) : Int_.cast_(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(int.class, key, val, exc);} + } + public int ReadIntOr(String key, int or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Int_.parse_(String_.as_(val)) : Int_.cast_(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, int.class, key, val, or); return or;} + } + public long ReadLongOr(String key, long or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Long_.parse_(String_.as_(val)) : Long_.cast_(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, long.class, key, val, or); return or;} + } + @gplx.Virtual public boolean ReadBool(String key) { + Object val = Read(key); + try {return (parse) ? Bool_.cast_(BoolClassXtn._.ParseOrNull(String_.as_(val))) : Bool_.cast_(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(boolean.class, key, val, exc);} + } + @gplx.Virtual public boolean ReadBoolOr(String key, boolean or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Bool_.parse_(String_.as_(val)) : Bool_.cast_(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, boolean.class, key, val, or); return or;} + } + public long ReadLong(String key) { + Object val = Read(key); + try {return (parse) ? Long_.parse_(String_.as_(val)) : Long_.cast_(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(long.class, key, val, exc);} + } + public float ReadFloat(String key) { + Object val = Read(key); + try {return (parse) ? Float_.parse_(String_.as_(val)) : Float_.cast_(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(float.class, key, val, exc);} + } + public float ReadFloatOr(String key, float or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Float_.parse_(String_.as_(val)) : Float_.cast_(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, float.class, key, val, or); return or;} + } + public double ReadDouble(String key) { + Object val = Read(key); + try {return (parse) ? Double_.parse_(String_.as_(val)) : Double_.cast_(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(double.class, key, val, exc);} + } + public double ReadDoubleOr(String key, double or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Double_.parse_(String_.as_(val)) : Double_.cast_(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, double.class, key, val, or); return or;} + } + @gplx.Virtual public byte ReadByte(String key) { + Object val = Read(key); + try {return (parse) ? Byte_.parse_(String_.as_(val)) : Byte_.cast_(val);} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(byte.class, key, val, exc);} + } + @gplx.Virtual public byte ReadByteOr(String key, byte or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? Byte_.parse_(String_.as_(val)) : Byte_.cast_(val);} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, byte.class, key, val, or); return or;} + } + @gplx.Virtual public DateAdp ReadDate(String key) { + Object val = Read(key); + try {return (parse) ? DateAdp_.parse_gplx(String_.as_(val)) : (DateAdp)val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(DateAdp.class, key, val, exc);} + } + @gplx.Virtual public DateAdp ReadDateOr(String key, DateAdp or) { + Object val = Read(key); if (val == null) return or; + try {return (parse) ? DateAdp_.parse_gplx(String_.as_(val)) : (DateAdp)val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(DateAdp.class, key, val, exc);} + } + @gplx.Virtual public DecimalAdp ReadDecimal(String key) { + Object val = Read(key); + try { + if (parse) return DecimalAdp_.parse_(String_.as_(val)); + DecimalAdp rv = DecimalAdp_.as_(val); + return (rv == null) + ? DecimalAdp_.db_(val) // HACK: GfoNde_.rdr_ will call ReadAt(int i) on Db_data_rdr; since no Db_data_rdr knows about DecimalAdp, it will always return decimalType + : rv; + } + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(DecimalAdp.class, key, val, exc);} + } + @gplx.Virtual public DecimalAdp ReadDecimalOr(String key, DecimalAdp or) { + Object val = Read(key); if (val == null) return or; + try { + if (parse) return DecimalAdp_.parse_(String_.as_(val)); + DecimalAdp rv = DecimalAdp_.as_(val); + return (rv == null) + ? DecimalAdp_.db_(val) // HACK: GfoNde_.rdr_ will call ReadAt(int i) on Db_data_rdr; since no Db_data_rdr knows about DecimalAdp, it will always return decimalType + : rv; + } + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(DecimalAdp.class, key, val, exc);} + } + public char ReadChar(String key) { + Object val = Read(key); + try { + if (parse) return Char_.parse_(String_.as_(val)); + return Char_.cast_(val); + } + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(char.class, key, val, exc);} + } + public char ReadCharOr(String key, char or) { + Object val = Read(key); if (val == null) return or; + try { + if (parse) return Char_.parse_(String_.as_(val)); + return Char_.cast_(val); + } + catch (Exception exc) {Err_.Noop(exc); return or;} + } + public byte[] ReadBry(String key) { + Object val = Read(key); + try {return (byte[])val;} + catch (Exception exc) {throw Err_dataRdr_ReadFailed_err(byte[].class, key, val, exc);} + } + public byte[] ReadBryOr(String key, byte[] or) { + Object val = Read(key); if (val == null) return or; + try {return (byte[])val;} + catch (Exception exc) {Err_dataRdr_ReadFailed_useOr(exc, byte[].class, key, val, or); return or;} + } + public gplx.ios.Io_stream_rdr ReadRdr(String key) {return gplx.ios.Io_stream_rdr_.Null;} + public boolean SrlBoolOr(String key, boolean or) {return ReadBoolOr(key, or);} + public byte SrlByteOr(String key, byte or) {return ReadByteOr(key, or);} + public int SrlIntOr(String key, int or) {return ReadIntOr(key, or);} + public long SrlLongOr(String key, long or) {return ReadLongOr(key, or);} + public String SrlStrOr(String key, String or) {return ReadStrOr(key, or);} + public DateAdp SrlDateOr(String key, DateAdp or) {return ReadDateOr(key, or);} + public DecimalAdp SrlDecimalOr(String key, DecimalAdp or) {return ReadDecimalOr(key, or);} + public double SrlDoubleOr(String key, double or) {return ReadDoubleOr(key, or);} + public Object SrlObjOr(String key, Object or) {throw Err_.not_implemented_();} + public void XtoStr_gfml(String_bldr sb) { + sb.Add(this.NameOfNode()).Add(":"); + for (int i = 0; i < this.FieldCount(); i++) { + KeyVal kv = this.KeyValAt(i); + if (i != 0) sb.Add(" "); + sb.Add_fmt("{0}='{1}'", kv.Key(), String_.Replace(kv.Val_to_str_or_empty(), "'", "\"")); + } + sb.Add(";"); + } + public abstract DataRdr Subs(); + public void TypeKey_(String v) {} + public abstract SrlMgr SrlMgr_new(Object o); + static Err Err_dataRdr_ReadFailed_err(Class type, String key, Object val, Exception inner) { + String innerMsg = inner == null ? "" : Err_.Message_lang(inner); + return Err_.new_key_("DataRdr_ReadFailed", "failed to read data").Add("key", key).Add("val", val).Add("type", type).Add("innerMsg", innerMsg); + } + static void Err_dataRdr_ReadFailed_useOr(Class type, String key, Object val, Object or) { + UsrDlg_._.Warn(UsrMsg.new_("failed to read data; substituting default").Add("key", key).Add("val", val).Add("default", or).Add("type", type)); + } + static void Err_dataRdr_ReadFailed_useOr(Exception exc, Class type, String key, Object val, Object or) { + UsrDlg_._.Warn(UsrMsg.new_("failed to read data; substituting default").Add("key", key).Add("val", val).Add("default", or).Add("type", type)); + } +} diff --git a/100_core/src_330_store/gplx/stores/DataRdr_mem.java b/100_core/src_330_store/gplx/stores/DataRdr_mem.java new file mode 100644 index 000000000..7439b2814 --- /dev/null +++ b/100_core/src_330_store/gplx/stores/DataRdr_mem.java @@ -0,0 +1,78 @@ +/* +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.stores; import gplx.*; +public class DataRdr_mem extends DataRdr_base implements GfoNdeRdr { + @Override public String NameOfNode() {return cur.Name();} + public GfoNde UnderNde() {return cur;} + @Override public int FieldCount() {return flds.Count();} + @Override public String KeyAt(int i) {return flds.FetchAt(i).Key();} + @Override public Object ReadAt(int i) {return cur.ReadAt(i);} + @Override public Object Read(String key) { + int i = flds.IndexOf(key); if (i == ListAdp_.NotFound) return null; + return cur.ReadAt(i); + } + public boolean MoveNextPeer() { + if (++peerPos >= peerList.Count()) { + cur = null; + return false; + } + cur = peerList.FetchAt_asGfoNde(peerPos); + return true; + } + @Override public DataRdr Subs() { + if (cur == null && peerList.Count() == 0) return DataRdr_.Null; + return GfoNdeRdr_.peers_(Peers_get(), this.Parse()); + } + public DataRdr Subs_byName(String name) { + if (cur == null && peerList.Count() == 0) return DataRdr_.Null; + String[] names = String_.Split(name, "/"); + GfoNdeList list = GfoNdeList_.new_(); + Subs_byName(list, names, 0, Peers_get()); + return GfoNdeRdr_.peers_(list, this.Parse()); + } + @Override public DataRdr Subs_byName_moveFirst(String name) { + DataRdr subRdr = Subs_byName(name); + boolean hasFirst = subRdr.MoveNextPeer(); + return (hasFirst) ? subRdr : DataRdr_.Null; + } + public String XtoStr() {return cur.XtoStr();} + public void Rls() {this.cur = null; this.peerList = null;} + @Override public SrlMgr SrlMgr_new(Object o) {return new DataRdr_mem();} + GfoNdeList Peers_get() { + boolean initialized = cur == null && peerPos == -1 && peerList.Count() > 0; // initialized = no current, at bof, subs available + return initialized ? peerList.FetchAt_asGfoNde(0).Subs() : cur.Subs(); + } + void Subs_byName(GfoNdeList list, String[] names, int depth, GfoNdeList peers) { + String name = names[depth]; + for (int i = 0; i < peers.Count(); i++) { + GfoNde sub = peers.FetchAt_asGfoNde(i); if (sub == null) continue; + if (!String_.Eq(name, sub.Name())) continue; + if (depth == names.length - 1) + list.Add(sub); + else + Subs_byName(list, names, depth + 1, sub.Subs()); + } + } + + GfoNde cur; GfoNdeList peerList; int peerPos = -1; GfoFldList flds; + public static DataRdr_mem new_(GfoNde cur, GfoFldList flds, GfoNdeList peerList) { + DataRdr_mem rv = new DataRdr_mem(); + rv.cur = cur; rv.peerList = peerList; rv.flds = flds; + return rv; + } @gplx.Internal protected DataRdr_mem() {} +} diff --git a/100_core/src_330_store/gplx/stores/GfoNdeRdr.java b/100_core/src_330_store/gplx/stores/GfoNdeRdr.java new file mode 100644 index 000000000..e0547a337 --- /dev/null +++ b/100_core/src_330_store/gplx/stores/GfoNdeRdr.java @@ -0,0 +1,21 @@ +/* +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.stores; import gplx.*; +public interface GfoNdeRdr extends DataRdr { + GfoNde UnderNde(); +} diff --git a/100_core/src_330_store/gplx/stores/GfoNdeRdr_.java b/100_core/src_330_store/gplx/stores/GfoNdeRdr_.java new file mode 100644 index 000000000..3143ede58 --- /dev/null +++ b/100_core/src_330_store/gplx/stores/GfoNdeRdr_.java @@ -0,0 +1,48 @@ +/* +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.stores; import gplx.*; +public class GfoNdeRdr_ { + public static GfoNdeRdr kvs_(KeyValList kvList) { + GfoFldList flds = GfoFldList_.new_(); + int pairsLen = kvList.Count(); + Object[] vals = new Object[pairsLen]; + for (int i = 0; i < pairsLen; i++) { + KeyVal pair = kvList.GetAt(i); + flds.Add(pair.Key(), StringClassXtn._); + vals[i] = pair.Val_to_str_or_empty(); + } + GfoNde nde = GfoNde_.vals_(flds, vals); + return root_(nde, true); + } + public static GfoNdeRdr root_parseNot_(GfoNde root) {return root_(root, true);} + public static GfoNdeRdr root_(GfoNde root, boolean parse) { + DataRdr_mem rv = DataRdr_mem.new_(root, root.Flds(), root.Subs()); rv.Parse_set(parse); + return rv; + } + public static GfoNdeRdr leaf_(GfoNde cur, boolean parse) { + DataRdr_mem rv = DataRdr_mem.new_(cur, cur.Flds(), GfoNdeList_.Null); rv.Parse_set(parse); + return rv; + } + public static GfoNdeRdr peers_(GfoNdeList peers, boolean parse) { + GfoFldList flds = peers.Count() == 0 ? GfoFldList_.Null : peers.FetchAt_asGfoNde(0).Flds(); + DataRdr_mem rv = DataRdr_mem.new_(null, flds, peers); rv.Parse_set(parse); + return rv; + } + public static GfoNdeRdr as_(Object obj) {return obj instanceof GfoNdeRdr ? (GfoNdeRdr)obj : null;} + public static GfoNdeRdr cast_(Object obj) {try {return (GfoNdeRdr)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, GfoNdeRdr.class, obj);}} +} diff --git a/100_core/src_330_store/gplx/stores/xmls/XmlDataRdr.java b/100_core/src_330_store/gplx/stores/xmls/XmlDataRdr.java new file mode 100644 index 000000000..ff92fb689 --- /dev/null +++ b/100_core/src_330_store/gplx/stores/xmls/XmlDataRdr.java @@ -0,0 +1,77 @@ +/* +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.stores.xmls; import gplx.*; import gplx.stores.*; +import gplx.xmls.*; /*Xpath_*/ +public class XmlDataRdr extends DataRdr_base implements DataRdr { + @Override public String NameOfNode() {return nde.Name();} public String XtoStr() {return nde.Xml_outer();} + @Override public int FieldCount() {return nde.Atrs() == null ? 0 : nde.Atrs().Count();} // nde.Attributes == null when nde is XmlText; ex: val + @Override public String KeyAt(int i) {return nde.Atrs().FetchAt(i).Name();} + @Override public Object ReadAt(int i) { + XmlAtr attrib = nde.Atrs().FetchAt(i); + return (attrib == null) ? null : attrib.Value(); + } + @Override public Object Read(String key) { + return nde.Atrs().FetchValOr(key, null); + } + public boolean MoveNextPeer() { + if (++pos >= peerList.Count()){ // moved out Of range + nde = null; + return false; + } + nde = peerList.FetchAt(pos); + return true; + } + @Override public DataRdr Subs() { + XmlNdeList list = Xpath_.SelectElements(nde); + XmlDataRdr rv = new XmlDataRdr(); + rv.ctor_(list, null); + return rv; + } + @Override public DataRdr Subs_byName_moveFirst(String name) { + DataRdr subRdr = Subs_byName(name); + boolean hasFirst = subRdr.MoveNextPeer(); + return (hasFirst) ? subRdr : DataRdr_.Null; + } + public DataRdr Subs_byName(String name) { + XmlNdeList list = Xpath_.SelectAll(nde, name); + XmlDataRdr rv = new XmlDataRdr(); + rv.ctor_(list, null); + return rv; + } + public void Rls() {nde = null; peerList = null;} + public String NodeValue_get() { + if (nde.SubNdes().Count() != 1) return ""; + XmlNde sub = nde.SubNdes().FetchAt(0); + return (sub.NdeType_textOrEntityReference()) ? sub.Text_inner() : ""; + } + public String Node_OuterXml() {return nde.Xml_outer();} + @Override public SrlMgr SrlMgr_new(Object o) {return new XmlDataRdr();} + void LoadString(String raw) { + XmlDoc xdoc = XmlDoc_.parse_(raw); + XmlNdeList list = Xpath_.SelectElements(xdoc.Root()); + ctor_(list, xdoc.Root()); + } + void ctor_(XmlNdeList peerList, XmlNde nde) { + this.peerList = peerList; this.nde = nde; pos = -1; + } + + XmlNde nde = null; + XmlNdeList peerList = null; int pos = -1; + @gplx.Internal protected XmlDataRdr(String raw) {this.LoadString(raw); this.Parse_set(true);} + XmlDataRdr() {this.Parse_set(true);} +} diff --git a/100_core/src_330_store/gplx/stores/xmls/XmlDataRdr_.java b/100_core/src_330_store/gplx/stores/xmls/XmlDataRdr_.java new file mode 100644 index 000000000..ddcfad373 --- /dev/null +++ b/100_core/src_330_store/gplx/stores/xmls/XmlDataRdr_.java @@ -0,0 +1,25 @@ +/* +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.stores.xmls; import gplx.*; import gplx.stores.*; +public class XmlDataRdr_ { + public static XmlDataRdr file_(Io_url url) { + String text = Io_mgr._.LoadFilStr(url); + return new XmlDataRdr(text); + } + public static XmlDataRdr text_(String text) {return new XmlDataRdr(text);} +} diff --git a/100_core/src_330_store/gplx/stores/xmls/XmlDataWtr_.java b/100_core/src_330_store/gplx/stores/xmls/XmlDataWtr_.java new file mode 100644 index 000000000..1674e8b2e --- /dev/null +++ b/100_core/src_330_store/gplx/stores/xmls/XmlDataWtr_.java @@ -0,0 +1,112 @@ +/* +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.stores.xmls; import gplx.*; import gplx.stores.*; +public class XmlDataWtr_ { + public static DataWtr new_() {return XmlDataWtr.new_();} +} +class XmlDataWtr extends DataWtr_base implements DataWtr { + public void InitWtr(String key, Object val) {} + @Override public void WriteData(String name, Object val) { +// if (val == null) return; + String valString = Object_.XtoStr_OrEmpty(val); + int valStringLen = String_.Len(valString); + sb.Add(" ").Add(name).Add("=\""); + for (int i = 0; i < valStringLen; i++) { + char c = String_.CharAt(valString, i); + if (c == '<') sb.Add("<"); + else if (c == '>') sb.Add(">"); + else if (c == '&') sb.Add("&"); + else if (c == '\"') sb.Add(""e;"); + else sb.Add(c); + } + sb.Add("\""); +// XmlAttribute atr = doc.CreateAttribute(name); +// atr.Value = (val == null) ? String_.Empty : val.toString(); +// nde.Attributes.Append(atr); + } + public void WriteLeafBgn(String leafName) {this.WriteXmlNodeBegin(leafName);} + public void WriteLeafEnd() {} + public void WriteTableBgn(String name, GfoFldList fields) {this.WriteXmlNodeBegin(name);} + @Override public void WriteNodeBgn(String nodeName) {this.WriteXmlNodeBegin(nodeName);} + @Override public void WriteNodeEnd() {this.WriteXmlNodeEnd();} + public String XtoStr() { + while (names.Count() > 0) { + WriteXmlNodeEnd(); + } + return sb.XtoStr(); +// while (nde.ParentNode != null) +// WriteXmlNodeEnd(); // close all open ndes automatically +// return doc.OuterXml; + } + public void WriteComment(String comment) { + sb.Add(""); +// XmlComment xmlComment = doc.CreateComment(comment); +// nde.AppendChild(xmlComment); + } + public void Clear() { + sb.Clear(); +// doc = new XmlDocument(); + } + void WriteXmlNodeBegin(String name) { + if (ndeOpened) { + sb.Add(">" + String_.CrLf); + } + ndeOpened = true; + names.Add(name); + sb.Add("<" + name); +// XmlNode owner = nde; +// nde = doc.CreateElement(name); +// if (owner == null) // first call to WriteXmlNodeBegin(); append child to doc +// doc.AppendChild(nde); +// else { +// WriteLineFeedIfNeeded(doc, owner); +// owner.AppendChild(nde); +// } + } + void WriteXmlNodeEnd() { + if (ndeOpened) { + sb.Add(" />" + String_.CrLf); + ndeOpened = false; + } + else { + String name = (String)names.FetchAtLast(); + sb.Add("" + String_.CrLf); + } + names.DelAt(names.Count() - 1); + // if (nde.ParentNode == null) throw Err_.new_("WriteXmlNodeEnd() called on root node"); +// nde = nde.ParentNode; +// WriteLineFeed(doc, nde); + } +// void WriteLineFeed(XmlDocument doc, XmlNode owner) { +// XmlSignificantWhitespace crlf = doc.CreateSignificantWhitespace(String_.CrLf); +// owner.AppendChild(crlf); +// } +// void WriteLineFeedIfNeeded(XmlDocument doc, XmlNode owner) { +// XmlSignificantWhitespace lastSubNode = owner.ChildNodes[owner.ChildNodes.Count - 1] as XmlSignificantWhitespace; +// if (lastSubNode == null) +// WriteLineFeed(doc, owner); // write LineFeed for consecutive WriteXmlNodeBegin calls; ex: +// } + @Override public SrlMgr SrlMgr_new(Object o) {return new XmlDataWtr();} + boolean ndeOpened = false; +// int atrCount = 0; +// int ndeState = -1; static final int NdeState0_Opened = 0, NdeState0_H = 1; +// XmlDocument doc = new XmlDocument(); XmlNode nde; + ListAdp names = ListAdp_.new_(); + String_bldr sb = String_bldr_.new_(); + public static XmlDataWtr new_() {return new XmlDataWtr();} XmlDataWtr() {} +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdrOpts.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdrOpts.java new file mode 100644 index 000000000..3205f8c9e --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdrOpts.java @@ -0,0 +1,25 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +public class DsvDataRdrOpts { + public boolean HasHeader() {return hasHeader;} public DsvDataRdrOpts HasHeader_(boolean val) {hasHeader = val; return this;} private boolean hasHeader = false; + public String NewLineSep() {return newLineSep;} public DsvDataRdrOpts NewLineSep_(String val) {newLineSep = val; return this;} private String newLineSep = String_.CrLf; + public String FldSep() {return fldSep;} public DsvDataRdrOpts FldSep_(String val) {fldSep = val; return this;} private String fldSep = ","; + public GfoFldList Flds() {return flds;} public DsvDataRdrOpts Flds_(GfoFldList val) {flds = val; return this;} GfoFldList flds = GfoFldList_.Null; + public static DsvDataRdrOpts new_() {return new DsvDataRdrOpts();} DsvDataRdrOpts() {} +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_.java new file mode 100644 index 000000000..d613c1e00 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_.java @@ -0,0 +1,247 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import gplx.texts.*; /*CharStream*/ +public class DsvDataRdr_ { + public static DataRdr dsv_(String text) {return DsvParser.dsv_().ParseAsRdr(text);} + public static DataRdr csv_hdr_(String text) {return csv_opts_(text, DsvDataRdrOpts.new_().HasHeader_(true));} + public static DataRdr csv_dat_(String text) {return csv_opts_(text, DsvDataRdrOpts.new_());} + public static DataRdr csv_dat_with_flds_(String text, String... flds) {return csv_opts_(text, DsvDataRdrOpts.new_().Flds_(GfoFldList_.str_(flds)));} + public static DataRdr csv_opts_(String text, DsvDataRdrOpts opts) { + DsvParser parser = DsvParser.csv_(opts.HasHeader(), opts.Flds()); // NOTE: this sets up the bldr; do not call .Init after this + parser.Symbols().RowSep_(opts.NewLineSep()).FldSep_(opts.FldSep()); + + DataRdr root = parser.ParseAsRdr(text); // don't return root; callers expect csv to return rdr for rows + DataRdr csvTable = root.Subs(); + return csvTable.Subs(); + } +} +class DsvParser { + @gplx.Internal protected DsvSymbols Symbols() {return sym;} DsvSymbols sym = DsvSymbols.default_(); + @gplx.Internal protected void Init() {sb.Clear(); bldr.Init(); qteOn = false;} + @gplx.Internal protected DataRdr ParseAsRdr(String raw) {return GfoNdeRdr_.root_(ParseAsNde(raw), csvOn);} // NOTE: csvOn means parse; assume manual typed flds not passed in (ex: id,Int) + @gplx.Internal protected GfoNde ParseAsNde(String raw) { + if (String_.Eq(raw, "")) return bldr.BldRoot(); + CharStream strm = CharStream.pos0_(raw); + while (true) { + if (strm.AtEnd()) { + ProcessLine(strm, true); + break; + } + if (qteOn) + ReadStreamInQte(strm); + else if (strm.MatchAndMove(sym.QteDlm())) + qteOn = true; + else if (strm.MatchAndMove(sym.FldSep())) + ProcessFld(strm); + else if (strm.MatchAndMove(sym.RowSep())) + ProcessLine(strm, false); + else { + sb.Add(strm.Cur()); + strm.MoveNext(); + } + } + return bldr.BldRoot(); + } + void ReadStreamInQte(CharStream strm) { + if (strm.MatchAndMove(sym.QteDlm())) { // is quote + if (strm.MatchAndMove(sym.QteDlm())) // double quote -> quote; "a"" + sb.Add(sym.QteDlm()); + else if (strm.MatchAndMove(sym.FldSep())) { // closing quote before field; "a", + ProcessFld(strm); + qteOn = false; + } + else if (strm.MatchAndMove(sym.RowSep()) || strm.AtEnd()) { // closing quote before record; "a"\r\n + ProcessLine(strm, false); + qteOn = false; + } + else + throw Err_.new_("invalid quote in quoted field; quote must be followed by quote, fieldSpr, or recordSpr").Add("sym", strm.Cur()).Add("text", strm.XtoStrByPos(strm.Pos() - 10, strm.Pos() + 10)); + } + else { // regular char; append and continue + sb.Add(strm.Cur()); + strm.MoveNext(); + } + } + void ProcessFld(CharStream strm) { + String val = sb.XtoStrAndClear(); + if (cmdSeqOn) { + cmdSeqOn = false; + if (String_.Eq(val, sym.CmdDlm()) && qteOn) { // 2 cmdDlms in a row; cmdSeq encountered; next fld must be cmdName + nextValType = ValType_CmdName; + return; + } + tkns.Add(sym.CmdDlm()); // curTkn is not cmdDlm; prevTkn happened to be cmdDlm; add prev to tkns and continue; ex: a, ,b + } + if (String_.Eq(val, sym.CmdDlm())) // val is cmdDlm; do not add now; wait til next fld to decide + cmdSeqOn = true; + else if (nextValType == ValType_Data) { + if (String_.Len(val) == 0) val = qteOn ? "" : null; // differentiate between null and emptyString; ,, vs ,"", + tkns.Add(val); + } + else if (nextValType == ValType_CmdName) { + if (String_.Eq(val, sym.TblNameSym())) lineMode = LineType_TblBgn; // # + else if (String_.Eq(val, sym.FldNamesSym())) lineMode = LineType_FldNames; // @ + else if (String_.Eq(val, sym.FldTypesSym())) lineMode = LineType_FldTypes; // $ + else if (String_.Eq(val, sym.CommentSym())) lineMode = LineType_Comment; // ' + else throw Err_.new_("unknown dsv cmd").Add("cmd", val); + } + else + throw Err_.new_("unable to process field value").Add("value", val); + } + void ProcessLine(CharStream strm, boolean cleanup) { + if (sb.Count() == 0 && tkns.Count() == 0) + if (csvOn) { // csvOn b/c csvMode allows blank lines as empty data + if (cleanup) // cleanup b/c blankLine should not be added when called by cleanup, else will always add extra row at end + return; // cleanup, so no further action needed; return; + else + ProcessFld(strm); + } + else + lineMode = LineType_BlankLine; + else + ProcessFld(strm); // always process fld; either (1) chars waiting in sb "a,b"; or (2) last char was fldSep "a," + if (cmdSeqOn) { // only happens if last fld is comma space (, ); do not let cmds span lines + cmdSeqOn = false; + tkns.Add(sym.CmdDlm()); + } + if (lineMode == LineType_TblBgn) bldr.MakeTblBgn(tkns); + else if (lineMode == LineType_FldNames) bldr.MakeFldNames(tkns); + else if (lineMode == LineType_FldTypes) bldr.MakeFldTypes(tkns); + else if (lineMode == LineType_Comment) bldr.MakeComment(tkns); + else if (lineMode == LineType_BlankLine) bldr.MakeBlankLine(); + else bldr.MakeVals(tkns); + nextValType = ValType_Data; + lineMode = LineType_Data; + } + String_bldr sb = String_bldr_.new_(); ListAdp tkns = ListAdp_.new_(); DsvTblBldr bldr = DsvTblBldr.new_(); + boolean cmdSeqOn = false, qteOn = false, csvOn = false; + int nextValType = ValType_Data, lineMode = LineType_Data; + @gplx.Internal protected static DsvParser dsv_() {return new DsvParser();} + @gplx.Internal protected static DsvParser csv_(boolean hasHdr, GfoFldList flds) { + DsvParser rv = new DsvParser(); + rv.csvOn = true; + rv.lineMode = hasHdr ? LineType_FldNames : LineType_Data; + ListAdp names = ListAdp_.new_(), types = ListAdp_.new_(); + for (int i = 0; i < flds.Count(); i++) { + GfoFld fld = flds.FetchAt(i); + names.Add(fld.Key()); types.Add(fld.Type().Key()); + } + rv.bldr.MakeFldNames(names); rv.bldr.MakeFldTypes(types); + return rv; + } + static final int ValType_Data = 0, ValType_CmdName = 1; + static final int LineType_Data = 0, LineType_Comment = 1, LineType_TblBgn = 2, LineType_FldNames = 3, LineType_FldTypes = 4, LineType_BlankLine = 5; +} +class DsvTblBldr { + public void Init() { + root = GfoNde_.root_(); tbl = GfoNde_.tbl_(NullTblName, GfoFldList_.new_()); + fldNames.Clear(); fldTypes.Clear(); + stage = Stage_Init; + } + public GfoNde BldRoot() { + if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress + return root; + } + public void MakeTblBgn(ListAdp tkns) { + if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress + tbl.Name_((String)tkns.FetchAt(0)); + layout.HeaderList().Add_TableName(); + stage = Stage_Hdr; tkns.Clear(); + } + public void MakeFldNames(ListAdp tkns) { + if (stage == Stage_Row) CreateTbl(); // CreateTbl if ROW is in progress; NOTE: exclude HDR, as first HDR would have called CreateTbl + fldNames.Clear(); + for (Object fldNameObj : tkns) + fldNames.Add(fldNameObj); + layout.HeaderList().Add_LeafNames(); + stage = Stage_Hdr; tkns.Clear(); + } + public void MakeFldTypes(ListAdp tkns) { + if (stage == Stage_Row) CreateTbl(); // CreateTbl if ROW is in progress; NOTE: exclude HDR, as first HDR would have called CreateTbl + fldTypes.Clear(); + for (Object fldTypeObj : tkns) { + ClassXtn type = ClassXtnPool._.FetchOrFail((String)fldTypeObj); + fldTypes.Add(type); + } + layout.HeaderList().Add_LeafTypes(); + stage = Stage_Hdr; tkns.Clear(); + } + public void MakeComment(ListAdp tkns) { + if (stage == Stage_Row) // comments in ROW; ignore; NOTE: tkns.Clear() could be merged, but this seems clearer + tkns.Clear(); + else { // comments in HDR + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < tkns.Count(); i++) + sb.Add((String)tkns.FetchAt(i)); + layout.HeaderList().Add_Comment(sb.XtoStr()); + tkns.Clear(); + } + } + public void MakeBlankLine() { + if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress; + layout.HeaderList().Add_BlankLine(); + stage = Stage_Init; // NOTE: mark stage as INIT; + } + public void MakeVals(ListAdp tkns) { + if (stage != Stage_Row) CreateFlds(tkns.Count()); // stage != Stage_Row means if (noRowsCreated) + GfoNde row = GfoNde_.vals_(tbl.SubFlds(), MakeValsAry(tkns)); + tbl.Subs().Add(row); + stage = Stage_Row; tkns.Clear(); + } + Object[] MakeValsAry(ListAdp tkns) { + GfoFldList subFlds = tbl.SubFlds(); int subFldsCount = subFlds.Count(); + if (tkns.Count() > subFldsCount) throw Err_.new_("values.Count cannot be greater than fields.Count").Add("values.Count", tkns.Count()).Add("fields.Count", subFldsCount); + Object[] rv = new Object[subFldsCount]; + for (int i = 0; i < subFldsCount; i++) { + ClassXtn typx = subFlds.FetchAt(i).Type(); + String val = i < tkns.Count() ? (String)tkns.FetchAt(i) : null; + rv[i] = typx.ParseOrNull(val); + } + return rv; + } + void CreateTbl() { + if (tbl.SubFlds().Count() == 0) CreateFlds(0); // this check occurs when tbl has no ROW; (TOMB: tdb test fails) + tbl.EnvVars().Add(DsvStoreLayout.Key_const, layout); + root.Subs().Add(tbl); // add pending table + layout = DsvStoreLayout.dsv_brief_(); + tbl = GfoNde_.tbl_(NullTblName, GfoFldList_.new_()); + stage = Stage_Hdr; + } + void CreateFlds(int valCount) { + int fldNamesCount = fldNames.Count(), fldTypesCount = fldTypes.Count(); + if (fldNamesCount == 0 && fldTypesCount == 0) { // csv tbls where no names or types, just values + for (int i = 0; i < valCount; i++) + tbl.SubFlds().Add("fld" + i, StringClassXtn._); + } + else { // all else, where either names or types is defined + int maxCount = fldNamesCount > fldTypesCount ? fldNamesCount : fldTypesCount; + for (int i = 0; i < maxCount; i++) { + String name = i < fldNamesCount ? (String)fldNames.FetchAt(i) : "fld" + i; + ClassXtn typx = i < fldTypesCount ? (ClassXtn)fldTypes.FetchAt(i) : StringClassXtn._; + tbl.SubFlds().Add(name, typx); + } + } + } + GfoNde root; GfoNde tbl; DsvStoreLayout layout = DsvStoreLayout.dsv_brief_(); + ListAdp fldNames = ListAdp_.new_(); ListAdp fldTypes = ListAdp_.new_(); + int stage = Stage_Init; + public static DsvTblBldr new_() {return new DsvTblBldr();} DsvTblBldr() {this.Init();} + @gplx.Internal protected static final String NullTblName = ""; + static final int Stage_Init = 0, Stage_Hdr = 1, Stage_Row = 2; +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_csv_dat_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_csv_dat_tst.java new file mode 100644 index 000000000..e733061f7 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_csv_dat_tst.java @@ -0,0 +1,213 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataRdr_csv_dat_tst { + @Before public void setup() { + fx.Parser_(DsvParser.csv_(false, GfoFldList_.Null)); + fx.Clear(); + } DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void Empty() { + fx.run_parse_(""); + fx.tst_DatNull(); + } + @Test public void Fld_0() { + fx.run_parse_("a"); + fx.tst_DatCsv(fx.ary_("a")); + } + @Test public void Fld_N() { + fx.run_parse_("a,b,c"); + fx.tst_FldListCsv("fld0", "fld1", "fld2"); + fx.tst_DatCsv(fx.ary_("a", "b", "c")); + } + @Test public void Row_N() { + fx.run_parse_ + ( "a,b,c", String_.CrLf + , "1,2,3" + ); + fx.tst_DatCsv + ( fx.ary_("a", "b", "c") + , fx.ary_("1", "2", "3") + ); + } + @Test public void Escape_WhiteSpace() { + fx.run_parse_("a,\" \t\",c"); + fx.tst_DatCsv(fx.ary_("a", " \t", "c")); + } + @Test public void Escape_FldSep() { + fx.run_parse_("a,\",\",c"); + fx.tst_DatCsv(fx.ary_("a", ",", "c")); + } + @Test public void Escape_RowSep() { + fx.run_parse_("a,\"" + String_.CrLf + "\",c"); + fx.tst_DatCsv(fx.ary_("a", String_.CrLf, "c")); + } + @Test public void Escape_Quote() { + fx.run_parse_("a,\"\"\"\",c"); + fx.tst_DatCsv(fx.ary_("a", "\"", "c")); + } + @Test public void Blank_Null() { + fx.run_parse_("a,,c"); + fx.tst_DatCsv(fx.ary_("a", null, "c")); + } + @Test public void Blank_EmptyString() { + fx.run_parse_("a,\"\",c"); + fx.tst_DatCsv(fx.ary_("a", "", "c")); + } + @Test public void Blank_Null_Multiple() { + fx.run_parse_(",,"); + fx.tst_DatCsv(fx.ary_(null, null, null)); + } + @Test public void TrailingNull() { + fx.run_parse_("a,"); + fx.tst_DatCsv(fx.ary_("a", null)); + } + @Test public void TrailingEmpty() { + fx.run_parse_("a,\"\""); + fx.tst_DatCsv(fx.ary_("a", "")); + } + @Test public void Quote_Error() { + try {fx.run_parse_("a,\"\" ,c"); Tfds.Fail_expdError();} + catch (Err exc) { + Tfds.Eq_true(String_.Has(Err_.Message_lang(exc), "invalid quote in quoted field")); + } + } + @Test public void Misc_AllowValsLessThanFields() { + // assume null when vals.Count < fields.Count; PURPOSE: MsExcel will not save trailing commas for csvExport; ex: a, -> a + fx.run_parse_ + ( "a0,a1", String_.CrLf + , "b0" + ); + fx.tst_DatCsv + ( fx.ary_("a0", "a1") + , fx.ary_("b0", null) + ); + } + @Test public void Misc_NewLineValidForSingleColumnTables() { + fx.run_parse_ + ( "a", String_.CrLf + , String_.CrLf + , "c" , String_.CrLf + , String_.CrLf + ); + fx.tst_DatCsv + ( fx.ary_("a") + , fx.ary_null_() + , fx.ary_("c") + , fx.ary_null_() + ); + } + @Test public void Misc_NewLineValidForSingleColumnTables_FirstLine() { + fx.run_parse_ + ( String_.CrLf + , "b", String_.CrLf + , "c" + ); + fx.tst_DatCsv + ( fx.ary_null_() + , fx.ary_("b") + , fx.ary_("c") + ); + } + @Test public void Hdr_Basic() { + fx.Parser_(DsvParser.csv_(true, GfoFldList_.Null)); + fx.run_parse_ + ( "id,name", String_.CrLf + , "0,me" + ); + fx.tst_FldListCsv("id", "name"); + fx.tst_DatCsv(fx.ary_("0", "me")); + } +// @Test public void Hdr_Manual() { +// fx.Parser_(DsvParser.csv_(false, GfoFldList_.new_().Add("id", IntClassXtn._).Add("name", StringClassXtn._), true)); +// fx.run_parse_("0,me"); +// fx.tst_DatCsv(fx.ary_(0, "me")); // NOTE: testing auto-parsing of id to int b/c id fld is IntClassXtn._; +// } +} +class DsvDataRdr_fxt { + public Object[] ary_(Object... ary) {return ary;} + public Object[] ary_null_() {return new Object[] {null};} + public void Clear() {parser.Init(); root = null;} + public DsvParser Parser() {return parser;} public DsvDataRdr_fxt Parser_(DsvParser val) {parser = val; return this;} DsvParser parser = DsvParser.dsv_(); + public GfoNde Root() {return root;} GfoNde root; + public void run_parse_(String... ary) {root = parser.ParseAsNde(String_.Concat(ary));} + public void run_parse_lines_(String... ary) {root = parser.ParseAsNde(String_.Concat_lines_crlf(ary));} + public DsvDataRdr_fxt tst_FldListCsv(String... names) {return tst_Flds(TblIdx0, GfoFldList_.str_(names));} + public DsvDataRdr_fxt tst_Flds(int tblIdx, GfoFldList expdFlds) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(tblIdx); + ListAdp expdList = ListAdp_.new_(), actlList = ListAdp_.new_(); + String_bldr sb = String_bldr_.new_(); + GfoFldList_BldDbgList(expdFlds, expdList, sb); + GfoFldList_BldDbgList(tbl.SubFlds(), actlList, sb); + Tfds.Eq_list(expdList, actlList); + return this; + } + void GfoFldList_BldDbgList(GfoFldList flds, ListAdp list, String_bldr sb) { + for (int i = 0; i < flds.Count(); i++) { + GfoFld fld = flds.FetchAt(i); + sb.Add(fld.Key()).Add(",").Add(fld.Type().Key()); + list.Add(sb.XtoStrAndClear()); + } + } + public DsvDataRdr_fxt tst_Tbls(String... expdNames) { + ListAdp actlList = ListAdp_.new_(); + for (int i = 0; i < root.Subs().Count(); i++) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(i); + actlList.Add(tbl.Name()); + } + Tfds.Eq_ary(expdNames, actlList.XtoStrAry()); + return this; + } + public DsvDataRdr_fxt tst_DatNull() { + Tfds.Eq(0, root.Subs().Count()); + return this; + } + public DsvDataRdr_fxt tst_DatCsv(Object[]... expdRows) {return tst_Dat(0, expdRows);} + public DsvDataRdr_fxt tst_Dat(int tblIdx, Object[]... expdRows) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(tblIdx); + if (expdRows.length == 0) { + Tfds.Eq(0, tbl.Subs().Count()); + return this; + } + ListAdp expdList = ListAdp_.new_(), actlList = ListAdp_.new_(); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < tbl.Subs().Count(); i++) { + GfoNde row = tbl.Subs().FetchAt_asGfoNde(i); + for (int j = 0; j < row.Flds().Count(); j++) { + if (j != 0) sb.Add("~"); + sb.Add_obj(Object_.XtoStr_OrNullStr(row.ReadAt(j))); + } + expdList.Add(sb.XtoStrAndClear()); + } + for (Object[] expdRow : expdRows) { + if (expdRow == null) { + actlList.Add(""); + continue; + } + for (int j = 0; j < expdRow.length; j++) { + if (j != 0) sb.Add("~"); + sb.Add_obj(Object_.XtoStr_OrNullStr(expdRow[j])); + } + actlList.Add(sb.XtoStrAndClear()); + } + Tfds.Eq_list(expdList, actlList); + return this; + } + public static DsvDataRdr_fxt new_() {return new DsvDataRdr_fxt();} + static final int TblIdx0 = 0; +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_dat_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_dat_tst.java new file mode 100644 index 000000000..c599a674d --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_dat_tst.java @@ -0,0 +1,70 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataRdr_dsv_dat_tst { + @Before public void setup() {fx.Clear();} DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void NameOnly() { + fx.run_parse_("tableName, ,\" \",#"); + fx.tst_Tbls("tableName"); + fx.tst_Dat(0); + } + @Test public void Rows_N() { + fx.run_parse_lines_ + ( "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_Tbls("numbers"); + fx.tst_Dat(0 + , fx.ary_("1", "2", "3") + , fx.ary_("4", "5", "6") + ); + } + @Test public void Tbls_N() { + fx.run_parse_lines_ + ( "letters, ,\" \",#" + , "a,b,c" + , "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_Tbls("letters", "numbers"); + fx.tst_Dat(0, fx.ary_("a", "b", "c")); + fx.tst_Dat(1, fx.ary_("1", "2", "3"), fx.ary_("4", "5", "6")); + } + @Test public void IgnoreTrailingBlankRow() { + fx.run_parse_lines_ + ( "letters, ,\" \",#" + , "a,b,c" + , "" // ignored + ); + fx.tst_Tbls("letters"); + fx.tst_Dat(0, fx.ary_("a", "b", "c")); + } + @Test public void AllowCommentsDuringData() { + fx.run_parse_lines_ + ( "letters, ,\" \",#" + , "a,b,c" + , "// letters omitted, ,\" \",//" // these comments are not preserved + , "x,y,z" + ); + fx.tst_Tbls("letters"); + fx.tst_Dat(0, fx.ary_("a", "b", "c"), fx.ary_("x", "y", "z")); + } +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_hdr_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_hdr_tst.java new file mode 100644 index 000000000..b97471cc2 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_hdr_tst.java @@ -0,0 +1,82 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataRdr_dsv_hdr_tst { + @Before public void setup() {fx.Clear();} DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void Names() { + fx.run_parse_lines_ + ( "id,name, ,\" \",@" + , "0,me" + , "1,you" + ); + fx.tst_Flds(0, GfoFldList_.str_("id", "name")); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Dat(0 + , fx.ary_("0", "me") + , fx.ary_("1", "you") + ); + } + @Test public void Types() { + fx.run_parse_lines_ + ( "int," + StringClassXtn.Key_const + ", ,\" \",$" + , "0,me" + , "1,you" + ); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", IntClassXtn._).Add("fld1", StringClassXtn._)); + fx.tst_Dat(0 + , fx.ary_(0, "me") + , fx.ary_(1, "you") + ); + } + @Test public void NamesAndTypes() { + fx.run_parse_lines_ + ( "id,name, ,\" \",@" + , "int," + StringClassXtn.Key_const + ", ,\" \",$" + , "0,me" + , "1,you" + ); + fx.tst_Flds(0, GfoFldList_.new_().Add("id", IntClassXtn._).Add("name", StringClassXtn._)); + fx.tst_Dat(0 + , fx.ary_(0, "me") + , fx.ary_(1, "you") + ); + } + @Test public void MultipleTables_NoData() { + fx.run_parse_lines_ + ( "persons, ,\" \",#" + , "id,name, ,\" \",@" + , "things, ,\" \",#" + , "id,data, ,\" \",@" + ); + fx.tst_Tbls("persons", "things"); + fx.tst_Flds(0, GfoFldList_.str_("id", "name")); + fx.tst_Flds(1, GfoFldList_.str_("id", "data")); + fx.tst_Dat(0); + fx.tst_Dat(1); + } + @Test public void Comment() { + fx.run_parse_lines_ + ( "--------------------, ,\" \",//" + , "tbl0, ,\" \",#" + , "a0,a1" + ); + fx.tst_Tbls("tbl0"); + fx.tst_Dat(0, fx.ary_("a0", "a1")); + } +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_misc_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_misc_tst.java new file mode 100644 index 000000000..680827565 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_dsv_misc_tst.java @@ -0,0 +1,76 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataRdr_dsv_misc_tst { + @Before public void setup() {fx.Clear();} DsvDataRdr_fxt fx = DsvDataRdr_fxt.new_(); + @Test public void CmdDlm_NearMatches() { + fx.run_parse_("a, ,b"); + fx.tst_DatCsv(fx.ary_("a", " ", "b")); + fx.Clear(); + + fx.run_parse_("a,\" \",b"); + fx.tst_DatCsv(fx.ary_("a", " ", "b")); + fx.Clear(); + + fx.run_parse_("a, ,b,\" \",c"); + fx.tst_DatCsv(fx.ary_("a", " ", "b", " ", "c")); + fx.Clear(); + } + @Test public void CmdDlm_DoNotSpanLines() { + fx.run_parse_lines_ + ( "a, " + , "\" \",b" + ); + fx.tst_DatCsv + ( fx.ary_("a", " ") + , fx.ary_(" ", "b") + ); + } + @Test public void CmdDlm_SecondFldMustBeQuoted() { + fx.run_parse_lines_("a, , ,b"); // will fail with "invalid command: b", if second , , is interpreted as command delimiter + fx.tst_DatCsv(fx.ary_("a", " ", " ", "b")); + } + @Test public void Null_Int() { + fx.run_parse_ // not using run_parse_lines_ b/c (a) will have extra lineBreak; (b) test will look funny; + ( "int," + StringClassXtn.Key_const + ", ,\" \",$", String_.CrLf + , ",val1" + ); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", IntClassXtn._).Add("fld1", StringClassXtn._)); + fx.tst_Dat(0, fx.ary_(null, "val1")); + } + @Test public void Null_String() { + fx.run_parse_ // not using run_parse_lines_ b/c (a) will have extra lineBreak; (b) test will look funny; + ( StringClassXtn.Key_const + "," + StringClassXtn.Key_const + ", ,\" \",$", String_.CrLf + , ",val1" + ); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", StringClassXtn._).Add("fld1", StringClassXtn._)); + fx.tst_Dat(0, fx.ary_(null, "val1")); + } + @Test public void EmptyString() { + fx.run_parse_ // not using run_parse_lines_ b/c (a) will have extra lineBreak; (b) test will look funny; + ( StringClassXtn.Key_const + "," + StringClassXtn.Key_const + ", ,\" \",$", String_.CrLf + , "\"\",val1" + ); + fx.tst_Tbls(DsvTblBldr.NullTblName); + fx.tst_Flds(0, GfoFldList_.new_().Add("fld0", StringClassXtn._).Add("fld1", StringClassXtn._)); + fx.tst_Dat(0, fx.ary_("", "val1")); + } +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_layout_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_layout_tst.java new file mode 100644 index 000000000..8e5353eb5 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataRdr_layout_tst.java @@ -0,0 +1,131 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataRdr_layout_tst { + @Test public void TableName() { + run_parse_lines("table0, ,\" \",#"); + tst_Layout(0, DsvHeaderItm.Id_TableName); + } + @Test public void Comment() { + run_parse_lines("-------------, ,\" \",//", "data"); // need dataLine or parser will throw away standalone header + tst_Layout(0, DsvHeaderItm.Id_Comment); + } + @Test public void BlankLine() { + run_parse_lines("", "data"); // need dataLine or parser will throw away standalone header + tst_Layout(0, DsvHeaderItm.Id_BlankLine); + } + @Test public void LeafNames() { + run_parse_lines("id,name, ,\" \",@"); + tst_Layout(0, DsvHeaderItm.Id_LeafNames); + } + @Test public void LeafTypes() { + run_parse_lines("int," + StringClassXtn.Key_const + ", ,\" \",$"); + tst_Layout(0, DsvHeaderItm.Id_LeafTypes); + } + @Test public void Combined() { + run_parse_lines + ( "" + , "-------------, ,\" \",//" + , "table0, ,\" \",#" + , "int," + StringClassXtn.Key_const + ", ,\" \",$" + , "id,name, ,\" \",@" + , "-------------, ,\" \",//" + , "0,me" + ); + tst_Layout(0 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_LeafTypes + , DsvHeaderItm.Id_LeafNames + , DsvHeaderItm.Id_Comment + ); + } + @Test public void Tbl_N() { + run_parse_lines + ( "" + , "*************, ,\" \",//" + , "table0, ,\" \",#" + , "-------------, ,\" \",//" + , "0,me" + , "" + , "*************, ,\" \",//" + , "table1, ,\" \",#" + , " extended data, ,\" \",//" + , "-------------, ,\" \",//" + , "1,you,more" + ); + tst_Layout(0 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + ); + tst_Layout(1 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_Comment + ); + } + @Test public void Tbl_N_FirstIsEmpty() { + run_parse_lines + ( "" + , "*************, ,\" \",//" + , "table0, ,\" \",#" + , "-------------, ,\" \",//" + , "" + , "" + , "*************, ,\" \",//" + , "table1, ,\" \",#" + , " extended data, ,\" \",//" + , "-------------, ,\" \",//" + , "1,you,more" + ); + tst_Layout(0 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + ); + tst_Layout(1 + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_BlankLine + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_TableName + , DsvHeaderItm.Id_Comment + , DsvHeaderItm.Id_Comment + ); + } + void run_parse_lines(String... ary) { + String raw = String_.Concat_lines_crlf(ary); + DsvParser parser = DsvParser.dsv_(); + root = parser.ParseAsNde(raw); + } + void tst_Layout(int subIdx, int... expd) { + GfoNde tbl = root.Subs().FetchAt_asGfoNde(subIdx); + DsvStoreLayout layout = (DsvStoreLayout)tbl.EnvVars().Fetch(DsvStoreLayout.Key_const); + int[] actl = new int[layout.HeaderList().Count()]; + for (int i = 0; i < actl.length; i++) + actl[i] = layout.HeaderList().FetchAt(i).Id(); + Tfds.Eq_ary(expd, actl); + } + GfoNde root; +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr.java new file mode 100644 index 000000000..b4b2e7d8d --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr.java @@ -0,0 +1,114 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +public class DsvDataWtr extends DataWtr_base implements DataWtr { + public void InitWtr(String key, Object val) { + if (key == DsvStoreLayout.Key_const) layout = (DsvStoreLayout)val; + } + @Override public void WriteData(String name, Object val) {sb.WriteFld(val == null ? null : val.toString());} + public void WriteLeafBgn(String leafName) {} + public void WriteLeafEnd() {sb.WriteRowSep();} + @Override public void WriteNodeBgn(String name) {WriteTableBgn(name, GfoFldList_.Null);} + public void WriteTableBgn(String name, GfoFldList flds) { + for (int i = 0; i < layout.HeaderList().Count(); i++) { + DsvHeaderItm data = layout.HeaderList().FetchAt(i); + int id = data.Id(); + if (id == DsvHeaderItm.Id_TableName) WriteTableName(name); + else if (id == DsvHeaderItm.Id_LeafNames) WriteMeta(flds, true, sym.FldNamesSym()); + else if (id == DsvHeaderItm.Id_LeafTypes) WriteMeta(flds, false, sym.FldTypesSym()); + else if (id == DsvHeaderItm.Id_BlankLine) sb.WriteRowSep(); + else if (id == DsvHeaderItm.Id_Comment) WriteComment(data.Val().toString()); + } + } + @Override public void WriteNodeEnd() {} + public void Clear() {sb.Clear();} + public String XtoStr() {return sb.XtoStr();} + void WriteTableName(String tableName) { + sb.WriteFld(tableName); + sb.WriteCmd(sym.TblNameSym()); + sb.WriteRowSep(); + } + void WriteMeta(GfoFldList flds, boolean isName, String cmd) { + for (int i = 0; i < flds.Count(); i++) { + GfoFld fld = flds.FetchAt(i); + String val = isName ? fld.Key(): fld.Type().Key(); + sb.WriteFld(val); + } + if (layout.WriteCmdSequence()) sb.WriteCmd(cmd); + sb.WriteRowSep(); + } + void WriteComment(String comment) { + sb.WriteFld(comment); + sb.WriteCmd(sym.CommentSym()); + sb.WriteRowSep(); + } + @Override public SrlMgr SrlMgr_new(Object o) {return new DsvDataWtr();} + DsvStringBldr sb; DsvSymbols sym = DsvSymbols.default_(); DsvStoreLayout layout = DsvStoreLayout.csv_dat_(); + @gplx.Internal protected DsvDataWtr() {sb = DsvStringBldr.new_(sym);} +} +class DsvStringBldr { + public void Clear() {sb.Clear();} + public String XtoStr() {return sb.XtoStr();} + public void WriteCmd(String cmd) { + WriteFld(sym.CmdSequence(), true); + WriteFld(cmd); + } + public void WriteFldSep() {sb.Add(sym.FldSep());} + public void WriteRowSep() { + sb.Add(sym.RowSep()); + isNewRow = true; + } + public void WriteFld(String val) {WriteFld(val, false);} + void WriteFld(String val, boolean writeRaw) { + if (isNewRow) // if isNewRow, then fld is first, and no fldSpr needed (RowSep serves as fldSpr) + isNewRow = false; + else + sb.Add(sym.FldSep()); + + if (val == null) {} // null -> append nothing + else if (String_.Eq(val, String_.Empty))// "" -> append "" + sb.Add("\"\""); + else if (writeRaw) // only cmds should be writeRaw (will append ," ") + sb.Add(val); + else { // escape as necessary; ex: "the quote "" char"; "the comma , char" + boolean quoteField = false; + if (String_.Has(val, sym.QteDlm())) { + val = String_.Replace(val, "\"", "\"\""); + quoteField = true; + } + else if (String_.Has(val, sym.FldSep())) + quoteField = true; + else if (sym.RowSepIsNewLine() + && (String_.Has(val, "\n") || String_.Has(val, "\r"))) + quoteField = true; + else if (String_.Has(val, sym.RowSep())) + quoteField = true; + + if (quoteField) sb.Add(sym.QteDlm()); + sb.Add(val); + if (quoteField) sb.Add(sym.QteDlm()); + } + } + + String_bldr sb = String_bldr_.new_(); DsvSymbols sym; boolean isNewRow = true; + public static DsvStringBldr new_(DsvSymbols sym) { + DsvStringBldr rv = new DsvStringBldr(); + rv.sym = sym; + return rv; + } DsvStringBldr() {} +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_.java new file mode 100644 index 000000000..f13b4b8f3 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_.java @@ -0,0 +1,31 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +public class DsvDataWtr_ { + public static DsvDataWtr csv_hdr_() { + DsvDataWtr rv = new DsvDataWtr(); + rv.InitWtr(DsvStoreLayout.Key_const, DsvStoreLayout.csv_hdr_()); + return rv; + } + public static DsvDataWtr csv_dat_() { + DsvDataWtr rv = new DsvDataWtr(); + rv.InitWtr(DsvStoreLayout.Key_const, DsvStoreLayout.csv_dat_()); + return rv; + } + public static DsvDataWtr new_() {return new DsvDataWtr();} +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_csv_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_csv_tst.java new file mode 100644 index 000000000..1954777e6 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_csv_tst.java @@ -0,0 +1,100 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataWtr_csv_tst { + @Test public void Dat_Val_0() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root); + expd = String_.Concat_lines_crlf(""); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Val_1() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a"); + expd = String_.Concat_lines_crlf("a"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Val_N() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", "b", "c"); + expd = String_.Concat_lines_crlf("a,b,c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Row_N() { + root = fx_nde.csv_dat_(); + this.AddCsvRow(root, "a", "b", "c"); + this.AddCsvRow(root, "d", "e", "f"); + expd = String_.Concat_lines_crlf + ( "a,b,c" + , "d,e,f" + ); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Escape_FldSpr() { // , + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", ",", "c"); + expd = String_.Concat_lines_crlf("a,\",\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Escape_RcdSpr() { // NewLine + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", String_.CrLf, "c"); + expd = String_.Concat_lines_crlf("a,\"" + String_.CrLf + "\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Escape_Quote() { // " -> "" + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", "\"", "c"); + expd = String_.Concat_lines_crlf("a,\"\"\"\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Whitespace() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", " b\t", "c"); + expd = String_.Concat_lines_crlf("a, b\t,c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_Null() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", null, "c"); + expd = String_.Concat_lines_crlf("a,,c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Dat_EmptyString() { + root = fx_nde.csv_dat_(); this.AddCsvRow(root, "a", "", "c"); + expd = String_.Concat_lines_crlf("a,\"\",c"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Hdr_Flds() { + wtr = DsvDataWtr_.csv_hdr_(); + GfoFldList flds = GfoFldList_.new_().Add("id", StringClassXtn._).Add("name", StringClassXtn._); + root = fx_nde.csv_hdr_(flds); this.AddCsvRow(root, "0", "me"); + expd = String_.Concat_lines_crlf + ( "id,name" + , "0,me" + ); + fx.tst_XtoStr(wtr, root, expd); + } + void AddCsvRow(GfoNde root, String... ary) { + GfoNde sub = GfoNde_.vals_(root.SubFlds(), ary); + root.Subs().Add(sub); + } + GfoNde root; String expd; DsvDataWtr wtr = DsvDataWtr_.csv_dat_(); DsvDataWtr_fxt fx = DsvDataWtr_fxt.new_(); GfoNdeFxt fx_nde = GfoNdeFxt.new_(); +} +class DsvDataWtr_fxt { + public void tst_XtoStr(DsvDataWtr wtr, GfoNde root, String expd) { + wtr.Clear(); + root.XtoStr_wtr(wtr); + String actl = wtr.XtoStr(); + Tfds.Eq(expd, actl); + } + public static DsvDataWtr_fxt new_() {return new DsvDataWtr_fxt();} DsvDataWtr_fxt() {} +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_tbls_tst.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_tbls_tst.java new file mode 100644 index 000000000..981de747a --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvDataWtr_tbls_tst.java @@ -0,0 +1,73 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class DsvDataWtr_tbls_tst { + @Before public void setup() { + DsvStoreLayout layout = DsvStoreLayout.dsv_brief_(); + layout.HeaderList().Add_TableName(); + wtr.InitWtr(DsvStoreLayout.Key_const, layout); + } + @Test public void Rows_0() { + root = fx_nde.tbl_("tbl0"); + expd = String_.Concat_lines_crlf( "tbl0, ,\" \",#"); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Rows_N() { + root = fx_nde.tbl_ + ( "numbers" + , fx_nde.row_vals_(1, 2, 3) + , fx_nde.row_vals_(4, 5, 6) + ); + expd = String_.Concat_lines_crlf + ( "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Tbls_N_Empty() { + root = fx_nde.root_ + ( fx_nde.tbl_("tbl0") + , fx_nde.tbl_("tbl1") + ); + expd = String_.Concat_lines_crlf + ( "tbl0, ,\" \",#" + , "tbl1, ,\" \",#" + ); + fx.tst_XtoStr(wtr, root, expd); + } + @Test public void Tbls_N() { + root = fx_nde.root_ + ( fx_nde.tbl_("letters" + , fx_nde.row_vals_("a", "b", "c")) + , fx_nde.tbl_("numbers" + , fx_nde.row_vals_(1, 2, 3) + , fx_nde.row_vals_(4, 5, 6) + )); + expd = String_.Concat_lines_crlf + ( "letters, ,\" \",#" + , "a,b,c" + , "numbers, ,\" \",#" + , "1,2,3" + , "4,5,6" + ); + fx.tst_XtoStr(wtr, root, expd); + } + GfoNde root; String expd; DsvDataWtr wtr = DsvDataWtr_.csv_dat_(); DsvDataWtr_fxt fx = DsvDataWtr_fxt.new_(); GfoNdeFxt fx_nde = GfoNdeFxt.new_(); +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvHeaderList.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvHeaderList.java new file mode 100644 index 000000000..9448c7677 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvHeaderList.java @@ -0,0 +1,44 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +public class DsvHeaderList { + @gplx.Internal protected int Count() {return list.Count();} + @gplx.Internal protected DsvHeaderItm FetchAt(int i) {return (DsvHeaderItm)list.FetchAt(i);} + public DsvHeaderList Add_LeafTypes() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_LeafTypes, null)); return this;} + public DsvHeaderList Add_LeafNames() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_LeafNames, null)); return this;} + public DsvHeaderList Add_TableName() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_TableName, null)); return this;} + public DsvHeaderList Add_BlankLine() {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_BlankLine, null)); return this;} + public DsvHeaderList Add_Comment(String comment) {this.Add(new DsvHeaderItm(DsvHeaderItm.Id_Comment, comment)); return this;} + void Add(DsvHeaderItm data) {list.Add(data);} + + ListAdp list = ListAdp_.new_(); + public static DsvHeaderList new_() {return new DsvHeaderList();} DsvHeaderList() {} +} +class DsvHeaderItm { + public int Id() {return id;} int id; + public Object Val() {return val;} Object val; + @gplx.Internal protected DsvHeaderItm(int id, Object val) {this.id = id; this.val = val;} + + public static final int + Id_Comment = 1 + , Id_TableName = 2 + , Id_BlankLine = 3 + , Id_LeafTypes = 4 + , Id_LeafNames = 5 + ; +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvStoreLayout.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvStoreLayout.java new file mode 100644 index 000000000..495d52e17 --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvStoreLayout.java @@ -0,0 +1,51 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +public class DsvStoreLayout { + public DsvHeaderList HeaderList() {return headerList;} DsvHeaderList headerList = DsvHeaderList.new_(); + @gplx.Internal protected boolean WriteCmdSequence() {return writeCmdSequence;} @gplx.Internal protected DsvStoreLayout WriteCmdSequence_(boolean val) {writeCmdSequence = val; return this;} private boolean writeCmdSequence; + + static DsvStoreLayout new_() {return new DsvStoreLayout();} + public static DsvStoreLayout csv_dat_() {return new_();} + public static DsvStoreLayout csv_hdr_() { + DsvStoreLayout rv = new_(); + rv.HeaderList().Add_LeafNames(); + return rv; + } + public static DsvStoreLayout dsv_brief_() { + DsvStoreLayout rv = new_(); + rv.writeCmdSequence = true; + return rv; + } + public static DsvStoreLayout dsv_full_() { + DsvStoreLayout rv = DsvStoreLayout.new_(); + rv.writeCmdSequence = true; + rv.HeaderList().Add_BlankLine(); + rv.HeaderList().Add_BlankLine(); + rv.HeaderList().Add_Comment("================================"); + rv.HeaderList().Add_TableName(); + rv.HeaderList().Add_Comment("================================"); + rv.HeaderList().Add_LeafTypes(); + rv.HeaderList().Add_LeafNames(); + rv.HeaderList().Add_Comment("================================"); + return rv; + } + public static DsvStoreLayout as_(Object obj) {return obj instanceof DsvStoreLayout ? (DsvStoreLayout)obj : null;} + public static DsvStoreLayout cast_(Object obj) {try {return (DsvStoreLayout)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, DsvStoreLayout.class, obj);}} + public static final String Key_const = "StoreLayoutWtr"; +} diff --git a/100_core/src_340_dsv/gplx/stores/dsvs/DsvSymbols.java b/100_core/src_340_dsv/gplx/stores/dsvs/DsvSymbols.java new file mode 100644 index 000000000..2e2e5f9ed --- /dev/null +++ b/100_core/src_340_dsv/gplx/stores/dsvs/DsvSymbols.java @@ -0,0 +1,52 @@ +/* +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.stores.dsvs; import gplx.*; import gplx.stores.*; +class DsvSymbols { + public String FldSep() {return fldSep;} public DsvSymbols FldSep_(String v) {fldSep = v; CmdSequence_set(); return this;} private String fldSep; + public String RowSep() {return rowSep;} + public DsvSymbols RowSep_(String v) { + rowSep = v; + rowSepIsNewLine = String_.Has(v, "\n") || String_.Has(v, "\r"); + return this; + } String rowSep; + public String QteDlm() {return qteDlm;} public void QteDlm_set(String v) {qteDlm = v; CmdSequence_set();} private String qteDlm; + public String CmdDlm() {return cmdDlm;} public void CmdDlm_set(String v) {cmdDlm = v; CmdSequence_set();} private String cmdDlm; + public String CmdSequence() {return cmdSequence;} private String cmdSequence; + public String CommentSym() {return commentSym;} public void CommentSym_set(String v) {commentSym = v;} private String commentSym; + public String TblNameSym() {return tblNameSym;} public void TblNamesSym_set(String v) {tblNameSym = v;} private String tblNameSym; + public String FldNamesSym() {return fldNamesSym;} public void FldNamesSym_set(String v) {fldNamesSym = v;} private String fldNamesSym; + public String FldTypesSym() {return fldTypesSym;} public void FldTypesSym_set(String v) {fldTypesSym = v;} private String fldTypesSym; + public boolean RowSepIsNewLine() {return rowSepIsNewLine;} private boolean rowSepIsNewLine; + public void Reset() { + fldSep = ","; + RowSep_ ("\r\n"); + + qteDlm = "\""; + cmdDlm = " "; + CmdSequence_set(); + + commentSym = "//"; + tblNameSym = "#"; + fldNamesSym = "@"; + fldTypesSym = "$"; + } + void CmdSequence_set() { // commandDelimiters are repeated; once without quotes and once with quotes; ex: , ," ", + cmdSequence = String_.Concat(cmdDlm, fldSep, qteDlm, cmdDlm, qteDlm); + } + public static DsvSymbols default_() {return new DsvSymbols();} DsvSymbols() {this.Reset();} +} diff --git a/100_core/src_400_gfs/gplx/GfsCore.java b/100_core/src_400_gfs/gplx/GfsCore.java new file mode 100644 index 000000000..a7c301314 --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsCore.java @@ -0,0 +1,167 @@ +/* +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; +public class GfsCore implements GfoInvkAble { + public GfoInvkAble Root() {return root;} + @gplx.Internal protected GfsRegy Root_as_regy() {return root;} GfsRegy root = GfsRegy.new_(); + public void Clear() {root.Clear();} + public GfoMsgParser MsgParser() {return msgParser;} public GfsCore MsgParser_(GfoMsgParser v) {msgParser = v; return this;} GfoMsgParser msgParser; + public void Del(String key) {root.Del(key);} + public void AddLib(GfsLibIni... ary) {for (GfsLibIni itm : ary) itm.Ini(this);} + public void AddCmd(GfoInvkAble invk, String key) {root.AddCmd(invk, key);} + public void AddObj(GfoInvkAble invk, String key) {root.AddObj(invk, key);} + public void AddDeep(GfoInvkAble invk, String... ary) { + GfoInvkCmdMgrOwner cur = (GfoInvkCmdMgrOwner)((GfsRegyItm)root.Fetch(ary[0])).InvkAble(); + for (int i = 1; i < ary.length - 1; i++) + cur = (GfoInvkCmdMgrOwner)cur.InvkMgr().Invk(GfsCtx._, 0, ary[i], GfoMsg_.Null, cur); + cur.InvkMgr().Add_cmd(ary[ary.length - 1], invk); + } + public String FetchKey(GfoInvkAble invk) {return root.FetchByType(invk).Key();} + public Object ExecOne(GfsCtx ctx, GfoMsg msg) {return GfsCore_.Exec(ctx, root, msg, null, 0);} + public Object ExecOne_to(GfsCtx ctx, GfoInvkAble invk, GfoMsg msg) {return GfsCore_.Exec(ctx, invk, msg, null, 0);} + public Object ExecMany(GfsCtx ctx, GfoMsg rootMsg) { + Object rv = null; + for (int i = 0; i < rootMsg.Subs_count(); i++) { + GfoMsg subMsg = (GfoMsg)rootMsg.Subs_getAt(i); + rv = GfsCore_.Exec(ctx, root, subMsg, null, 0); + } + return rv; + } + public void ExecRegy(String key) { + GfoRegyItm itm = GfoRegy._.FetchOrNull(key); + if (itm == null) {UsrDlg_._.Warn(UsrMsg.new_("could not find script for key").Add("key", key)); return;} + Io_url url = itm.Url(); + if (!Io_mgr._.ExistsFil(url)) { + UsrDlg_._.Warn(UsrMsg.new_("script url does not exist").Add("key", key).Add("url", url)); + return; + } + this.ExecText(Io_mgr._.LoadFilStr(url)); + } + public Object ExecFile_ignoreMissing(Io_url url) {if (!Io_mgr._.ExistsFil(url)) return null; return ExecText(Io_mgr._.LoadFilStr(url));} + public Object ExecFile(Io_url url) {return ExecText(Io_mgr._.LoadFilStr(url));} + public Object ExecFile_ignoreMissing(GfoInvkAble root, Io_url url) { + if (!Io_mgr._.ExistsFil(url)) return null; + if (msgParser == null) throw Err_.new_("msgParser is null"); + return Exec_bry(Io_mgr._.LoadFilBry(url), root); + } + public Object Exec_bry(byte[] bry) {return Exec_bry(bry, root);} + public Object Exec_bry(byte[] bry, GfoInvkAble root) { + GfoMsg rootMsg = msgParser.ParseToMsg(String_.new_utf8_(bry)); + Object rv = null; + GfsCtx ctx = GfsCtx.new_(); + for (int i = 0; i < rootMsg.Subs_count(); i++) { + GfoMsg subMsg = (GfoMsg)rootMsg.Subs_getAt(i); + rv = GfsCore_.Exec(ctx, root, subMsg, null, 0); + } + return rv; + } + public Object ExecText(String text) { + if (msgParser == null) throw Err_.new_("msgParser is null"); + GfsCtx ctx = GfsCtx.new_(); + return ExecMany(ctx, msgParser.ParseToMsg(text)); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_ExecFil)) { + Io_url url = m.ReadIoUrl("url"); + if (ctx.Deny()) return this; + return ExecFile(url); + } + else return GfoInvkAble_.Rv_unhandled; +// return this; + } public static final String Invk_ExecFil = "ExecFil"; + public static final GfsCore _ = new GfsCore(); + @gplx.Internal protected static GfsCore new_() {return new GfsCore();} +} +class GfsCore_ { + public static final String Arg_primitive = "v"; + public static Object Exec(GfsCtx ctx, GfoInvkAble ownerInvk, GfoMsg ownerMsg, Object ownerPrimitive, int depth) { + if (ownerMsg.Args_count() == 0 && ownerMsg.Subs_count() == 0 && String_.Eq(ownerMsg.Key(), "")) {UsrDlg_._.Warn("empty msg"); return GfoInvkAble_.Rv_unhandled;} + if (ownerPrimitive != null) ownerMsg.Parse_(false).Add(GfsCore_.Arg_primitive, ownerPrimitive); + Object rv = ownerInvk.Invk(ctx, 0, ownerMsg.Key(), ownerMsg); + if (rv == GfoInvkAble_.Rv_cancel) return rv; + else if (rv == GfoInvkAble_.Rv_unhandled) { + if (ctx.Fail_if_unhandled()) + throw Err_.new_("Object does not support key").Add("key", ownerMsg.Key()).Add("ownerType", ClassAdp_.FullNameOf_obj(ownerInvk)); + else { + Gfo_usr_dlg usr_dlg = ctx.Usr_dlg(); + if (usr_dlg != null) usr_dlg.Warn_many(GRP_KEY, "unhandled_key", "Object does not support key: key=~{0} ownerType=~{1}", ownerMsg.Key(), ClassAdp_.FullNameOf_obj(ownerInvk)); + return GfoInvkAble_.Null; + } + } + if (ownerMsg.Subs_count() == 0) { // msg is leaf + GfsRegyItm regyItm = GfsRegyItm.as_(rv); + if (regyItm == null) return rv; // rv is primitive or other non-regy Object + if (regyItm.IsCmd()) // rv is cmd; invk cmd + return regyItm.InvkAble().Invk(ctx, 0, ownerMsg.Key(), ownerMsg); + else // rv is host + return regyItm.InvkAble(); + } + else { // intermediate; cast to invk and call Exec + GfoInvkAble invk = GfoInvkAble_.as_(rv); + Object primitive = null; + if (invk == null) { // rv is primitive; find appropriate mgr + Class type = rv.getClass(); + if (type == String.class) invk = String_.Gfs; + else if (Int_.TypeMatch(type)) invk = Int_.Gfs; + else if (Bool_.TypeMatch(type)) invk = Bool_.Gfs; + else throw Err_.new_("unknown primitive").Add("type", ClassAdp_.NameOf_type(type)).Add("obj", Object_.XtoStr_OrNullStr(rv)); + primitive = rv; + } + Object exec_rv = null; + int len = ownerMsg.Subs_count(); + for (int i = 0; i < len; i++) // iterate over subs; needed for a{b;c;d;} + exec_rv = Exec(ctx, invk, ownerMsg.Subs_getAt(i), primitive, depth + 1); + return exec_rv; + } + } + static final String GRP_KEY = "gplx.gfs_core"; +} +// class GfsRegyMgr : GfoInvkAble { +// public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { +// if (ctx.Match(k, Invk_Add)) { +// String libKey = m.ReadStr("libKey"), regKey = m.ReadStr("regKey"); +// if (ctx.Deny()) return this; +// GfsRegyItm itm = regy.Fetch(libKey); +// if (regy.Has(regKey)) {ctx.Write_warn("'{0}' already exists", regKey); return this;} +// regy.Add(regKey, itm.InvkAble(), itm.Type_cmd()); +// ctx.Write_note("added '{0}' as '{1}'", regKey, libKey); +// } +// else if (ctx.Match(k, Invk_Del)) { +// String regKey = m.ReadStr("regKey"); +// if (ctx.Deny()) return this; +// if (!regy.Has(regKey)) {ctx.Write_warn("{0} does not exist", regKey); return this;} +// regy.Del(regKey); +// ctx.Write_note("removed '{0}'", regKey); +// } +// else if (ctx.Match(k, Invk_Load)) { +// Io_url url = (Io_url)m.ReadObj("url", Io_url_.Parser); +// if (ctx.Deny()) return this; +// String loadText = Io_mgr._.LoadFilStr(url); +// GfoMsg loadMsg = core.MsgParser().ParseToMsg(loadText); +// return core.Exec(ctx, loadMsg); +// } +// else return GfoInvkAble_.Rv_unhandled; +// return this; +// } public static final String Invk_Add = "Add", Invk_Del = "Del", Invk_Load = "Load"; +// GfsCore core; GfsRegy regy; +// public static GfsRegyMgr new_(GfsCore core, GfsRegy regy) { +// GfsRegyMgr rv = new GfsRegyMgr(); +// rv.core = core; rv.regy = regy; +// return rv; +// } GfsRegyMgr() {} +// } diff --git a/100_core/src_400_gfs/gplx/GfsCoreHelp.java b/100_core/src_400_gfs/gplx/GfsCoreHelp.java new file mode 100644 index 000000000..668d89e3d --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsCoreHelp.java @@ -0,0 +1,72 @@ +/* +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; +class GfsCoreHelp implements GfoInvkAble { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + String path = m.ReadStrOr("path", ""); + if (String_.Eq(path, "")) { + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < core.Root_as_regy().Count(); i++) { + GfsRegyItm itm = (GfsRegyItm)core.Root_as_regy().FetchAt(i); + sb.Add_spr_unless_first(itm.Key(), String_.CrLf, i); + } + return sb.XtoStr(); + } + else return Exec(ctx, core.Root_as_regy(), path); + } + public static Err Err_Unhandled(String objPath, String key) {return Err_.new_("obj does not handle msgKey").Add("objPath", objPath).Add("key", key);} + static Err Err_Unhandled(String[] itmAry, int i) { + String_bldr sb = String_bldr_.new_(); + for (int j = 0; j < i; j++) + sb.Add_spr_unless_first(itmAry[j], ".", j); + return Err_Unhandled(sb.XtoStr(), itmAry[i]); + } + static Object Exec(GfsCtx rootCtx, GfoInvkAble rootInvk, String path) { + String[] itmAry = String_.Split(path, "."); + GfoInvkAble invk = rootInvk; + GfsCtx ctx = GfsCtx.new_(); + Object curRv = null; + for (int i = 0; i < itmAry.length; i++) { + String itm = itmAry[i]; + curRv = invk.Invk(ctx, 0, itm, GfoMsg_.Null); + if (curRv == GfoInvkAble_.Rv_unhandled) throw Err_Unhandled(itmAry, i); + invk = GfoInvkAble_.as_(curRv); + } + GfsCoreHelp helpData = GfsCoreHelp.as_(curRv); + if (helpData != null) { // last itm is actually Method + return ""; + } + else { + ctx = GfsCtx.new_().Help_browseMode_(true); + invk.Invk(ctx, 0, "", GfoMsg_.Null); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < ctx.Help_browseList().Count(); i++) { + String s = (String)ctx.Help_browseList().FetchAt(i); + sb.Add_spr_unless_first(s, String_.CrLf, i); + } + return sb.XtoStr(); + } + } + public static GfsCoreHelp as_(Object obj) {return obj instanceof GfsCoreHelp ? (GfsCoreHelp)obj : null;} + public static GfsCoreHelp new_(GfsCore core) { + GfsCoreHelp rv = new GfsCoreHelp(); + rv.core = core; + return rv; + } GfsCoreHelp() {} + GfsCore core; +} diff --git a/100_core/src_400_gfs/gplx/GfsCore_tst.java b/100_core/src_400_gfs/gplx/GfsCore_tst.java new file mode 100644 index 000000000..7a1316bef --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsCore_tst.java @@ -0,0 +1,113 @@ +/* +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; +import org.junit.*; +public class GfsCore_tst { + @Before public void setup() { + core = GfsCore.new_(); + core.AddObj(String_.Gfs, "String_"); + core.AddObj(Int_.Gfs, "Int_"); + } GfsCore core; + @Test public void Basic() { // String_.Len('abc') >> 3 + tst_Msg + ( msg_("String_").Subs_ + ( msg_("Len").Add("v", "abc")) + , 3); + } + @Test public void PrimitiveConversion() { // String_.Len('abc').Add(-3) >> 0 + tst_Msg + ( msg_("String_").Subs_ + ( msg_("Len").Add("v", "abc").Subs_ + ( msg_("Add").Add("operand", -3)) + ) + , 0); + } +// @Test public void Fail_notFound() { // String_.DoesNotExists +// tst_Err +// ( msg_("help").Add("", "String_.DoesNotExist") +// , GfsHelp.Err_Unhandled("String_", "DoesNotExist")); +// } + @Test public void Cmd() { // cmd + core.AddCmd(new GfsTest_cmd(), "testCmd"); + tst_Msg + ( msg_("testCmd").Add("s", "pass") + , "pass"); + } + @Test public void EmptyMsg() { + tst_Msg + ( msg_("") + , GfoInvkAble_.Rv_unhandled); + } +// @Test public void Fail_argMissing() { // String_.Len() +// tst_String__Len_Err(msg_("Len"), GfsCtx.Err_KeyNotFound("v", "<>")); +// } +// @Test public void Fail_argWrongKey() { // String_.Len(badKey='abc') +// tst_String__Len_Err(msg_("Len").Add("badKey", "abc"), GfsCtx.Err_KeyNotFound("v", "badKey;")); +// } +// @Test public void Fail_argExtraKey() { // String_.Len(v='abc' extraKey=1) +// tst_String__Len_Err(msg_("Len").Add("v", "abc").Add("extraKey", 1), GfsCtx.Err_KeyNotFound("v", "badKey;")); +// } + @Test public void Add_obj_deep() { // String_.Len(badKey='abc') + GfsCore_tst_nest obj1 = GfsCore_tst_nest.new_("1", "val1"); + GfsCore_tst_nest obj1_1 = GfsCore_tst_nest.new_("1_1", "val2"); + core.AddObj(obj1, "1"); + core.AddDeep(obj1_1, "1", "1_1"); + + GfoMsg root = GfoMsg_.root_("1", "1_1", GfsCore_tst_nest.Prop2); + Object actl = core.ExecOne(GfsCtx._, root); + Tfds.Eq("val2", actl); + } + void tst_String__Len_Err(GfoMsg m, Err expd) { + tst_Err(msg_("String_").Subs_(m), expd); + } + void tst_Err(GfoMsg msg, Err expd) { + GfoMsg root = msg; + GfsCtx ctx = GfsCtx.new_(); + try { + core.ExecOne(ctx, root); + Tfds.Fail_expdError(); + } + catch (Exception e) { + Tfds.Eq_err(expd, e); + } + } + GfoMsg msg_(String k) {return GfoMsg_.new_cast_(k);} + void tst_Msg(GfoMsg msg, Object expd) { + GfsCtx ctx = GfsCtx.new_(); + Object actl = core.ExecOne(ctx, msg); + Tfds.Eq(expd, actl); + } +} +class GfsCore_tst_nest implements GfoInvkAble, GfoInvkCmdMgrOwner { + public GfoInvkCmdMgr InvkMgr() {return invkMgr;} GfoInvkCmdMgr invkMgr = GfoInvkCmdMgr.new_(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Prop1)) {return prop1;} + else if (ctx.Match(k, Prop2)) {return prop2;} + else if (ctx.Match(k, prop1)) {return this;} + else return invkMgr.Invk(ctx, ikey, k, m, this); + } public static final String Prop1 = "Prop1", Prop2 = "Prop2"; + String prop1, prop2; + public static GfsCore_tst_nest new_(String prop1, String prop2) { + GfsCore_tst_nest rv = new GfsCore_tst_nest(); + rv.prop1 = prop1; rv.prop2 = prop2; + return rv; + } GfsCore_tst_nest() {} +} +class GfsTest_cmd implements GfoInvkAble { + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return m.ReadStr("s");} +} diff --git a/100_core/src_400_gfs/gplx/GfsCtx.java b/100_core/src_400_gfs/gplx/GfsCtx.java new file mode 100644 index 000000000..b3fedf647 --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsCtx.java @@ -0,0 +1,65 @@ +/* +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; +public class GfsCtx { + public OrderedHash Vars() {return vars;} OrderedHash vars = OrderedHash_.new_(); + public boolean Fail_if_unhandled() {return fail_if_unhandled;} public GfsCtx Fail_if_unhandled_(boolean v) {fail_if_unhandled = v; return this;} private boolean fail_if_unhandled; + public Gfo_usr_dlg Usr_dlg() {return usr_dlg;} public GfsCtx Usr_dlg_(Gfo_usr_dlg v) {usr_dlg = v; return this;} Gfo_usr_dlg usr_dlg; + public boolean Help_browseMode() {return help_browseMode;} public GfsCtx Help_browseMode_(boolean v) {help_browseMode = v; return this;} private boolean help_browseMode; + @gplx.Internal protected ListAdp Help_browseList() {return help_browseList;} ListAdp help_browseList = ListAdp_.new_(); + public Object MsgSrc() {return msgSrc;} public GfsCtx MsgSrc_(Object v) {msgSrc = v; return this;} Object msgSrc; + public boolean Match(String k, String match) { + if (help_browseMode) { + help_browseList.Add(match); + return false; + } + else + return String_.Eq(k, match); + } + public boolean MatchPriv(String k, String match) {return help_browseMode ? false : String_.Eq(k, match);} + public boolean MatchIn(String k, String... match) { + if (help_browseMode) { + for (String i : match) + help_browseList.Add(i); + return false; + } + return String_.In(k, match); + } + public boolean Write_note(String fmt, Object... ary) {UsrDlg_._.Note(fmt, ary); return false;} + public boolean Write_warn(String fmt, Object... ary) {UsrDlg_._.Note("! " + fmt, ary); return false;} + public boolean Write_stop(UsrMsg umsg) {UsrDlg_._.Note("* " + umsg.XtoStr()); return false;} + public boolean Write_stop(String fmt, Object... ary) {UsrDlg_._.Note("* " + fmt, ary); return false;} + public boolean Deny() {return deny;} private boolean deny; + public static final GfsCtx _ = new GfsCtx(); + public static GfsCtx new_() {return new GfsCtx();} GfsCtx() {} + public static GfsCtx rdr_() { + GfsCtx rv = new GfsCtx(); + rv.deny = true; + rv.mode = "read"; + return rv; + } + public static GfsCtx wtr_() { + GfsCtx rv = new GfsCtx(); + rv.deny = true; + rv.mode = Mode_write; + return rv; + } + public String Mode() {return mode;} public GfsCtx Mode_(String v) {mode = v; return this;} private String mode = "regular"; + public static final String Mode_write = "write"; + public static final int Ikey_null = -1; +} diff --git a/100_core/src_400_gfs/gplx/GfsLibIni.java b/100_core/src_400_gfs/gplx/GfsLibIni.java new file mode 100644 index 000000000..da359de29 --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsLibIni.java @@ -0,0 +1,21 @@ +/* +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; +public interface GfsLibIni { + void Ini(GfsCore core); +} diff --git a/100_core/src_400_gfs/gplx/GfsLibIni_core.java b/100_core/src_400_gfs/gplx/GfsLibIni_core.java new file mode 100644 index 000000000..2b1e241a7 --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsLibIni_core.java @@ -0,0 +1,35 @@ +/* +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; +public class GfsLibIni_core implements GfsLibIni { + public void Ini(GfsCore core) { + core.AddCmd(GfsCoreHelp.new_(core), "help"); + core.AddObj(String_.Gfs, "String_"); + core.AddObj(Int_.Gfs, "Int_"); + core.AddObj(DateAdp_.Gfs, "Date_"); + core.AddObj(RandomAdp_.Gfs, "RandomAdp_"); + core.AddObj(GfoTemplateFactory._, "factory"); + core.AddObj(GfoRegy._, "GfoRegy_"); + core.AddObj(GfsCore._, "GfsCore_"); + core.AddObj(gplx.ios.IoUrlInfoRegy._, "IoUrlInfoRegy_"); + core.AddObj(gplx.ios.IoUrlTypeRegy._, "IoUrlTypeRegy_"); + + GfoRegy._.Parsers().Add("Io_url", Io_url_.Parser); + } + public static final GfsLibIni_core _ = new GfsLibIni_core(); GfsLibIni_core() {} +} diff --git a/100_core/src_400_gfs/gplx/GfsRegy.java b/100_core/src_400_gfs/gplx/GfsRegy.java new file mode 100644 index 000000000..0391449dc --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsRegy.java @@ -0,0 +1,54 @@ +/* +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; +class GfsRegy implements GfoInvkAble { + public int Count() {return hash.Count();} + public void Clear() {hash.Clear(); typeHash.Clear();} + public boolean Has(String k) {return hash.Has(k);} + public GfsRegyItm FetchAt(int i) {return (GfsRegyItm)hash.FetchAt(i);} + public GfsRegyItm Fetch(String key) {return (GfsRegyItm)hash.Fetch(key);} + public GfsRegyItm FetchByType(GfoInvkAble invk) {return (GfsRegyItm)typeHash.Fetch(ClassAdp_.FullNameOf_obj(invk));} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + Object rv = (GfsRegyItm)hash.Fetch(k); if (rv == null) throw Err_.missing_key_(k); + return rv; + } + public void AddObj(GfoInvkAble invk, String key) {Add(key, invk, false);} + public void AddCmd(GfoInvkAble invk, String key) {Add(key, invk, true);} + public void Add(String key, GfoInvkAble invk, boolean typeCmd) { + if (hash.Has(key)) return; + GfsRegyItm regyItm = new GfsRegyItm().Key_(key).InvkAble_(invk).IsCmd_(typeCmd).TypeKey_(ClassAdp_.FullNameOf_obj(invk)); + hash.Add(key, regyItm); + typeHash.Add_if_new(regyItm.TypeKey(), regyItm); // NOTE: changed to allow same Object to be added under different aliases (app, xowa) DATE:2014-06-09; + } + public void Del(String k) { + GfsRegyItm itm =(GfsRegyItm)hash.Fetch(k); + if (itm != null) typeHash.Del(itm.TypeKey()); + hash.Del(k); + } + HashAdp typeHash = HashAdp_.new_(); + OrderedHash hash = OrderedHash_.new_(); + public static GfsRegy new_() {return new GfsRegy();} GfsRegy() {} +} +class GfsRegyItm implements GfoInvkAble { + public String Key() {return key;} public GfsRegyItm Key_(String v) {key = v; return this;} private String key; + public String TypeKey() {return typeKey;} public GfsRegyItm TypeKey_(String v) {typeKey = v; return this;} private String typeKey; + public boolean IsCmd() {return isCmd;} public GfsRegyItm IsCmd_(boolean v) {isCmd = v; return this;} private boolean isCmd; + public GfoInvkAble InvkAble() {return invkAble;} public GfsRegyItm InvkAble_(GfoInvkAble v) {invkAble = v; return this;} GfoInvkAble invkAble; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return invkAble.Invk(ctx, ikey, k, m);} + public static GfsRegyItm as_(Object obj) {return obj instanceof GfsRegyItm ? (GfsRegyItm)obj : null;} +} diff --git a/100_core/src_400_gfs/gplx/GfsTypeNames.java b/100_core/src_400_gfs/gplx/GfsTypeNames.java new file mode 100644 index 000000000..70c581f50 --- /dev/null +++ b/100_core/src_400_gfs/gplx/GfsTypeNames.java @@ -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 . +*/ +package gplx; +public class GfsTypeNames { + public static final String + String = "string" + , Int = "int" + , Bool = "bool" + , Float = "float" + , YesNo = "yn" + , Date = "date" + ; +} diff --git a/100_core/src_400_gfs/gplx/Gfs_Date_tst.java b/100_core/src_400_gfs/gplx/Gfs_Date_tst.java new file mode 100644 index 000000000..5ddeaa0a7 --- /dev/null +++ b/100_core/src_400_gfs/gplx/Gfs_Date_tst.java @@ -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 . +*/ +package gplx; +import org.junit.*; +public class Gfs_Date_tst { + @Before public void setup() { + fx = new GfsCoreFxt(); + fx.AddObj(DateAdp_.Gfs, "Date_"); + Tfds.Now_enabled_y_(); + } GfsCoreFxt fx; + @Test public void Now() { + fx.tst_MsgStr(fx.msg_(String_.Ary("Date_", "Now")), DateAdp_.parse_gplx("2001-01-01 00:00:00.000")); + } + @Test public void Add_day() { + fx.tst_MsgStr(fx.msg_(String_.Ary("Date_", "Now", "Add_day"), KeyVal_.new_("days", 1)), DateAdp_.parse_gplx("2001-01-02 00:00:00.000")); + } +} +class GfsCoreFxt { + public GfsCore Core() {return core;} GfsCore core = GfsCore.new_(); + public GfoMsg msg_(String[] ary, KeyVal... kvAry) {return GfoMsg_.root_leafArgs_(ary, kvAry);} + public void AddObj(GfoInvkAble invk, String s) {core.AddObj(invk, s);} + public void tst_MsgStr(GfoMsg msg, Object expd) { + GfsCtx ctx = GfsCtx.new_(); + Object actl = core.ExecOne(ctx, msg); + Tfds.Eq(Object_.XtoStr_OrNullStr(expd), Object_.XtoStr_OrNullStr(actl)); + } +} diff --git a/100_core/src_410_gfoCfg/gplx/GfoMsgParser.java b/100_core/src_410_gfoCfg/gplx/GfoMsgParser.java new file mode 100644 index 000000000..a149505f1 --- /dev/null +++ b/100_core/src_410_gfoCfg/gplx/GfoMsgParser.java @@ -0,0 +1,21 @@ +/* +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; +public interface GfoMsgParser { + GfoMsg ParseToMsg(String s); +} diff --git a/100_core/src_410_gfoCfg/gplx/GfoRegy.java b/100_core/src_410_gfoCfg/gplx/GfoRegy.java new file mode 100644 index 000000000..fb108b186 --- /dev/null +++ b/100_core/src_410_gfoCfg/gplx/GfoRegy.java @@ -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 . +*/ +package gplx; +public class GfoRegy implements GfoInvkAble { + public int Count() {return hash.Count();} + public HashAdp Parsers() {return parsers;} HashAdp parsers = HashAdp_.new_(); + public GfoRegyItm FetchOrNull(String key) {return (GfoRegyItm)hash.Fetch(key);} + public Object FetchValOrFail(String key) { + GfoRegyItm rv = (GfoRegyItm)hash.Fetch(key); if (rv == null) throw Err_.new_("regy does not have key").Add("key", key); + return rv.Val(); + } + public Object FetchValOrNull(String key) {return FetchValOr(key, null);} + public Object FetchValOr(String key, Object or) { + GfoRegyItm itm = FetchOrNull(key); + return itm == null ? or : itm.Val(); + } + public void Del(String key) {hash.Del(key);} + public void RegObj(String key, Object val) {RegItm(key, val, GfoRegyItm.ValType_Obj, Io_url_.Null);} + public void RegDir(Io_url dirUrl, String match, boolean recur, String chopBgn, String chopEnd) { + Io_url[] filUrls = Io_mgr._.QueryDir_args(dirUrl).FilPath_(match).Recur_(recur).ExecAsUrlAry(); + if (filUrls.length == 0 && !Io_mgr._.ExistsDir(dirUrl)) {UsrDlg_._.Stop(UsrMsg.new_("dirUrl does not exist").Add("dirUrl", dirUrl.Xto_api())); return;} + for (Io_url filUrl : filUrls) { + String key = filUrl.NameAndExt(); + int pos = String_.Find_none; + if (String_.EqNot(chopBgn, "")) { + pos = String_.FindFwd(key, chopBgn); + if (pos == String_.Len(key) - 1) + throw Err_.new_(Err_ChopBgn).Add("key", key).Add("chopBgn", chopBgn); + else if (pos != String_.Find_none) + key = String_.Mid(key, pos + 1); + } + if (String_.EqNot(chopEnd, "")) { + pos = String_.FindBwd(key, chopEnd); + if (pos == 0) + throw Err_.new_(Err_ChopEnd).Add("key", key).Add("chopEnd", chopEnd); + else if (pos != String_.Find_none) + key = String_.MidByLen(key, 0, pos); + } + if (hash.Has(key)) throw Err_.new_(Err_Dupe).Add("key", key).Add("filUrl", filUrl); + RegItm(key, null, GfoRegyItm.ValType_Url, filUrl); + } + } + public void RegObjByType(String key, String val, String type) { + Object o = val; + if (String_.EqNot(type, StringClassXtn.Key_const)) { + ParseAble parser = (ParseAble)parsers.Fetch(type); + if (parser == null) throw Err_.new_("could not find parser").Add("type", type).Add("key", key).Add("val", val); + o = parser.ParseAsObj(val); + } + RegItm(key, o, GfoRegyItm.ValType_Obj, Io_url_.Null); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_RegDir)) { + Io_url dir = m.ReadIoUrl("dir"); + String match = m.ReadStrOr("match", "*.*"); + boolean recur = m.ReadBoolOr("recur", false); + String chopBgn = m.ReadStrOr("chopBgn", ""); + String chopEnd = m.ReadStrOr("chopEnd", "."); + if (ctx.Deny()) return this; + RegDir(dir, match, recur, chopBgn, chopEnd); + } + else if (ctx.Match(k, Invk_RegObj)) { + String key = m.ReadStr("key"); + String val = m.ReadStr("val"); + String type = m.ReadStrOr("type", StringClassXtn.Key_const); + if (ctx.Deny()) return this; + RegObjByType(key, val, type); + } + else return GfoInvkAble_.Rv_unhandled; + return this; + } public static final String Invk_RegDir = "RegDir", Invk_RegObj = "RegObj"; + void RegItm(String key, Object val, int valType, Io_url url) { + hash.AddReplace(key, new GfoRegyItm(key, val, valType, url)); + } + HashAdp hash = HashAdp_.new_(); + public static final String Err_ChopBgn = "chopBgn results in null key", Err_ChopEnd = "chopEnd results in null key", Err_Dupe = "key already registered"; + public static final GfoRegy _ = new GfoRegy(); GfoRegy() {} + @gplx.Internal protected static GfoRegy new_() {return new GfoRegy();} +} diff --git a/100_core/src_410_gfoCfg/gplx/GfoRegyItm.java b/100_core/src_410_gfoCfg/gplx/GfoRegyItm.java new file mode 100644 index 000000000..afada9f7c --- /dev/null +++ b/100_core/src_410_gfoCfg/gplx/GfoRegyItm.java @@ -0,0 +1,31 @@ +/* +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; +public class GfoRegyItm { + public String Key() {return key;} private String key; + public Object Val() {return val;} Object val; + public Io_url Url() {return url;} Io_url url; + public int ValType() {return valType;} int valType; + public GfoRegyItm(String key, Object val, int valType, Io_url url) {this.key = key; this.val = val; this.valType = valType; this.url = url;} + + public static final int + ValType_Obj = 1 + , ValType_Url = 2 + , ValType_B64 = 3 + ; +} diff --git a/100_core/src_410_gfoCfg/gplx/GfoRegy_RegDir_tst.java b/100_core/src_410_gfoCfg/gplx/GfoRegy_RegDir_tst.java new file mode 100644 index 000000000..88a5bc0cd --- /dev/null +++ b/100_core/src_410_gfoCfg/gplx/GfoRegy_RegDir_tst.java @@ -0,0 +1,61 @@ +/* +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; +import org.junit.*; +public class GfoRegy_RegDir_tst { + @Before public void setup() { + regy = GfoRegy.new_(); + Io_mgr._.InitEngine_mem(); + root = Io_url_.mem_dir_("mem/root"); + } GfoRegy regy; Io_url root; + @Test public void Basic() { + ini_fil("101_tsta.txt"); + ini_fil("102_tstb.txt"); + ini_fil("103_tstc.png"); + ini_fil("dir1", "104_tstd.txt"); + regy.RegDir(root, "*.txt", false, "_", "."); + tst_Count(2); + tst_Exists("tsta"); + tst_Exists("tstb"); + } + @Test public void Err_dupe() { + ini_fil("101_tsta.txt"); + ini_fil("102_tsta.txt"); + try {regy.RegDir(root, "*.txt", false, "_", ".");} + catch (Exception e) {Tfds.Err_has(e, GfoRegy.Err_Dupe); return;} + Tfds.Fail_expdError(); + } + @Test public void Err_chopBgn() { + ini_fil("123_"); + try {regy.RegDir(root, "*", false, "_", ".");} + catch (Exception e) {Tfds.Err_has(e, GfoRegy.Err_ChopBgn); return;} + Tfds.Fail_expdError(); + } + @Test public void Err_chopEnd() { + ini_fil(".txt"); + try {regy.RegDir(root, "*.txt", false, "_", ".");} + catch (Exception e) {Tfds.Err_has(e, GfoRegy.Err_ChopEnd); return;} + Tfds.Fail_expdError(); + } + void tst_Count(int expd) {Tfds.Eq(expd, regy.Count());} + void tst_Exists(String expd) { + GfoRegyItm itm = regy.FetchOrNull(expd); + Tfds.Eq_nullNot(itm); + } + void ini_fil(String... nest) {Io_mgr._.SaveFilStr(root.GenSubFil_nest(nest), "");} +} diff --git a/100_core/src_410_gfoCfg/gplx/GfoRegy_basic_tst.java b/100_core/src_410_gfoCfg/gplx/GfoRegy_basic_tst.java new file mode 100644 index 000000000..b11c05ae9 --- /dev/null +++ b/100_core/src_410_gfoCfg/gplx/GfoRegy_basic_tst.java @@ -0,0 +1,31 @@ +/* +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; +import org.junit.*; +public class GfoRegy_basic_tst { + @Before public void setup() { + regy = GfoRegy.new_(); + } GfoRegy regy; + @Test public void RegObjByType() { + regy.Parsers().Add("Io_url", Io_url_.Parser); + Io_url expd = Io_url_.new_any_("C:\\fil.txt"); + regy.RegObjByType("test", expd.Xto_api(), "Io_url"); + Io_url actl = (Io_url)regy.FetchValOr("test", Io_url_.Null); + Tfds.Eq(expd.Xto_api(), actl.Xto_api()); + } +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_log_bfr.java b/100_core/src_420_usrMsg/gplx/Gfo_log_bfr.java new file mode 100644 index 000000000..6ab5589e2 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_log_bfr.java @@ -0,0 +1,29 @@ +/* +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; +public class Gfo_log_bfr { + private Bry_bfr bfr = Bry_bfr.reset_(255); + public Gfo_log_bfr Add(String s) { + bfr.Add_str(DateAdp_.Now().XtoUtc().XtoStr_fmt_yyyyMMdd_HHmmss_fff()); + bfr.Add_byte_space(); + bfr.Add_str(s); + bfr.Add_byte_nl(); + return this; + } + public String Xto_str() {return bfr.XtoStrAndClear();} +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_log_wtr.java b/100_core/src_420_usrMsg/gplx/Gfo_log_wtr.java new file mode 100644 index 000000000..946a283e5 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_log_wtr.java @@ -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 . +*/ +package gplx; +public interface Gfo_log_wtr extends GfoInvkAble { + Io_url Session_dir(); + Io_url Log_dir(); void Log_dir_(Io_url v); + Io_url Session_fil(); + boolean Enabled(); void Enabled_(boolean v); + boolean Queue_enabled(); void Queue_enabled_(boolean v); + void Log_msg_to_url_fmt(Io_url url, String fmt, Object... args); + void Log_msg_to_session(String txt); + void Log_msg_to_session_fmt(String fmt, Object... args); + void Log_msg_to_session_direct(String txt); + void Log_err(String txt); + void Init(); + void Term(); +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_log_wtr_.java b/100_core/src_420_usrMsg/gplx/Gfo_log_wtr_.java new file mode 100644 index 000000000..65892cf1b --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_log_wtr_.java @@ -0,0 +1,36 @@ +/* +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; +public class Gfo_log_wtr_ { + public static final Gfo_log_wtr Null = new Gfo_log_wtr_null(); +} +class Gfo_log_wtr_null implements Gfo_log_wtr { + public Io_url Session_fil() {return Io_url_.Null;} + public Io_url Session_dir() {return Io_url_.Null;} + public Io_url Log_dir() {return Io_url_.Null;} public void Log_dir_(Io_url v) {} + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public boolean Queue_enabled() {return queue_enabled;} public void Queue_enabled_(boolean v) {queue_enabled = v;} private boolean queue_enabled; + public void Log_msg_to_url_fmt(Io_url url, String fmt, Object... args) {} + public void Log_msg_to_session_fmt(String fmt, Object... args) {} + public void Log_msg_to_session(String txt) {} + public void Log_msg_to_session_direct(String txt) {} + public void Log_err(String txt) {} + public void Init() {} + public void Term() {} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg.java b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg.java new file mode 100644 index 000000000..b1a0b6849 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg.java @@ -0,0 +1,35 @@ +/* +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; +public interface Gfo_usr_dlg extends GfoInvkAble, Cancelable { + void Canceled_y_(); void Canceled_n_(); + void Clear(); + Gfo_usr_dlg_ui Ui_wkr(); void Ui_wkr_(Gfo_usr_dlg_ui v); + Gfo_log_wtr Log_wtr(); void Log_wtr_(Gfo_log_wtr v); + String Log_many(String grp_key, String msg_key, String fmt, Object... args); + String Warn_many(String grp_key, String msg_key, String fmt, Object... args); + Err Fail_many(String grp_key, String msg_key, String fmt, Object... args); + String Prog_many(String grp_key, String msg_key, String fmt, Object... args); + String Prog_none(String grp_key, String msg_key, String fmt); + String Note_many(String grp_key, String msg_key, String fmt, Object... args); + String Note_none(String grp_key, String msg_key, String fmt); + String Note_gui_none(String grp_key, String msg_key, String fmt); + String Prog_one(String grp_key, String msg_key, String fmt, Object arg); + String Prog_direct(String msg); + String Log_direct(String msg); +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_.java b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_.java new file mode 100644 index 000000000..385fcda98 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_.java @@ -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 . +*/ +package gplx; +public class Gfo_usr_dlg_ { + public static Gfo_usr_dlg _ = Gfo_usr_dlg_null._; + public static final Gfo_usr_dlg Null = Gfo_usr_dlg_null._; +} +class Gfo_usr_dlg_null implements Gfo_usr_dlg { + public boolean Canceled() {return false;} public void Canceled_y_() {} public void Canceled_n_() {} + public void Cancel() {} public void Cancel_reset() {} + public void Clear() {} + public Gfo_usr_dlg_ui Ui_wkr() {throw Err_.not_implemented_();} public void Ui_wkr_(Gfo_usr_dlg_ui v) {} + public Gfo_log_wtr Log_wtr() {throw Err_.not_implemented_();} public void Log_wtr_(Gfo_log_wtr v) {} + public String Log_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public String Warn_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public Err Fail_many(String grp_key, String msg_key, String fmt, Object... args) {return Err_.new_(fmt);} + public String Prog_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public String Prog_none(String grp_key, String msg_key, String fmt) {return "";} + public String Note_many(String grp_key, String msg_key, String fmt, Object... args) {return "";} + public String Note_none(String grp_key, String msg_key, String fmt) {return "";} + public String Note_gui_none(String grp_key, String msg_key, String fmt) {return "";} + public String Prog_one(String grp_key, String msg_key, String fmt, Object arg) {return "";} + public String Prog_direct(String msg) {return "";} + public String Log_direct(String msg) {return "";} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} + public static final Gfo_usr_dlg_null _ = new Gfo_usr_dlg_null(); Gfo_usr_dlg_null() {} +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui.java b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui.java new file mode 100644 index 000000000..01fe7eb6a --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui.java @@ -0,0 +1,26 @@ +/* +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; +public interface Gfo_usr_dlg_ui { + void Clear(); + String_ring Prog_msgs(); + void Write_prog(String text); + void Write_note(String text); + void Write_warn(String text); + void Write_stop(String text); +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui_.java b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui_.java new file mode 100644 index 000000000..8105c41b4 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui_.java @@ -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 . +*/ +package gplx; +public class Gfo_usr_dlg_ui_ { + public static final Gfo_usr_dlg_ui Null = new Gfo_usr_dlg_ui_null(); + public static final Gfo_usr_dlg_ui Console = new Gfo_usr_dlg_ui_console(); + public static final Gfo_usr_dlg_ui Test = new Gfo_usr_dlg_ui_test(); +} +class Gfo_usr_dlg_ui_null implements Gfo_usr_dlg_ui { + public void Clear() {} + public String_ring Prog_msgs() {return ring;} String_ring ring = new String_ring().Max_(0); + public void Write_prog(String text) {} + public void Write_note(String text) {} + public void Write_warn(String text) {} + public void Write_stop(String text) {} +} +class Gfo_usr_dlg_ui_console implements Gfo_usr_dlg_ui { + public void Clear() {} + public String_ring Prog_msgs() {return ring;} String_ring ring = new String_ring().Max_(0); + public void Write_prog(String text) {console.WriteTempText(text);} + public void Write_note(String text) {console.WriteLine(text);} + public void Write_warn(String text) {console.WriteLine(text);} + public void Write_stop(String text) {console.WriteLine(text);} + ConsoleAdp console = ConsoleAdp._; +} diff --git a/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui_test.java b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui_test.java new file mode 100644 index 000000000..1dc21391d --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/Gfo_usr_dlg_ui_test.java @@ -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 . +*/ +package gplx; +public class Gfo_usr_dlg_ui_test implements Gfo_usr_dlg_ui { + public String[] Xto_str_ary() {return msgs.XtoStrAry();} + public ListAdp Warns() {return warns;} + public String_ring Prog_msgs() {return ring;} String_ring ring = new String_ring().Max_(0); + public void Clear() {msgs.Clear(); warns.Clear();} + public void Write_prog(String text) {msgs.Add(text);} ListAdp msgs = ListAdp_.new_(); + public void Write_note(String text) {msgs.Add(text);} + public void Write_warn(String text) {warns.Add(text);} ListAdp warns = ListAdp_.new_(); + public void Write_stop(String text) {msgs.Add(text);} +} diff --git a/100_core/src_420_usrMsg/gplx/UsrDlg.java b/100_core/src_420_usrMsg/gplx/UsrDlg.java new file mode 100644 index 000000000..70298a840 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrDlg.java @@ -0,0 +1,51 @@ +/* +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; +public class UsrDlg { + public int Verbosity() {return verbosity;} public UsrDlg Verbosity_(int v) {verbosity = v; return this;} int verbosity = UsrMsgWkr_.Type_Note; + public void Note(String text, Object... ary) {Exec(text, ary, noteWkrs);} + public void Warn(String text, Object... ary) {Exec(text, ary, warnWkrs);} + public void Stop(String text, Object... ary) {Exec(text, ary, stopWkrs);} + public void Note(UsrMsg msg) {Exec(UsrMsgWkr_.Type_Note, msg);} + public void Warn(UsrMsg msg) {Exec(UsrMsgWkr_.Type_Warn, msg);} + public void Stop(UsrMsg msg) {Exec(UsrMsgWkr_.Type_Stop, msg);} + public void Exec(int type, UsrMsg umsg) { + UsrMsgWkrList list = GetList(type); + list.Exec(umsg); + } + void Exec(String text, Object[] ary, UsrMsgWkrList list) { + String msg = String_.Format(text, ary); + list.Exec(UsrMsg.new_(msg)); + } + public void Reg(int type, UsrMsgWkr wkr) { + UsrMsgWkrList list = GetList(type); + list.Add(wkr); + } + public void RegOff(int type, UsrMsgWkr wkr) { + UsrMsgWkrList list = GetList(type); + list.Del(wkr); + } + UsrMsgWkrList GetList(int type) { + if (type == UsrMsgWkr_.Type_Note) return noteWkrs; + else if (type == UsrMsgWkr_.Type_Warn) return warnWkrs; + else if (type == UsrMsgWkr_.Type_Stop) return stopWkrs; + else throw Err_.unhandled(type); + } + UsrMsgWkrList noteWkrs = new UsrMsgWkrList(UsrMsgWkr_.Type_Note), warnWkrs = new UsrMsgWkrList(UsrMsgWkr_.Type_Warn), stopWkrs = new UsrMsgWkrList(UsrMsgWkr_.Type_Stop); + public static UsrDlg new_() {return new UsrDlg();} +} diff --git a/100_core/src_420_usrMsg/gplx/UsrDlg_.java b/100_core/src_420_usrMsg/gplx/UsrDlg_.java new file mode 100644 index 000000000..72fb054e4 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrDlg_.java @@ -0,0 +1,21 @@ +/* +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; +public class UsrDlg_ { + public static final UsrDlg _ = UsrDlg.new_(); +} diff --git a/100_core/src_420_usrMsg/gplx/UsrMsg.java b/100_core/src_420_usrMsg/gplx/UsrMsg.java new file mode 100644 index 000000000..d56d07362 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrMsg.java @@ -0,0 +1,67 @@ +/* +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; +public class UsrMsg { + public int VisibilityDuration() {return visibilityDuration;} public UsrMsg VisibilityDuration_(int v) {visibilityDuration = v; return this;} int visibilityDuration = 3000; + public String Hdr() {return hdr;} public UsrMsg Hdr_(String val) {hdr = val; return this;} private String hdr; + public OrderedHash Args() {return args;} OrderedHash args = OrderedHash_.new_(); + public UsrMsg Add(String k, Object v) { + args.Add(k, KeyVal_.new_(k, v)); + return this; + } + public UsrMsg AddReplace(String k, Object v) { + args.AddReplace(k, KeyVal_.new_(k, v)); + return this; + } + public String XtoStrSingleLine() {return XtoStr(" ");} + public String XtoStr() {return XtoStr(Op_sys.Cur().Nl_str());} + String XtoStr(String spr) { + if (hdr == null) { + GfoMsg m = GfoMsg_.new_cast_(cmd); + for (int i = 0; i < args.Count(); i++) { + KeyVal kv = (KeyVal)args.FetchAt(i); + m.Add(kv.Key(), kv.Val()); + } + return Object_.XtoStr_OrNullStr(invk.Invk(GfsCtx._, 0, cmd, m)); + } + String_bldr sb = String_bldr_.new_(); + sb.Add(hdr).Add(spr); + for (int i = 0; i < args.Count(); i++) { + KeyVal kv = (KeyVal)args.FetchAt(i); + sb.Add_spr_unless_first("", " ", i); + sb.Add_fmt("{0}={1}", kv.Key(), kv.Val(), spr); + } + return sb.XtoStr(); + } + public static UsrMsg fmt_(String hdr, Object... ary) { + UsrMsg rv = new UsrMsg(); + rv.hdr = String_.Format(hdr, ary); + return rv; + } UsrMsg() {} + public static UsrMsg new_(String hdr) { + UsrMsg rv = new UsrMsg(); + rv.hdr = hdr; + return rv; + } + public static UsrMsg invk_(GfoInvkAble invk, String cmd) { + UsrMsg rv = new UsrMsg(); + rv.invk = invk; + rv.cmd = cmd; + return rv; + } GfoInvkAble invk; String cmd; +} diff --git a/100_core/src_420_usrMsg/gplx/UsrMsgWkr.java b/100_core/src_420_usrMsg/gplx/UsrMsgWkr.java new file mode 100644 index 000000000..cedbef815 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrMsgWkr.java @@ -0,0 +1,50 @@ +/* +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; +public interface UsrMsgWkr { + void ExecUsrMsg(int type, UsrMsg umsg); +} +class UsrMsgWkrList { + public void Add(UsrMsgWkr v) { + if (wkr == null && list == null) + wkr = v; + else { + if (list == null) { + list = ListAdp_.new_(); + list.Add(wkr); + wkr = null; + } + list.Add(v); + } + } + public void Del(UsrMsgWkr v) { +// list.Del(v); + } + public void Exec(UsrMsg umsg) { + if (wkr != null) + wkr.ExecUsrMsg(type, umsg); + else if (list != null) { + for (Object lObj : list) { + UsrMsgWkr l = (UsrMsgWkr)lObj; + l.ExecUsrMsg(type, umsg); + } + } + } + ListAdp list; UsrMsgWkr wkr; int type; + public UsrMsgWkrList(int type) {this.type = type;} +} diff --git a/100_core/src_420_usrMsg/gplx/UsrMsgWkr_.java b/100_core/src_420_usrMsg/gplx/UsrMsgWkr_.java new file mode 100644 index 000000000..2bedb2294 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrMsgWkr_.java @@ -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 . +*/ +package gplx; +public class UsrMsgWkr_ { + public static final int + Type_None = 0 + , Type_Stop = 1 + , Type_Warn = 2 + , Type_Note = 4 + , Type_Log = 8 + ; +} diff --git a/100_core/src_420_usrMsg/gplx/UsrMsgWkr_console.java b/100_core/src_420_usrMsg/gplx/UsrMsgWkr_console.java new file mode 100644 index 000000000..7ba18b027 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrMsgWkr_console.java @@ -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 . +*/ +package gplx; +public class UsrMsgWkr_console implements UsrMsgWkr { + public void ExecUsrMsg(int type, UsrMsg umsg) { + String text = umsg.XtoStr(); + if (type == UsrMsgWkr_.Type_Warn) + text = "!!!!" + text; + else if (type == UsrMsgWkr_.Type_Stop) + text = "****" + text; + ConsoleAdp._.WriteText(text); + } + public static void RegAll(UsrDlg dlg) { + UsrMsgWkr wkr = new UsrMsgWkr_console(); + dlg.Reg(UsrMsgWkr_.Type_Note, wkr); + dlg.Reg(UsrMsgWkr_.Type_Stop, wkr); + dlg.Reg(UsrMsgWkr_.Type_Warn, wkr); + } +} diff --git a/100_core/src_420_usrMsg/gplx/UsrMsgWkr_test.java b/100_core/src_420_usrMsg/gplx/UsrMsgWkr_test.java new file mode 100644 index 000000000..e7d215982 --- /dev/null +++ b/100_core/src_420_usrMsg/gplx/UsrMsgWkr_test.java @@ -0,0 +1,38 @@ +/* +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; +public class UsrMsgWkr_test implements UsrMsgWkr { + public void ExecUsrMsg(int type, UsrMsg m) { + msgs.Add(m); + } + public boolean HasWarn(UsrMsg um) { + for (int i = 0; i < msgs.Count(); i++) { + UsrMsg found = (UsrMsg)msgs.FetchAt(i); + if (String_.Eq(um.XtoStr(), found.XtoStr())) return true; + } + return false; + } + public static UsrMsgWkr_test RegAll(UsrDlg dlg) { + UsrMsgWkr_test wkr = new UsrMsgWkr_test(); + dlg.Reg(UsrMsgWkr_.Type_Note, wkr); + dlg.Reg(UsrMsgWkr_.Type_Stop, wkr); + dlg.Reg(UsrMsgWkr_.Type_Warn, wkr); + return wkr; + } + ListAdp msgs = ListAdp_.new_(); +} diff --git a/100_core/src_800_tst/gplx/PerfLogMgr_fxt.java b/100_core/src_800_tst/gplx/PerfLogMgr_fxt.java new file mode 100644 index 000000000..552d368ca --- /dev/null +++ b/100_core/src_800_tst/gplx/PerfLogMgr_fxt.java @@ -0,0 +1,65 @@ +/* +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; +public class PerfLogMgr_fxt { + public void Init(Io_url url, String text) { + this.url = url; + entries.ResizeBounds(1000); + entries.Add(new PerfLogItm(0, text + "|" + DateAdp_.Now().XtoStr_gplx())); + tmr.Bgn(); + } + public void Write(String text) { + long milliseconds = tmr.ElapsedMilliseconds(); + entries.Add(new PerfLogItm(milliseconds, text)); + tmr.Bgn(); + } + public void WriteFormat(String fmt, Object... ary) { + long milliseconds = tmr.ElapsedMilliseconds(); + String text = String_.Format(fmt, ary); + entries.Add(new PerfLogItm(milliseconds, text)); + tmr.Bgn(); + } + public void Flush() { + String_bldr sb = String_bldr_.new_(); + for (Object itmObj : entries) { + PerfLogItm itm = (PerfLogItm)itmObj; + sb.Add(itm.XtoStr()).Add_char_crlf(); + } + Io_mgr._.AppendFilStr(url, sb.XtoStr()); + entries.Clear(); + } + ListAdp entries = ListAdp_.new_(); PerfLogTmr tmr = PerfLogTmr.new_(); Io_url url = Io_url_.Null; + public static final PerfLogMgr_fxt _ = new PerfLogMgr_fxt(); PerfLogMgr_fxt() {} + class PerfLogItm { + public String XtoStr() { + String secondsStr = TimeSpanAdp_.XtoStr(milliseconds, TimeSpanAdp_.Fmt_Default); + secondsStr = String_.PadBgn(secondsStr, 7, "0"); // 7=000.000; left-aligns all times + return String_.Concat(secondsStr, "|", text); + } + long milliseconds; String text; + @gplx.Internal protected PerfLogItm(long milliseconds, String text) { + this.milliseconds = milliseconds; this.text = text; + } + } + +} +class PerfLogTmr { + public void Bgn() {bgn = Env_.TickCount();} long bgn; + public long ElapsedMilliseconds() {return Env_.TickCount() - bgn; } + public static PerfLogTmr new_() {return new PerfLogTmr();} PerfLogTmr() {} +} diff --git a/100_core/src_800_tst/gplx/Tfds.java b/100_core/src_800_tst/gplx/Tfds.java new file mode 100644 index 000000000..0eb8532bd --- /dev/null +++ b/100_core/src_800_tst/gplx/Tfds.java @@ -0,0 +1,244 @@ +/* +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; +public class Tfds { // URL:doc/gplx.tfds/Tfds.txt + public static boolean SkipDb = false; + public static void Eq(Object expd, Object actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_able(EqAble expd, EqAble actl) {Eq_able_wkr(expd, actl, true, EmptyStr);} + public static void Eq_able(EqAble expd, EqAble actl, String fmt, Object... args) {Eq_able_wkr(expd, actl, true, String_.Format(fmt, args));} + public static void Eq_byte(byte expd, byte actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_long(long expd, long actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_float(float expd, float actl) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_decimal(DecimalAdp expd, DecimalAdp actl) {Eq_wkr(expd.XtoDouble(), actl.XtoDouble(), true, EmptyStr);} + public static void Eq_date(DateAdp expd, DateAdp actl) {Eq_wkr(expd.XtoStr_gplx(), actl.XtoStr_gplx(), true, EmptyStr);} + public static void Eq_date(DateAdp expd, DateAdp actl, String fmt, Object... args){Eq_wkr(expd.XtoStr_gplx(), actl.XtoStr_gplx(), true, String_.Format(fmt, args));} + public static void Eq_url(Io_url expd, Io_url actl) {Eq_wkr(expd.Raw(), actl.Raw(), true, EmptyStr);} + public static void Eq_bry(String expd, byte[] actl) {Eq_wkr(expd, String_.new_utf8_(actl), true, EmptyStr);} + public static void Eq_bry(byte[] expd, byte[] actl) {Eq_wkr(String_.new_utf8_(expd), String_.new_utf8_(actl), true, EmptyStr);} + public static void Eq_str(XtoStrAble expd, XtoStrAble actl, String msg) {Eq_wkr(expd.XtoStr(), actl.XtoStr(), true, msg);} + public static void Eq_str(XtoStrAble expd, XtoStrAble actl) {Eq_wkr(expd.XtoStr(), actl.XtoStr(), true, String_.Empty);} + public static void Eq_str_lines(String lhs, String rhs) {Eq_str_lines(lhs, rhs, EmptyStr);} + public static void Eq_str_lines(String lhs, String rhs, String note) { + if (lhs == null && rhs == null) return; // true + else if (lhs == null) throw Err_.new_("lhs is null" + note); + else if (rhs == null) throw Err_.new_("rhs is null" + note); + else Eq_ary_wkr(String_.Split(lhs, Char_.NewLine), String_.Split(rhs, Char_.NewLine), false, note); + } + public static void Eq(Object expd, Object actl, String fmt, Object... args) {Eq_wkr(expd, actl, true, String_.Format(fmt, args));} + public static void Eq_rev(Object actl, Object expd) {Eq_wkr(expd, actl, true, EmptyStr);} + public static void Eq_rev(Object actl, Object expd, String fmt, Object... args) {Eq_wkr(expd, actl, true, String_.Format(fmt, args));} + public static void Eq_true(Object actl) {Eq_wkr(true, actl, true, EmptyStr);} + public static void Eq_true(Object actl, String fmt, Object... args) {Eq_wkr(true, actl, true, String_.Format(fmt, args));} + public static void Eq_false(Object actl) {Eq_wkr(false, actl, true, EmptyStr);} + public static void Eq_false(Object actl, String fmt, Object... args) {Eq_wkr(false, actl, true, String_.Format(fmt, args));} + public static void Eq_null(Object actl) {Eq_wkr(null, actl, true, EmptyStr);} + public static void Eq_null(Object actl, String fmt, Object... args) {Eq_wkr(null, actl, true, String_.Format(fmt, args));} + public static void Eq_nullNot(Object actl) {Eq_wkr(null, actl, false, EmptyStr);} + public static void Eq_nullNot(Object actl, String fmt, Object... args) {Eq_wkr(null, actl, false, String_.Format(fmt, args));} + public static void Fail_expdError() {Eq_wkr(true, false, true, "fail expd error");} + public static void Fail(String fmt, Object... args) {Eq_wkr(true, false, true, String_.Format(fmt, args));} + public static void Eq_ary(Object lhs, Object rhs) {Eq_ary_wkr(lhs, rhs, true, EmptyStr);} + public static void Eq_ary(Object lhs, Object rhs, String fmt, Object... args){Eq_ary_wkr(lhs, rhs, true, String_.Format(fmt, args));} + public static void Eq_ary_str(Object lhs, Object rhs, String note) {Eq_ary_wkr(lhs, rhs, false, note);} + public static void Eq_ary_str(Object lhs, Object rhs) {Eq_ary_wkr(lhs, rhs, false, EmptyStr);} + public static void Eq_list(ListAdp lhs, ListAdp rhs) {Eq_list_wkr(lhs, rhs, TfdsEqListItmStr_cls_default._, EmptyStr);} + public static void Eq_list(ListAdp lhs, ListAdp rhs, TfdsEqListItmStr xtoStr) {Eq_list_wkr(lhs, rhs, xtoStr, EmptyStr);} + static void Eq_able_wkr(EqAble lhs, EqAble rhs, boolean expd, String customMsg) { + boolean actl = false; + if (lhs == null && rhs != null) actl = false; + else if (lhs != null && rhs == null) actl = false; + else actl = lhs.Eq(rhs); + if (expd == actl) return; + String msg = msgBldr.Eq_xtoStr(lhs, rhs, customMsg); + throw Err_.new_(msg); + } + static void Eq_wkr(Object lhs, Object rhs, boolean expd, String customMsg) { + boolean actl = Object_.Eq(lhs, rhs); + if (expd == actl) return; + String msg = msgBldr.Eq_xtoStr(lhs, rhs, customMsg); + throw Err_.new_(msg); + } + static void Eq_ary_wkr(Object lhsAry, Object rhsAry, boolean compareUsingEquals, String customMsg) { + ListAdp list = ListAdp_.new_(); boolean pass = true; + int lhsLen = Array_.Len(lhsAry), rhsLen = Array_.Len(rhsAry); + for (int i = 0; i < lhsLen; i++) { + Object lhs = Array_.FetchAt(lhsAry, i); + Object rhs = i >= rhsLen ? "<>" : Array_.FetchAt(rhsAry, i); + String lhsString = msgBldr.Obj_xtoStr(lhs); String rhsString = msgBldr.Obj_xtoStr(rhs); // even if compareUsingEquals, method does ToStr on each itm for failMsg + boolean isEq = compareUsingEquals + ? Object_.Eq(lhs, rhs) + : Object_.Eq(lhsString, rhsString); + Eq_ary_wkr_addItm(list, i, isEq, lhsString, rhsString); + if (!isEq) pass = false; + } + for (int i = lhsLen; i < rhsLen; i++) { + String lhsString = "<>"; + String rhsString = msgBldr.Obj_xtoStr(Array_.FetchAt(rhsAry, i)); + Eq_ary_wkr_addItm(list, i, false, lhsString, rhsString); + pass = false; + } + if (pass) return; + String msg = msgBldr.Eq_ary_xtoStr(list, lhsLen, rhsLen, customMsg); + throw Err_.new_(msg); + } + static void Eq_list_wkr(ListAdp lhsList, ListAdp rhsList, TfdsEqListItmStr xtoStr, String customMsg) { + ListAdp list = ListAdp_.new_(); boolean pass = true; + int lhsLen = lhsList.Count(), rhsLen = rhsList.Count(); + for (int i = 0; i < lhsLen; i++) { + Object lhs = lhsList.FetchAt(i); + Object rhs = i >= rhsLen ? null : rhsList.FetchAt(i); + String lhsStr = xtoStr.XtoStr(lhs, lhs); + String rhsStr = rhs == null ? "<>" : xtoStr.XtoStr(rhs, lhs); + boolean isEq = Object_.Eq(lhsStr, rhsStr); if (!isEq) pass = false; + Eq_ary_wkr_addItm(list, i, isEq, lhsStr, rhsStr); + } + for (int i = lhsLen; i < rhsLen; i++) { + String lhsStr = "<>"; + Object rhs = rhsList.FetchAt(i); + String rhsStr = xtoStr.XtoStr(rhs, null); + Eq_ary_wkr_addItm(list, i, false, lhsStr, rhsStr); + pass = false; + } + if (pass) return; + String msg = msgBldr.Eq_ary_xtoStr(list, lhsLen, rhsLen, customMsg); + throw Err_.new_(msg); + } + static void Eq_ary_wkr_addItm(ListAdp list, int i, boolean isEq, String lhsString, String rhsString) { + TfdsEqAryItm itm = new TfdsEqAryItm().Idx_(i).Eq_(isEq).Lhs_(lhsString).Rhs_(rhsString); + list.Add(itm); + } + public static void Err_classMatch(Exception exc, Class type) { + boolean match = ClassAdp_.Eq_typeSafe(exc, type); + if (!match) throw Err_.new_key_("Tfds", "error types do not match").Add("expdType", ClassAdp_.FullNameOf_type(type)).Add("actlType", ClassAdp_.NameOf_obj(exc)).Add("actlMsg", Err_.Message_lang(exc)); + } + public static void Eq_err(Err expd, Exception actlExc) { + Tfds.Eq(XtoStr_Err(expd), XtoStr_Err(actlExc)); + } + public static void Err_has(Exception e, String hdr) { + Tfds.Eq_true(String_.Has(Err_.Message_gplx_brief(e), hdr), "could not find '{0}' in '{1}'", hdr, Err_.Message_gplx_brief(e)); + } + static String XtoStr_Err(Exception e) { + Err err = Err_.as_(e); if (err == null) return Err_.Message_lang(e); + String_bldr sb = String_bldr_.new_(); + sb.Add(err.Hdr()).Add(":"); + for (Object kvo : err.Args()) { + KeyVal kv = (KeyVal)kvo; + if (sb.Count() != 0) sb.Add(" "); + sb.Add_fmt("{0}={1}", kv.Key(), kv.Val()); + } + return sb.XtoStr(); + } + static final String EmptyStr = TfdsMsgBldr.EmptyStr; + static TfdsMsgBldr msgBldr = TfdsMsgBldr.new_(); + public static final Io_url RscDir = Io_url_.Usr().GenSubDir_nest("xowa", "dev", "tst"); + public static DateAdp Now_time0_add_min(int minutes) {return time0.Add_minute(minutes);} + @gplx.Internal protected static boolean Now_enabled() {return now_enabled;} static boolean now_enabled; + public static void Now_enabled_n_() {now_enabled = false;} + public static void Now_set(DateAdp date) {now_enabled = true; nowTime = date;} + public static void Now_enabled_y_() {now_enabled = true; nowTime = time0;} + @gplx.Internal protected static DateAdp Now() { + DateAdp rv = nowTime; + nowTime = rv.Add_minute(1); + return rv; + } + private static final DateAdp time0 = DateAdp_.parse_gplx("2001-01-01 00:00:00.000"); + private static DateAdp nowTime; // NOTE: cannot set to time0 due to static initialization; + public static void WriteText(String text) {ConsoleAdp._.WriteText(text);} + public static void Write_bry(byte[] ary) {Write(String_.new_utf8_(ary));} + public static void Write() {Write("tmp");} + public static void Write(Object... ary) { + String_bldr sb = String_bldr_.new_(); + int aryLen = Array_.Len(ary); + for (int i = 0; i < aryLen; i++) + sb.Add_many("'", Object_.XtoStr_OrNullStr(ary[i]), "'", " "); + WriteText(sb.XtoStr() + String_.CrLf); + } +} +class TfdsEqListItmStr_cls_default implements TfdsEqListItmStr { + public String XtoStr(Object cur, Object actl) { + return Object_.XtoStr_OrNullStr(cur); + } + public static final TfdsEqListItmStr_cls_default _ = new TfdsEqListItmStr_cls_default(); TfdsEqListItmStr_cls_default() {} +} +class TfdsEqAryItm { + public int Idx() {return idx;} public TfdsEqAryItm Idx_(int v) {idx = v; return this;} int idx; + public String Lhs() {return lhs;} public TfdsEqAryItm Lhs_(String v) {lhs = v; return this;} private String lhs; + public String Rhs() {return rhs;} public TfdsEqAryItm Rhs_(String v) {rhs = v; return this;} private String rhs; + public boolean Eq() {return eq;} public TfdsEqAryItm Eq_(boolean v) {eq = v; return this;} private boolean eq; +} +class TfdsMsgBldr { + public String Eq_xtoStr(Object expd, Object actl, String customMsg) { + String expdString = Obj_xtoStr(expd); String actlString = Obj_xtoStr(actl); + String detail = String_.Concat + ( CustomMsg_xtoStr(customMsg) + , "\t\t", "expd: ", expdString, String_.CrLf + , "\t\t", "actl: ", actlString, String_.CrLf + ); + return WrapMsg(detail); + } + public String Eq_ary_xtoStr(ListAdp list, int lhsAryLen, int rhsAryLen, String customMsg) { + String_bldr sb = String_bldr_.new_(); + sb.Add(CustomMsg_xtoStr(customMsg)); + if (lhsAryLen != rhsAryLen) + sb.Add_fmt_line("{0}element counts differ: {1} {2}", "\t\t", lhsAryLen, rhsAryLen); + int lhsLenMax = 0, rhsLenMax = 0; + for (int i = 0; i < list.Count(); i++) { + TfdsEqAryItm itm = (TfdsEqAryItm)list.FetchAt(i); + int lhsLen = String_.Len(itm.Lhs()), rhsLen = String_.Len(itm.Rhs()); + if (lhsLen > lhsLenMax) lhsLenMax = lhsLen; + if (rhsLen > rhsLenMax) rhsLenMax = rhsLen; + } + for (int i = 0; i < list.Count(); i++) { + TfdsEqAryItm itm = (TfdsEqAryItm)list.FetchAt(i); + sb.Add_fmt_line("{0}: {1} {2} {3}" + , Int_.XtoStr_PadBgn(itm.Idx(), 4) + , String_.PadBgn(itm.Lhs(), lhsLenMax, " ") + , itm.Eq() ? "==" : "!=" + , String_.PadBgn(itm.Rhs(), rhsLenMax, " ") + ); + } +// String compSym = isEq ? " " : "!="; +// String result = String_.Format("{0}: {1}{2} {3} {4}", Int_.XtoStr_PadBgn(i, 4), lhsString, String_.CrLf + "\t\t", compSym, rhsString); +// foreach (Object obj in list) { +// String itmComparison = (String)obj; +// sb.Add_fmt_line("{0}{1}", "\t\t", itmComparison); +// } + return WrapMsg(sb.XtoStr()); + } + String CustomMsg_xtoStr(String customMsg) { + return (customMsg == EmptyStr) + ? "" + : String_.Concat(customMsg, String_.CrLf); + } + public String Obj_xtoStr(Object obj) { + String s = String_.as_(obj); + if (s != null) return String_.Concat("'", s, "'"); // if Object is String, put quotes around it for legibility + XtoStrAble xtoStrAble = XtoStrAble_.as_(obj); + if (xtoStrAble != null) return xtoStrAble.XtoStr(); + return Object_.XtoStr_OrNullStr(obj); + } + String WrapMsg(String text) { + return String_.Concat(String_.CrLf + , "************************************************************************************************", String_.CrLf + , text + , "________________________________________________________________________________________________" + ); + } + public static TfdsMsgBldr new_() {return new TfdsMsgBldr();} TfdsMsgBldr() {} + public static final String EmptyStr = ""; +} diff --git a/100_core/src_800_tst/gplx/TfdsEqListItmStr.java b/100_core/src_800_tst/gplx/TfdsEqListItmStr.java new file mode 100644 index 000000000..92d98f50e --- /dev/null +++ b/100_core/src_800_tst/gplx/TfdsEqListItmStr.java @@ -0,0 +1,21 @@ +/* +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; +public interface TfdsEqListItmStr { + String XtoStr(Object cur, Object actl); +} diff --git a/100_core/src_900_xml/gplx/Base85_utl.java b/100_core/src_900_xml/gplx/Base85_utl.java new file mode 100644 index 000000000..df08a034a --- /dev/null +++ b/100_core/src_900_xml/gplx/Base85_utl.java @@ -0,0 +1,66 @@ +/* +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; +public class Base85_utl { + public static String XtoStr(int val, int minLen) {return String_.new_utf8_(XtoStrByAry(val, null, 0, minLen));} + public static byte[] XtoStrByAry(int val, int minLen) {return XtoStrByAry(val, null, 0, minLen);} + public static byte[] XtoStrByAry(int val, byte[] ary, int aryPos, int minLen) { + int strLen = DigitCount(val); + int aryLen = strLen, padLen = 0; + boolean pad = aryLen < minLen; + if (pad) { + padLen = minLen - aryLen; + aryLen = minLen; + } + if (ary == null) ary = new byte[aryLen]; + if (pad) { + for (int i = 0; i < padLen; i++) // fill ary with padLen + ary[i + aryPos] = AsciiOffset; + } + for (int i = aryLen - padLen; i > 0; i--) { + int div = Pow85[i - 1]; + byte tmp = (byte)(val / div); + ary[aryPos + aryLen - i] = (byte)(tmp + AsciiOffset); + val -= tmp * div; + } + return ary; + } + public static byte XtoByteChar(int v) {return (byte)(v + AsciiOffset);} + public static int XtoInt(byte v) {return v - AsciiOffset;} + public static int XtoIntByStr(String s) { + byte[] ary = Bry_.new_utf8_(s); + return XtoIntByAry(ary, 0, ary.length - 1); + } + public static int XtoIntByAry(byte[] ary, int bgn, int end) { + int rv = 0, factor = 1; + for (int i = end; i >= bgn; i--) { + rv += (ary[i] - AsciiOffset) * factor; + factor *= Radix; + } + return rv; + } + public static int DigitCount(int v) { + if (v == 0) return 1; + for (int i = Pow85Last; i > -1; i--) + if (v >= Pow85[i]) return i + 1; + throw Err_.new_("neg number not allowed").Add("v", v); + } + static int[] Pow85 = new int[]{1, 85, 7225, 614125, 52200625}; // NOTE: ary constructed to match index to exponent; Pow85[1] = 85^1 + static final int Pow85Last = 4, Radix = 85; static final byte AsciiOffset = 33; + public static final int Len_int = 5; +} diff --git a/100_core/src_900_xml/gplx/Base85_utl_tst.java b/100_core/src_900_xml/gplx/Base85_utl_tst.java new file mode 100644 index 000000000..1b4668c0d --- /dev/null +++ b/100_core/src_900_xml/gplx/Base85_utl_tst.java @@ -0,0 +1,56 @@ +/* +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; +import org.junit.*; +public class Base85_utl_tst { + @Test public void Log() { + tst_Log( 0, 1); + tst_Log( 84, 1); + tst_Log( 85, 2); + tst_Log( 7224, 2); + tst_Log( 7225, 3); + tst_Log( 614124, 3); + tst_Log( 614125, 4); + tst_Log( 52200624, 4); + tst_Log( 52200625, 5); + tst_Log(Int_.MaxValue, 5); + } void tst_Log(int val, int expd) {Tfds.Eq(expd, Base85_utl.DigitCount(val));} + @Test public void XtoStr() { + tst_XtoStr( 0, "!"); + tst_XtoStr( 84, "u"); + tst_XtoStr( 85, "\"!"); + tst_XtoStr( 7224, "uu"); + tst_XtoStr( 7225, "\"!!"); + tst_XtoStr( 614124, "uuu"); + tst_XtoStr( 614125, "\"!!!"); + tst_XtoStr( 52200624, "uuuu"); + tst_XtoStr( 52200625, "\"!!!!"); + } + void tst_XtoStr(int val, String expd) { + String actl = Base85_utl.XtoStr(val, 0); + Tfds.Eq(expd, actl); + Tfds.Eq(val, Base85_utl.XtoIntByStr(expd)); + } + @Test public void XtoStrAry() { + byte[] ary = new byte[9]; + run_XtoStr(ary, 0, 2); // !!# + run_XtoStr(ary, 3, 173); // !#$ + run_XtoStr(ary, 6, 14709); // #$% + Tfds.Eq("!!#!#$#$%", String_.new_utf8_(ary)); + } void run_XtoStr(byte[] ary, int aryPos, int val) {Base85_utl.XtoStrByAry(val, ary, aryPos, 3);} +} diff --git a/100_core/src_900_xml/gplx/HierStrBldr.java b/100_core/src_900_xml/gplx/HierStrBldr.java new file mode 100644 index 000000000..cb83b09aa --- /dev/null +++ b/100_core/src_900_xml/gplx/HierStrBldr.java @@ -0,0 +1,55 @@ +/* +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; +public class HierStrBldr { + public String Root() {return root;} public HierStrBldr Root_(String v) {root = v; return this;} private String root; + public Io_url RootAsIoUrl() {return Io_url_.new_dir_(root);} + public String DirFmt() {return dirFmt;} private String dirFmt; + public String DirSpr() {return dirSpr;} private String dirSpr = Op_sys.Cur().Fsys_dir_spr_str(); + public String FilFmt() {return filFmt;} private String filFmt; + public String NumFmt() {return numFmt;} private String numFmt; + public int[] FilCountMaxs() {return filCountMaxs;} int[] filCountMaxs; + public Io_url GenStrIdxOnlyAsoUrl(int idx) {return Io_url_.new_fil_(GenStrIdxOnly(idx));} + public String GenStrIdxOnly(int idx) {return GenStr(String_.Ary_empty, idx);} + public Io_url GenStrAsIoUrl(String[] subDirs, int idx) { + return Io_url_.new_fil_(GenStr(subDirs, idx)); + } + String GenStr(String[] subDirs, int idx) { + String_bldr sb = String_bldr_.new_(); + sb.Add(root); + for (String subDir : subDirs) + sb.Add(subDir).Add(dirSpr); + int multiple = 1; + int[] multipleAry = new int[filCountMaxs.length]; + for (int i = filCountMaxs.length - 1; i >= 0; i--) { + multiple *= filCountMaxs[i]; + multipleAry[i] = (idx / multiple) * multiple; // NOTE: rounds down to multiple; EX: 11 -> 10 + } + for (int i = 0; i < multipleAry.length; i++) + sb.Add_fmt(dirFmt, Int_.XtoStr_fmt(multipleAry[i], numFmt)); + sb.Add_fmt(filFmt, Int_.XtoStr_fmt(idx, numFmt)); + return sb.XtoStr(); + } + public HierStrBldr Ctor_io(Io_url root, String dirFmt, String filFmt, String numFmt, int... filCountMaxs) { + this.Ctor(root.Raw(), dirFmt + dirSpr, filFmt, numFmt, filCountMaxs); + return this; + } + public void Ctor(String root, String dirFmt, String filFmt, String numFmt, int... filCountMaxs) { + this.root = root; this.dirFmt = dirFmt; this.filFmt = filFmt; this.numFmt = numFmt; this.filCountMaxs = filCountMaxs; + } +} diff --git a/100_core/src_900_xml/gplx/HierStrBldr_tst.java b/100_core/src_900_xml/gplx/HierStrBldr_tst.java new file mode 100644 index 000000000..e330dfbdc --- /dev/null +++ b/100_core/src_900_xml/gplx/HierStrBldr_tst.java @@ -0,0 +1,47 @@ +/* +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; +import org.junit.*; +import gplx.ios.*; import gplx.texts.*; +public class HierStrBldr_tst { + @Before public void setup() {bldr = new HierStrBldr();} HierStrBldr bldr; + @Test public void Hier0() { + bldr.Ctor("/root/", "dir_{0}/", "idx_{0}.csv", "000"); + tst_MakeName( 0, "/root/idx_000.csv"); + tst_MakeName( 1, "/root/idx_001.csv"); + tst_MakeName(10, "/root/idx_010.csv"); + } + @Test public void Hier1() { + bldr.Ctor("/root/", "dir_{0}/", "idx_{0}.csv", "000", 10); + tst_MakeName( 0, "/root/dir_000/idx_000.csv"); + tst_MakeName( 1, "/root/dir_000/idx_001.csv"); + tst_MakeName(10, "/root/dir_010/idx_010.csv"); + } + @Test public void Hier2() { + bldr.Ctor("/root/", "dir_{0}/", "idx_{0}.csv", "000", 5, 10); + tst_MakeName( 0, "/root/dir_000/dir_000/idx_000.csv"); + tst_MakeName( 1, "/root/dir_000/dir_000/idx_001.csv"); + tst_MakeName( 10, "/root/dir_000/dir_010/idx_010.csv"); + tst_MakeName( 49, "/root/dir_000/dir_040/idx_049.csv"); + tst_MakeName( 50, "/root/dir_050/dir_050/idx_050.csv"); + tst_MakeName( 99, "/root/dir_050/dir_090/idx_099.csv"); + tst_MakeName(100, "/root/dir_100/dir_100/idx_100.csv"); + tst_MakeName(110, "/root/dir_100/dir_110/idx_110.csv"); + } + void tst_MakeName(int val, String expd) {Tfds.Eq(expd, bldr.GenStrIdxOnly(val));} +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlAtr.java b/100_core/src_900_xml/gplx/xmls/XmlAtr.java new file mode 100644 index 000000000..d99b1b427 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlAtr.java @@ -0,0 +1,24 @@ +/* +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.xmls; import gplx.*; +import org.w3c.dom.Node; +public class XmlAtr { + public String Name() {return xatr.getNodeName();} + public String Value() {return xatr.getNodeValue();} + @gplx.Internal protected XmlAtr(Node xatr) {this.xatr = xatr;} Node xatr; +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlAtrList.java b/100_core/src_900_xml/gplx/xmls/XmlAtrList.java new file mode 100644 index 000000000..c4267d824 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlAtrList.java @@ -0,0 +1,37 @@ +/* +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.xmls; import gplx.*; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +public class XmlAtrList { + public int Count() {return list == null ? 0 : list.getLength();} + public String FetchValOr(String key, String or) { + Node xatr = list.getNamedItem(key); + return (xatr == null) ? or : xatr.getNodeValue(); + } + public XmlAtr Fetch(String key) { + Node xatr = list.getNamedItem(key); if (xatr == null) throw Err_arg.notFound_key_("key", key); + return new XmlAtr(xatr); + } + public XmlAtr Fetch_or_null(String key) { + Node xatr = list.getNamedItem(key); if (xatr == null) return null; + return new XmlAtr(xatr); + } + public XmlAtr FetchAt(int i) {return list == null ? null : new XmlAtr(list.item(i));} + @gplx.Internal protected XmlAtrList(NamedNodeMap list) {this.list = list;} NamedNodeMap list; +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlDoc.java b/100_core/src_900_xml/gplx/xmls/XmlDoc.java new file mode 100644 index 000000000..7c2df540a --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlDoc.java @@ -0,0 +1,24 @@ +/* +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.xmls; import gplx.*; +import org.w3c.dom.Document; +public class XmlDoc { + public XmlNde Root() {return new XmlNde(xdoc.getDocumentElement());} + @gplx.Internal protected XmlDoc(Document xdoc) {this.xdoc = xdoc;} Document xdoc; +} +//#} \ No newline at end of file diff --git a/100_core/src_900_xml/gplx/xmls/XmlDoc_.java b/100_core/src_900_xml/gplx/xmls/XmlDoc_.java new file mode 100644 index 000000000..17866ed17 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlDoc_.java @@ -0,0 +1,53 @@ +/* +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.xmls; import gplx.*; +import gplx.Io_url; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +public class XmlDoc_ { + public static XmlDoc parse_(String raw) {return new XmlDoc(doc_(raw));} + static Document doc_(String raw) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder bldr = null; + try {bldr = factory.newDocumentBuilder();} + catch (ParserConfigurationException e) {throw Err_.err_key_(e, XmlDoc_.Err_XmlException, "failed to create newDocumentBuilder");} + StringReader reader = new StringReader(raw); + InputSource source = new InputSource(reader); + Document doc = null; + try {doc = bldr.parse(source);} + catch (SAXException e) {throw Err_.err_key_(e, XmlDoc_.Err_XmlException, "failed to parse xml").Add("raw", raw);} + catch (IOException e) {throw Err_.err_key_(e, XmlDoc_.Err_XmlException, "failed to parse xml").Add("raw", raw);} + return doc; + } + public static final String Err_XmlException = "gplx.xmls.XmlException"; +} +//#} \ No newline at end of file diff --git a/100_core/src_900_xml/gplx/xmls/XmlDoc_tst.java b/100_core/src_900_xml/gplx/xmls/XmlDoc_tst.java new file mode 100644 index 000000000..49a65e7b7 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlDoc_tst.java @@ -0,0 +1,71 @@ +/* +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.xmls; import gplx.*; +import org.junit.*; +public class XmlDoc_tst { + String xml; XmlDoc xdoc; XmlNde xnde; + @Test public void parse_() { + xml = String_.Concat(""); + xdoc = XmlDoc_.parse_(xml); + Tfds.Eq("root", xdoc.Root().Name()); + Tfds.Eq(true, xdoc.Root().NdeType_element()); + } + @Test public void Xml_outer() { + xml = String_.Concat + ( "" + , "" + , "" + , "" + , "" + , "" + ); + xdoc = XmlDoc_.parse_(xml); + xnde = xdoc.Root().SubNdes().FetchAt(0); + Tfds.Eq("a", xnde.Name()); + Tfds.Eq("", xnde.Xml_outer()); + } + @Test public void Text_inner() { + xml = String_.Concat + ( "" + , "" + , "test me" + , "" + , "" + ); + xdoc = XmlDoc_.parse_(xml); + xnde = xdoc.Root().SubNdes().FetchAt(0); + Tfds.Eq("a", xnde.Name()); + Tfds.Eq("test me", xnde.Text_inner()); + } + @Test public void Atrs() { + xml = String_.Concat + ( "" + , "" + ); + xdoc = XmlDoc_.parse_(xml); + XmlAtrList atrs = xdoc.Root().Atrs(); + XmlAtr atr = atrs.FetchAt(1); + tst_Atr(atr, "atr1", "1"); + atr = atrs.FetchAt(1); + tst_Atr(atr, "atr1", "1"); + } + void tst_Atr(XmlAtr atr, String expdName, String expdVal) { + Tfds.Eq(expdName, atr.Name()); + Tfds.Eq(expdVal, atr.Value()); + } +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlFileSplitter.java b/100_core/src_900_xml/gplx/xmls/XmlFileSplitter.java new file mode 100644 index 000000000..cd50ee171 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlFileSplitter.java @@ -0,0 +1,140 @@ +/* +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.xmls; import gplx.*; +import gplx.ios.*; +import gplx.texts.*; +public class XmlFileSplitter { + public XmlFileSplitterOpts Opts() {return opts;} XmlFileSplitterOpts opts = new XmlFileSplitterOpts(); + public byte[] Hdr() {return hdr;} private byte[] hdr; + public void Clear() {hdr = null;} + public void Split(Io_url xmlUrl) { + Io_url partDir = opts.PartDir(); + byte[] xmlEndTagAry = Bry_.new_utf8_(opts.XmlEnd()); + byte[][] nameAry = XtoByteAry(opts.XmlNames()); + int partIdx = 0; + + // bgn reading file + XmlSplitRdr rdr = new XmlSplitRdr().Init_(xmlUrl, opts.FileSizeMax()); + + // split hdr: includes , xmlNamespaces, and any DTD headers; will be prepended to each partFile + rdr.Read(); + int findPos = FindMatchPos(rdr.CurAry(), nameAry); if (findPos == String_.Find_none) throw Err_.new_("could not find any names in first segment"); + byte[] dataAry = SplitHdr(rdr.CurAry(), findPos); + if (opts.XmlBgn() != null) + hdr = Bry_.new_utf8_(opts.XmlBgn()); + byte[] tempAry = new byte[0]; + int newFindPos = FindMatchPosRev(dataAry, nameAry); + findPos = (newFindPos <= findPos) ? String_.Find_none : newFindPos; + boolean first = true; + + // split files + XmlSplitWtr partWtr = new XmlSplitWtr().Init_(partDir, hdr, opts); + while (true) { + partWtr.Bgn(partIdx++); + if (opts.StatusFmt() != null) ConsoleAdp._.WriteLine(String_.Format(opts.StatusFmt(), partWtr.Url().NameOnly())); + partWtr.Write(tempAry); + if (!first) { + rdr.Read(); + dataAry = rdr.CurAry(); + findPos = FindMatchPosRev(dataAry, nameAry); + } + else + first = false; + + // find last closing node + while (findPos == String_.Find_none) { + if (rdr.Done()) { + findPos = rdr.CurRead(); + break; + } + else { + partWtr.Write(dataAry); + rdr.Read(); + dataAry = rdr.CurAry(); + findPos = FindMatchPosRev(dataAry, nameAry); + } + } + + byte[][] rv = SplitRest(dataAry, findPos); + partWtr.Write(rv[0]); + tempAry = rv[1]; + boolean done = rdr.Done() && tempAry.length == 0; + if (!done) + partWtr.Write(xmlEndTagAry); + partWtr.Rls(); + if (done) break; + } + rdr.Rls(); + } + public byte[] SplitHdr(byte[] src, int findPos) { + hdr = new byte[findPos]; + Array_.CopyTo(src, 0, hdr, 0, findPos); + byte[] rv = new byte[src.length - findPos]; + Array_.CopyTo(src, findPos, rv, 0, rv.length); + return rv; + } + public byte[][] SplitRest(byte[] src, int findPos) { + byte[][] rv = new byte[2][]; + rv[0] = new byte[findPos]; + Array_.CopyTo(src, 0, rv[0], 0, findPos); + rv[1] = new byte[src.length - findPos]; + Array_.CopyTo(src, findPos, rv[1], 0, rv[1].length); + return rv; + } + public int FindMatchPos(byte[] src, byte[][] wordAry) {return FindMatchPos(src, wordAry, true);} + public int FindMatchPosRev(byte[] src, byte[][] wordAry) {return FindMatchPos(src, wordAry, false);} + int FindMatchPos(byte[] src, byte[][] wordAry, boolean fwd) { + int[] findAry = new int[wordAry.length]; + for (int i = 0; i < findAry.length; i++) + findAry[i] = fwd ? -1 : Int_.MaxValue; + for (int i = 0; i < wordAry.length; i++) { // look at each word in wordAry + int srcLen = src.length, srcPos, srcEnd, srcDif; + if (fwd) {srcPos = 0; srcEnd = srcLen; srcDif = 1;} + else {srcPos = srcLen - 1; srcEnd = -1; srcDif = -1;} + while (srcPos != srcEnd) { // look at each byte in src + byte[] ary = wordAry[i]; + int aryLen = ary.length, aryPos, aryEnd, aryDif; + if (fwd) {aryPos = 0; aryEnd = aryLen; aryDif = 1;} + else {aryPos = aryLen - 1; aryEnd = -1; aryDif = -1;} + boolean found = true; + while (aryPos != aryEnd) { // look at each byte in word + int lkpPos = srcPos + aryPos; + if (lkpPos >= srcLen) {found = false; break;} // outside bounds; exit + if (ary[aryPos] != src[lkpPos]) {found = false; break;} // srcByte doesn't match wordByte; exit + aryPos += aryDif; + } + if (found) {findAry[i] = srcPos; break;} // result found; stop now and keep "best" result + srcPos += srcDif; + } + } + int best = fwd ? -1 : Int_.MaxValue; + for (int find : findAry) { + if ((fwd && find > best) + || (!fwd && find < best)) + best = find; + } + if (best == Int_.MaxValue) best = -1; + return best; + } + byte[][] XtoByteAry(String[] names) { + byte[][] rv = new byte[names.length][]; + for (int i = 0; i < names.length; i++) + rv[i] = Bry_.new_utf8_(names[i]); + return rv; + } +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlFileSplitterOpts.java b/100_core/src_900_xml/gplx/xmls/XmlFileSplitterOpts.java new file mode 100644 index 000000000..39825f2d8 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlFileSplitterOpts.java @@ -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 . +*/ +package gplx.xmls; import gplx.*; +public class XmlFileSplitterOpts { + public int FileSizeMax() {return fileSizeMax;} public XmlFileSplitterOpts FileSizeMax_(int v) {fileSizeMax = v; return this;} int fileSizeMax = 1024 * 1024; + public String[] XmlNames() {return xmlNames;} public XmlFileSplitterOpts XmlNames_(String... v) {xmlNames = v; return this;} private String[] xmlNames; + public String XmlBgn() {return xmlBgn;} public XmlFileSplitterOpts XmlBgn_(String v) {xmlBgn = v; return this;} private String xmlBgn; + public String XmlEnd() {return xmlEnd;} public XmlFileSplitterOpts XmlEnd_(String v) {xmlEnd = v; return this;} private String xmlEnd; + public Io_url PartDir() {return partDir;} public XmlFileSplitterOpts PartDir_(Io_url v) {partDir = v; return this;} Io_url partDir; + public String StatusFmt() {return statusFmt;} public XmlFileSplitterOpts StatusFmt_(String v) {statusFmt = v; return this;} private String statusFmt = "splitting {0}"; + public HierStrBldr Namer() {return namer;} HierStrBldr namer = new HierStrBldr(); +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlFileSplitter_tst.java b/100_core/src_900_xml/gplx/xmls/XmlFileSplitter_tst.java new file mode 100644 index 000000000..824a9147f --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlFileSplitter_tst.java @@ -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 . +*/ +package gplx.xmls; import gplx.*; +import org.junit.*; +import gplx.ios.*; import gplx.texts.*; +public class XmlFileSplitter_tst { + @Before public void setup() { + splitter = new XmlFileSplitter(); + Io_mgr._.InitEngine_mem(); + } XmlFileSplitter splitter; + @Test public void FindMatchPos() { + tst_FindMatchPos("abcde", "a", 0); + tst_FindMatchPos("abcde", "b", 1); + tst_FindMatchPos("abcde", "cd", 2); + tst_FindMatchPos("abcde", "f", -1); + tst_FindMatchPos("abcde", "fg", -1); + } void tst_FindMatchPos(String src, String find, int expd) {Tfds.Eq(expd, splitter.FindMatchPos(byte_(src), byteAry_(find)));} + @Test public void FindMatchPosRev() { + tst_FindMatchPosRev("abcde", "a", 0); + tst_FindMatchPosRev("abcde", "b", 1); + tst_FindMatchPosRev("abcde", "cd", 2); + tst_FindMatchPosRev("abcde", "f", -1); + tst_FindMatchPosRev("abcde", "ef", -1); + tst_FindMatchPosRev("abcde", "za", -1); + tst_FindMatchPosRev("dbcde", "d", 3); + } void tst_FindMatchPosRev(String src, String find, int expd) {Tfds.Eq(expd, splitter.FindMatchPosRev(byte_(src), byteAry_(find)));} + @Test public void ExtractHdr() { + tst_ExtractHdr("", "", ""); + } + @Test public void Split() { + splitter.Opts().FileSizeMax_(30).XmlNames_(""); + tst_Split + ( "" + , "" + , "" + , "" + ); + tst_Split + ( "" + , "" + , "" + ); + } + void tst_Split(String txt, String... expd) { + Io_url xmlFil = Io_url_.mem_fil_("mem/800_misc/txt.xml"); + Io_url tmpDir = xmlFil.OwnerDir().GenSubDir("temp_xml"); + Io_mgr._.DeleteDirDeep(tmpDir); + splitter.Opts().StatusFmt_(null).PartDir_(tmpDir); + splitter.Opts().Namer().Ctor_io(tmpDir, "", "fil_{0}.xml", "000"); + Io_mgr._.SaveFilStr(xmlFil, txt); + splitter.Split(xmlFil); + Io_url[] tmpFilAry = Io_mgr._.QueryDir_fils(tmpDir); + Tfds.Eq(expd.length, tmpFilAry.length); + for (int i = 0; i < tmpFilAry.length; i++) { + Io_url tmpFil = tmpFilAry[i]; + Tfds.Eq(expd[i], Io_mgr._.LoadFilStr(tmpFil)); + } + } + byte[] byte_(String s) {return Bry_.new_utf8_(s);} + byte[][] byteAry_(String s) { + byte[][] rv = new byte[1][]; + rv[0] = Bry_.new_utf8_(s); + return rv; + } + void tst_ExtractHdr(String src, String find, String expdHdr, String expdSrc) { + splitter.Clear(); + byte[] srcAry = byte_(src); + int findPos = splitter.FindMatchPos(srcAry, byteAry_(find)); + srcAry = splitter.SplitHdr(srcAry, findPos); + Tfds.Eq(String_.new_utf8_(splitter.Hdr()), expdHdr); + Tfds.Eq(String_.new_utf8_(srcAry), expdSrc); + } +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlNde.java b/100_core/src_900_xml/gplx/xmls/XmlNde.java new file mode 100644 index 000000000..7eb5b5e7f --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlNde.java @@ -0,0 +1,51 @@ +/* +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.xmls; import gplx.*; +import java.io.StringWriter; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Node; +public class XmlNde { + public XmlAtrList Atrs() {return new XmlAtrList(xnde.getAttributes());} + public XmlNdeList SubNdes() {return new XmlNdeList_cls_xml(xnde.getChildNodes());} + public String Name() {return xnde.getNodeName();} + public String Xml_outer() { + Transformer transformer = transformer_(); + StringWriter writer = new StringWriter(); + try {transformer.transform(new DOMSource(xnde), new StreamResult(writer));} + catch (TransformerException e) {throw Err_.err_key_(e, XmlDoc_.Err_XmlException, "failed to get xml string");} + return writer.toString(); + } + public String Text_inner() {return xnde.getTextContent();} + public boolean NdeType_element() {return xnde.getNodeType() == Node.ELEMENT_NODE;} + public boolean NdeType_textOrEntityReference() {return xnde.getNodeType() == Node.TEXT_NODE || xnde.getNodeType() == Node.ENTITY_REFERENCE_NODE;} + @gplx.Internal protected XmlNde(Node xnde) {this.xnde = xnde;} Node xnde; + static Transformer transformer_() { + TransformerFactory transformerfactory = TransformerFactory.newInstance(); + Transformer transformer = null; + try {transformer = transformerfactory.newTransformer();} + catch (TransformerConfigurationException e) {throw Err_.err_key_(e, XmlDoc_.Err_XmlException, "failed to get create transformer");} + transformer.setOutputProperty("omit-xml-declaration", "yes"); + return transformer; + } + } diff --git a/100_core/src_900_xml/gplx/xmls/XmlNdeList.java b/100_core/src_900_xml/gplx/xmls/XmlNdeList.java new file mode 100644 index 000000000..bbd657766 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlNdeList.java @@ -0,0 +1,35 @@ +/* +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.xmls; import gplx.*; +import org.w3c.dom.NodeList; +public interface XmlNdeList { + int Count(); + XmlNde FetchAt(int i); +} +class XmlNdeList_cls_xml implements XmlNdeList { + public int Count() {return list.getLength();} + public XmlNde FetchAt(int i) {return new XmlNde(list.item(i));} + @gplx.Internal protected XmlNdeList_cls_xml(NodeList list) {this.list = list;} NodeList list; +} +class XmlNdeList_cls_list implements XmlNdeList { + public int Count() {return list.Count();} + public XmlNde FetchAt(int i) {return (XmlNde)list.FetchAt(i);} + public void Add(XmlNde xnde) {list.Add(xnde);} + @gplx.Internal protected XmlNdeList_cls_list(int count) {list = ListAdp_.new_(); list.ResizeBounds(count);} ListAdp list; +} +//#} \ No newline at end of file diff --git a/100_core/src_900_xml/gplx/xmls/XmlSplitRdr.java b/100_core/src_900_xml/gplx/xmls/XmlSplitRdr.java new file mode 100644 index 000000000..947f81822 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlSplitRdr.java @@ -0,0 +1,51 @@ +/* +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.xmls; import gplx.*; +import gplx.ios.*; +public class XmlSplitRdr { + public byte[] CurAry() {return curAry;} private byte[] curAry; + public long CurSum() {return curSum;} long curSum; + public int CurRead() {return curRead;} int curRead; + public boolean Done() {return done;} private boolean done; + public XmlSplitRdr InitAll_(Io_url url) { + stream = Io_mgr._.OpenStreamRead(url); + curLen = stream.Len(); + curAry = new byte[(int)curLen]; + curSum = 0; + curRead = 0; + done = false; + return this; + } + public XmlSplitRdr Init_(Io_url url, int curArySize) { + stream = Io_mgr._.OpenStreamRead(url); + curLen = Io_mgr._.QueryFil(url).Size(); + curAry = new byte[curArySize]; + curSum = 0; + curRead = 0; + done = false; + return this; + } IoStream stream; long curLen; + public void Read() { + curRead = stream.ReadAry(curAry); + curSum += curRead; + done = curSum == curLen; + if (done && curRead != curAry.length) // on last pass, readAry may have garbage at end, remove + curAry = (byte[])Bry_.Resize_manual(curAry, curRead); + } + public void Rls() {stream.Rls();} +} diff --git a/100_core/src_900_xml/gplx/xmls/XmlSplitWtr.java b/100_core/src_900_xml/gplx/xmls/XmlSplitWtr.java new file mode 100644 index 000000000..a4ca7afd1 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/XmlSplitWtr.java @@ -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 . +*/ +package gplx.xmls; import gplx.*; +import gplx.ios.*; +public class XmlSplitWtr { + public Io_url Url() {return url;} Io_url url; + public XmlSplitWtr Init_(Io_url partDir, byte[] hdr, XmlFileSplitterOpts opts) { + this.partDir = partDir; this.hdr = hdr; this.opts = opts; + return this; + } + public void Bgn(int partIdx) { + String partStr = opts.Namer().GenStrIdxOnly(partIdx); + url = Io_url_.mem_fil_(partStr); + stream = Io_mgr._.OpenStreamWrite(url); + init = true; + } boolean init = true; byte[] hdr; XmlFileSplitterOpts opts; Io_url partDir; IoStream stream; + public void Write(byte[] ary) { + if (init) { + stream.WriteAry(hdr); + init = false; + } + stream.WriteAry(ary); + } + public void Rls() {stream.Rls();} +} diff --git a/100_core/src_900_xml/gplx/xmls/Xpath_.java b/100_core/src_900_xml/gplx/xmls/Xpath_.java new file mode 100644 index 000000000..6c4398250 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/Xpath_.java @@ -0,0 +1,105 @@ +/* +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.xmls; import gplx.*; +public class Xpath_ { + public static XmlNdeList SelectAll(XmlNde owner, String xpath) {return Select(owner, xpath, Xpath_Args.all_());} + public static XmlNde SelectFirst(XmlNde owner, String xpath) { + XmlNdeList rv = Select(owner, xpath, Xpath_Args.first_()); + return rv.Count() == 0 ? null : rv.FetchAt(0); // selects first + } + public static XmlNdeList SelectElements(XmlNde owner) { + XmlNdeList subNdes = owner.SubNdes(); int count = subNdes.Count(); + XmlNdeList_cls_list list = new XmlNdeList_cls_list(count); + for (int i = 0; i < count; i++) { + XmlNde sub = subNdes.FetchAt(i); + if (sub.NdeType_element()) + list.Add(sub); + } + return list; + } + static XmlNdeList Select(XmlNde owner, String xpath, Xpath_Args args) { + XmlNdeList_cls_list rv = new XmlNdeList_cls_list(ListAdp_.Capacity_initial); + String[] parts = String_.Split(xpath, "/"); + TraverseSubs(owner, parts, 0, rv, args); + return rv; + } + static void TraverseSubs(XmlNde owner, String[] parts, int depth, XmlNdeList_cls_list results, Xpath_Args args) { + int partsLen = Array_.Len(parts); + if (depth == partsLen) return; + String name = parts[depth]; + XmlNdeList subNdes = owner.SubNdes(); int count = subNdes.Count(); + for (int i = 0; i < count; i++) { + XmlNde sub = subNdes.FetchAt(i); + if (args.Cancel) return; + if (!String_.Eq(name, sub.Name())) continue; + if (depth == partsLen - 1) { + results.Add(sub); + if (args.SelectFirst) args.Cancel = true; + } + else + TraverseSubs(sub, parts, depth + 1, results, args); + } + } + public static final String InnetTextKey = "&innerText"; + public static KeyValHash ExtractKeyVals(String xml, Int_obj_ref posRef, String nodeName) { + int pos = posRef.Val(); + Err xmlErr = Err_.new_("error parsing xml").Add("xml", xml).Add("pos", pos); + String headBgnFind = "<" + nodeName + " "; int headBgnFindLen = String_.Len(headBgnFind); + int headBgn = String_.FindFwd(xml, headBgnFind, pos); if (headBgn == String_.Find_none) return null; + int headEnd = String_.FindFwd(xml, ">", headBgn + headBgnFindLen); if (headEnd == String_.Find_none) throw xmlErr; + String atrXml = String_.Mid(xml, headBgn, headEnd); + KeyValHash rv = ExtractNodeVals(atrXml, xmlErr); + boolean noInnerText = String_.CharAt(xml, headEnd - 1) == '/'; // if />, then no inner text + if (!noInnerText) { + int tail = String_.FindFwd(xml, "", headBgn); if (tail == String_.Find_none) throw xmlErr.Hdr_("could not find tailPos").Add("headBgn", headBgn); + String innerText = String_.Mid(xml, headEnd + 1, tail); + rv.Add(InnetTextKey, innerText); + } + posRef.Val_(headEnd); + return rv; + } + static KeyValHash ExtractNodeVals(String xml, Err xmlErr) { + KeyValHash rv = KeyValHash.new_(); + int pos = 0; + while (true) { + int eqPos = String_.FindFwd(xml, "=", pos); if (eqPos == String_.Find_none) break; + int q0Pos = String_.FindFwd(xml, "\"", eqPos + 1); if (q0Pos == String_.Find_none) throw xmlErr.Add("eqPos", eqPos); + int q1Pos = String_.FindFwd(xml, "\"", q0Pos + 1); if (q1Pos == String_.Find_none) throw xmlErr.Add("q1Pos", q1Pos); + int spPos = eqPos - 1; + while (spPos > -1) { + char c = String_.CharAt(xml, spPos); + if (Char_.IsWhitespace(c)) break; + spPos--; + } + if (spPos == String_.Find_none) throw xmlErr.Hdr_("could not find hdr").Add("eqPos", eqPos); + String key = String_.Mid(xml, spPos + 1, eqPos); + String val = String_.Mid(xml, q0Pos + 1, q1Pos); + rv.Add(key, val); + pos = q1Pos; + } + return rv; + } +} +class Xpath_Args { + public boolean SelectFirst; // false=SelectAll + public boolean Cancel; + public static Xpath_Args all_() {return new Xpath_Args(false);} + public static Xpath_Args first_() {return new Xpath_Args(true);} + Xpath_Args(boolean selectFirst) {this.SelectFirst = selectFirst;} +} +enum Xpath_SelectMode {All, First} diff --git a/100_core/src_900_xml/gplx/xmls/Xpath__tst.java b/100_core/src_900_xml/gplx/xmls/Xpath__tst.java new file mode 100644 index 000000000..e83f1fec7 --- /dev/null +++ b/100_core/src_900_xml/gplx/xmls/Xpath__tst.java @@ -0,0 +1,44 @@ +/* +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.xmls; import gplx.*; +import org.junit.*; +public class Xpath__tst { + @Test public void Select_all() { + String xml = String_.Concat + ( "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + ); + tst_SelectAll(xml, "a", 2); + tst_SelectAll(xml, "b", 1); + tst_SelectAll(xml, "b/c", 3); + } + void tst_SelectAll(String raw, String xpath, int expdCount) { + XmlDoc xdoc = XmlDoc_.parse_(raw); + XmlNdeList xndeList = Xpath_.SelectAll(xdoc.Root(), xpath); + Tfds.Eq(expdCount, xndeList.Count()); + } +} diff --git a/100_core/tst/gplx/EnmParser_tst.java b/100_core/tst/gplx/EnmParser_tst.java new file mode 100644 index 000000000..5fbc879db --- /dev/null +++ b/100_core/tst/gplx/EnmParser_tst.java @@ -0,0 +1,60 @@ +/* +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; +import org.junit.*; +public class EnmParser_tst { + @Before public void setup() { + parser = EnmMgr.new_(); + } + @Test public void Basic() { // 1,2,4,8 + parser.BitRngEnd_(8); + run_Reg(0, "zero"); + run_Reg(1, "one"); + run_Reg(2, "two"); + run_Reg(4, "four"); + run_Reg(8, "eight"); + + tst_Convert("zero", 0); + tst_Convert("one", 1); + tst_Convert("eight", 8); + tst_Convert("one+eight", 9); + } + @Test public void Keys() { + parser.BitRngBgn_(65536).BitRngEnd_(262144); + run_Reg( 65, "a"); + run_Reg( 65536, "shift"); + run_Reg(131072, "ctrl"); + run_Reg(262144, "alt"); + tst_Convert("a", 65); + tst_Convert("shift+a", 65 + 65536); + tst_Convert("ctrl+a", 65 + 131072); + tst_Convert("shift+ctrl+a", 65 + 65536 + 131072); + } + @Test public void Prefix() { + parser.Prefix_("key.").BitRngBgn_(128).BitRngEnd_(128); + run_Reg(65, "a"); + tst_Convert("key.a", 65); + } + void run_Reg(int i, String s) {parser.RegObj(i, s, "NULL");} + void tst_Convert(String raw, int val) { + int actlVal = parser.GetVal(raw); + Tfds.Eq(val, actlVal); + Tfds.Eq(raw, parser.GetStr(val)); + } + EnmMgr parser; +} diff --git a/100_core/tst/gplx/ErrMock_tst.java b/100_core/tst/gplx/ErrMock_tst.java new file mode 100644 index 000000000..f292e2543 --- /dev/null +++ b/100_core/tst/gplx/ErrMock_tst.java @@ -0,0 +1,62 @@ +/* +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; +import org.junit.*; +public class ErrMock_tst { // NOTE: ErrMock_tst name important b/c gplx java code will ignore all stacks with gplx.Err_ + @Before public void setup() {} Err err = null; + @Test public void Basic() { + err = Err_.new_key_("gplx.MockFail", "mock fail"); + try {throw err;} catch (Exception e) {err = Err.convert_(e);} + tst_Key("gplx.MockFail").tst_Msg("mock fail").tst_ProcName("gplx.ErrMock_tst.Basic"); + } + @Test public void Args() { + err = Err_.new_key_("gplx.MockFail", "mock fail").Add("a", 1).Add("b", 2); + try {throw err;} catch (Exception e) {err = Err.convert_(e);} + tst_Arg(0, "a", 1).tst_Arg(1, "b", 2); + } +// @Test public void PrintAll() { +// String actl = ""; +// try {FailedMethod();} +// catch (Exception e) { +// actl = Err_.Message_gplx(e); +// } +// Tfds.Eq_ary_str(String_.Split(String_.Concat_lines_crlf( +// // " mock fail " +// ," @a 1" +// ," @b 2" +// ," gplx.ErrMock_tst.FailedMethod()" +// ,"-----------------------------------" +// ," gplx.ErrMock_tst.PrintAll()" +// ," c:\\000\\200.dev\\100.gplx\\100.framework\\100.core\\gplx\\tst\\gplx\\errmock_tst.cs(18,0)" +// ," gplx.ErrMock_tst.FailedMethod()" +// ," c:\\000\\200.dev\\100.gplx\\100.framework\\100.core\\gplx\\tst\\gplx\\errmock_tst.cs(37,0)"), String_.NewLine), +// // String_.Split(actl, String_.CrLf)); +// } + void FailedMethod() { + throw Err_.new_key_("gplx.MockFail", "mock fail").Add("a", 1).Add("b", 2); + } + + ErrMock_tst tst_Key(String expd) {Tfds.Eq(expd, err.Key()); return this;} + ErrMock_tst tst_Msg(String expd) {Tfds.Eq(expd, err.Hdr()); return this;} + ErrMock_tst tst_ProcName(String expd) {Tfds.Eq(expd, err.Proc().SignatureRaw()); return this;} + ErrMock_tst tst_Arg(int i, String expdKey, Object expdVal) { + KeyVal actl = (KeyVal)err.Args().FetchAt(i); + KeyVal expd = KeyVal_.new_(expdKey, expdVal); + Tfds.Eq(expd.XtoStr(), actl.XtoStr()); return this; + } +} diff --git a/100_core/tst/gplx/ErrMsgWtr_tst.java b/100_core/tst/gplx/ErrMsgWtr_tst.java new file mode 100644 index 000000000..41c04d259 --- /dev/null +++ b/100_core/tst/gplx/ErrMsgWtr_tst.java @@ -0,0 +1,72 @@ +/* +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; +import org.junit.*; +public class ErrMsgWtr_tst { + @Test public void Rethrow() { + tst(new RethrowExample() + , "0 failed run " + , " gplx.ErrMsgWtr_tst$RethrowExample.Run" + , "1 failed proc2 " + , " gplx.ErrMsgWtr_tst$RethrowExample.Proc1" + , "2 failed proc1; key=key val=123:0 " + , " gplx.ErrMsgWtr_tst$RethrowExample.Proc2" + , "-----------------------------------" + , " gplx.ErrMsgWtr_tst.Rethrow(ErrMsgWtr_tst.java:5)" + , " gplx.ErrMsgWtr_tst.tst(ErrMsgWtr_tst.java:24)" + , "0 gplx.ErrMsgWtr_tst$RethrowExample.Run(ErrMsgWtr_tst.java:35)" + , "1 gplx.ErrMsgWtr_tst$RethrowExample.Proc1(ErrMsgWtr_tst.java:36)" + , "2 gplx.ErrMsgWtr_tst$RethrowExample.Proc2(ErrMsgWtr_tst.java:37)" + , "" + ); + } + void tst(ErrExample o, String... expdAry) { +// try {o.Run();} +// catch (Exception exc) { +// String actlMsg = ErrMsgWtr._.Message_gplx(exc); +// String[] actlAry = String_.Split(actlMsg, String_.CrLf); +// Tfds.Eq_ary_str(expdAry, actlAry); //Tfds.Write(String_.CrLf + actlMsg); +// return; +// } +// Tfds.Fail_expdError(); + } + interface ErrExample {void Run();} + class RethrowExample implements ErrExample { + public void Run() {try {Proc1();} catch(Exception exc) {throw Err_.err_(exc, "failed run");} } + public void Proc1() {try {Proc2();} catch(Exception exc) {throw Err_.err_(exc, "failed proc2");} } + public void Proc2() {throw Err_.new_key_("gplx.parse", "failed proc1");} + } + @Test public void Deep() { + tst(new DeepExample() + , "0 failed proc1; key=key val=123:0 " + , " gplx.ErrMsgWtr_tst$DeepExample.Proc2" + , "-----------------------------------" + , " gplx.ErrMsgWtr_tst.Deep(ErrMsgWtr_tst.java:40)" + , " gplx.ErrMsgWtr_tst.tst(ErrMsgWtr_tst.java:24)" + , " gplx.ErrMsgWtr_tst$DeepExample.Run(ErrMsgWtr_tst.java:55)" + , " gplx.ErrMsgWtr_tst$DeepExample.Proc1(ErrMsgWtr_tst.java:56)" + , "0 gplx.ErrMsgWtr_tst$DeepExample.Proc2(ErrMsgWtr_tst.java:57)" + , "" + ); + } + class DeepExample implements ErrExample { + public void Run() {Proc1();} + public void Proc1() {Proc2();} + public void Proc2() {throw Err_.new_key_("gplx.parse", "failed proc1");} + } +} diff --git a/100_core/tst/gplx/GfoMsg_rdr_tst.java b/100_core/tst/gplx/GfoMsg_rdr_tst.java new file mode 100644 index 000000000..edd5c6c7f --- /dev/null +++ b/100_core/tst/gplx/GfoMsg_rdr_tst.java @@ -0,0 +1,57 @@ +/* +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; +import org.junit.*; +public class GfoMsg_rdr_tst { + @Before public void setup() { + msg = msg_().Add("a", "1").Add("b", "2").Add("c", "3"); + ctx.Match("init", "init"); + } GfoMsg msg; GfsCtx ctx = GfsCtx.new_(); + @Test public void Key() { + tst_Msg(msg, "a", "1"); + tst_Msg(msg, "b", "2"); + tst_Msg(msg, "c", "3"); + tst_Msg(msg, "d", null); + } + @Test public void Pos() { + msg = msg_().Add("", "1").Add("", "2").Add("", "3"); + tst_Msg(msg, "", "1"); + tst_Msg(msg, "", "2"); + tst_Msg(msg, "", "3"); + tst_Msg(msg, "", null); + } + @Test public void OutOfOrder() { + tst_Msg(msg, "c", "3"); + tst_Msg(msg, "b", "2"); + tst_Msg(msg, "a", "1"); + } + @Test public void Key3_Pos1_Pos2() { + msg = msg_().Add("", "1").Add("", "2").Add("c", "3"); + tst_Msg(msg, "c", "3"); + tst_Msg(msg, "", "1"); + tst_Msg(msg, "", "2"); + } + @Test public void MultipleEmpty() { + msg = msg_().Add("", "1").Add("", "2").Add("", "3"); + tst_Msg(msg, "", "1"); + tst_Msg(msg, "", "2"); + tst_Msg(msg, "", "3"); + } + GfoMsg msg_() {return GfoMsg_.new_parse_("test");} + void tst_Msg(GfoMsg m, String k, String expd) {Tfds.Eq(expd, m.ReadStrOr(k, null));} +} diff --git a/100_core/tst/gplx/GfoTreeBldr_fxt.java b/100_core/tst/gplx/GfoTreeBldr_fxt.java new file mode 100644 index 000000000..6fdd61dd3 --- /dev/null +++ b/100_core/tst/gplx/GfoTreeBldr_fxt.java @@ -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 . +*/ +package gplx; +public class GfoTreeBldr_fxt { + public ListAdp Atrs() {return atrs;} ListAdp atrs = ListAdp_.new_(); + public ListAdp Subs() {return subs;} ListAdp subs = ListAdp_.new_(); + public GfoTreeBldr_fxt atr_(Object key, Object val) { + atrs.Add(new Object[] {key, val}); + return this; + } + public GfoTreeBldr_fxt sub_(GfoTreeBldr_fxt... ary) { + for (GfoTreeBldr_fxt sub : ary) + subs.Add(sub); + return this; + } + public static GfoTreeBldr_fxt new_() {return new GfoTreeBldr_fxt();} GfoTreeBldr_fxt() {} +} diff --git a/100_core/tst/gplx/TfdsTstr_fxt.java b/100_core/tst/gplx/TfdsTstr_fxt.java new file mode 100644 index 000000000..835116cbe --- /dev/null +++ b/100_core/tst/gplx/TfdsTstr_fxt.java @@ -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 . +*/ +package gplx; +import gplx.lists.*; +public class TfdsTstr_fxt { + public TfdsTstr_fxt Eq_str(Object expd, Object actl, String name) { + int nameLen = String_.Len(name); if (nameLen > nameLenMax) nameLenMax = nameLen; + TfdsTstrItm itm = TfdsTstrItm.new_().Expd_(expd).Actl_(actl).Name_(name); + list.Add(itm); + return this; + } + public void SubName_push(String s) { + stack.Push(s); + TfdsTstrItm itm = TfdsTstrItm.new_(); + itm.SubName_make(stack); + itm.TypeOf = 1; + list.Add(itm); + } StackAdp stack = StackAdp_.new_(); + public void Fail() { + manualFail = true; + }boolean manualFail = false; + public int List_Max(ListAdp expd, ListAdp actl) {return Math_.Max(expd.Count(), actl.Count());} + public int List_Max(String[] expd, String[] actl) {return Math_.Max(expd.length, actl.length);} + public Object List_FetchAtOrNull(ListAdp l, int i) {return (i >= l.Count()) ? null : l.FetchAt(i);} + + public void SubName_pop() {stack.Pop();} + int nameLenMax = 0; + public void tst_Equal(String hdr) { + boolean pass = true; + for (int i = 0; i < list.Count(); i++) { + TfdsTstrItm itm = (TfdsTstrItm)list.FetchAt(i); + if (!itm.Compare()) pass = false; // don't break early; Compare all vals + } + if (pass && !manualFail) return; + String_bldr sb = String_bldr_.new_(); + sb.Add_char_crlf(); + sb.Add_str_w_crlf(hdr); + for (int i = 0; i < list.Count(); i++) { + TfdsTstrItm itm = (TfdsTstrItm)list.FetchAt(i); + if (itm.TypeOf == 1) { + sb.Add_fmt_line(" /{0}", itm.SubName()); + continue; + } + boolean hasError = itm.CompareResult() != TfdsTstrItm.CompareResult_eq; + String errorKey = hasError ? "*" : " "; + sb.Add_fmt_line("{0}{1} {2}", errorKey, String_.PadEnd(itm.Name(), nameLenMax, " "), itm.Expd()); + if (hasError) + sb.Add_fmt_line("{0}{1} {2}", errorKey, String_.PadEnd("", nameLenMax, " "), itm.Actl()); + } + sb.Add(String_.Repeat("_", 80)); + throw Err_.new_(sb.XtoStr()); + } + ListAdp list = ListAdp_.new_(); + public static TfdsTstr_fxt new_() {return new TfdsTstr_fxt();} TfdsTstr_fxt() {} +} +class TfdsTstrItm { + public String Name() {return name;} public TfdsTstrItm Name_(String val) {name = val; return this;} private String name; + public Object Expd() {return expd;} public TfdsTstrItm Expd_(Object val) {expd = val; return this;} Object expd; + public Object Actl() {return actl;} public TfdsTstrItm Actl_(Object val) {actl = val; return this;} Object actl; + public String SubName() {return subName;} private String subName = ""; + public int TypeOf; + public void SubName_make(StackAdp stack) { + if (stack.Count() == 0) return; + ListAdp list = stack.XtoList(); + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < list.Count(); i++) { + if (i != 0) sb.Add("."); + sb.Add((String)list.FetchAt(i)); + } + subName = sb.XtoStr(); + } + public int CompareResult() {return compareResult;} public TfdsTstrItm CompareResult_(int val) {compareResult = val; return this;} int compareResult; + public boolean Compare() { + boolean eq = Object_.Eq(expd, actl); + compareResult = eq ? 1 : 0; + return eq; + } + public String CompareSym() { + return compareResult == 1 ? "==" : "!="; + } + public static TfdsTstrItm new_() {return new TfdsTstrItm();} TfdsTstrItm() {} + public static final int CompareResult_none = 0, CompareResult_eq = 1, CompareResult_eqn = 2; +} diff --git a/100_core/tst/gplx/ios/IoEngineFxt.java b/100_core/tst/gplx/ios/IoEngineFxt.java new file mode 100644 index 000000000..42cfa6924 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngineFxt.java @@ -0,0 +1,54 @@ +/* +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.ios; import gplx.*; +public class IoEngineFxt { + IoEngine EngineOf(Io_url url) {return IoEnginePool._.Fetch(url.Info().EngineKey());} + public void tst_ExistsPaths(boolean expd, Io_url... ary) { + for (Io_url fil : ary) { + if (fil.Type_dir()) + Tfds.Eq(expd, EngineOf(fil).ExistsDir(fil), "ExistsDir failed; dir={0}", fil); + else + Tfds.Eq(expd, EngineOf(fil).ExistsFil_api(fil), "ExistsFil failed; fil={0}", fil); + } + } + public void tst_LoadFilStr(Io_url fil, String expd) {Tfds.Eq(expd, EngineOf(fil).LoadFilStr(IoEngine_xrg_loadFilStr.new_(fil)));} + public void run_SaveFilText(Io_url fil, String expd) {EngineOf(fil).SaveFilText_api(IoEngine_xrg_saveFilStr.new_(fil, expd));} + public void run_UpdateFilModifiedTime(Io_url fil, DateAdp modifiedTime) {EngineOf(fil).UpdateFilModifiedTime(fil, modifiedTime);} + public void tst_QueryFilReadOnly(Io_url fil, boolean expd) {Tfds.Eq(expd, EngineOf(fil).QueryFil(fil).ReadOnly());} + public IoEngineFxt tst_QueryFil_size(Io_url fil, long expd) {Tfds.Eq(expd, EngineOf(fil).QueryFil(fil).Size()); return this;} + public IoEngineFxt tst_QueryFil_modifiedTime(Io_url fil, DateAdp expd) {Tfds.Eq_date(expd, EngineOf(fil).QueryFil(fil).ModifiedTime()); return this;} + public IoItmDir tst_ScanDir(Io_url dir, Io_url... expd) { + IoItmDir dirItem = EngineOf(dir).QueryDir(dir); + Io_url[] actl = new Io_url[dirItem.SubDirs().Count() + dirItem.SubFils().Count()]; + for (int i = 0; i < dirItem.SubDirs().Count(); i++) { + IoItmDir subDir = IoItmDir_.as_(dirItem.SubDirs().FetchAt(i)); + actl[i] = subDir.Url(); + } + for (int i = 0; i < dirItem.SubFils().Count(); i++) { + IoItmFil subFil = IoItmFil_.as_(dirItem.SubFils().FetchAt(i)); + actl[i + dirItem.SubDirs().Count()] = subFil.Url(); + } + Tfds.Eq_ary_str(expd, actl); + return dirItem; + } + public static IoEngineFxt new_() { + IoEngineFxt rv = new IoEngineFxt(); + return rv; + } + public IoEngineFxt() {} +} diff --git a/100_core/tst/gplx/ios/IoEngine_dir_basic_base.java b/100_core/tst/gplx/ios/IoEngine_dir_basic_base.java new file mode 100644 index 000000000..c80d93e92 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_dir_basic_base.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public abstract class IoEngine_dir_basic_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + } protected IoEngine engine; @gplx.Internal protected IoEngineFxt fx; protected Io_url fil, root; + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + @Test @gplx.Virtual public void CreateDir() { + fx.tst_ExistsPaths(false, root); + + engine.CreateDir(root); + fx.tst_ExistsPaths(true, root); + } + @Test public void DeleteDir() { + engine.CreateDir(root); + fx.tst_ExistsPaths(true, root); + + engine.DeleteDir(root); + fx.tst_ExistsPaths(false, root); + } + @Test public void CreateDir_createAllOwners() { + Io_url subDir = root.GenSubDir_nest("sub1"); + fx.tst_ExistsPaths(false, subDir, subDir.OwnerDir()); + + engine.CreateDir(subDir); + fx.tst_ExistsPaths(true, subDir, subDir.OwnerDir()); + } +// @Test public void DeleteDir_missing_fail() { +// try {engine.DeleteDir(root);} +// catch {return;} +// Tfds.Fail_expdError(); +// } + @Test public void DeleteDir_missing_pass() { + engine.DeleteDir(root); + } + @Test @gplx.Virtual public void ScanDir() { + Io_url fil = root.GenSubFil("fil1.txt"); fx.run_SaveFilText(fil, "test"); + Io_url dir1 = root.GenSubDir_nest("dir1"); engine.CreateDir(dir1); + Io_url dir1_1 = dir1.GenSubDir_nest("dir1_1"); engine.CreateDir(dir1_1); // NOTE: QueryDir should not recurse by default; dir1_1 should not be returned below + + fx.tst_ScanDir(root, dir1, fil); + } + @Test public void MoveDir() { + Io_url src = root.GenSubDir_nest("src"), trg = root.GenSubDir_nest("trg"); + engine.CreateDir(src); + fx.tst_ExistsPaths(true, src); fx.tst_ExistsPaths(false, trg); + + engine.MoveDir(src, trg); + fx.tst_ExistsPaths(false, src); fx.tst_ExistsPaths(true, trg); +} +@Test @gplx.Virtual public void CopyDir() { + Io_url src = root.GenSubDir_nest("src"), trg = root.GenSubDir_nest("trg"); + engine.CreateDir(src); + fx.tst_ExistsPaths(true, src); fx.tst_ExistsPaths(false, trg); + + engine.CopyDir(src, trg); + fx.tst_ExistsPaths(true, src, trg); +} +} diff --git a/100_core/tst/gplx/ios/IoEngine_dir_basic_memory_tst.java b/100_core/tst/gplx/ios/IoEngine_dir_basic_memory_tst.java new file mode 100644 index 000000000..d11cae4f3 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_dir_basic_memory_tst.java @@ -0,0 +1,24 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_dir_basic_memory_tst extends IoEngine_dir_basic_base { + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem"); + } @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} +} diff --git a/100_core/tst/gplx/ios/IoEngine_dir_basic_system_tst.java b/100_core/tst/gplx/ios/IoEngine_dir_basic_system_tst.java new file mode 100644 index 000000000..dcb87fe90 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_dir_basic_system_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoEngine_dir_basic_system_tst extends IoEngine_dir_basic_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + IoEngine_xrg_deleteDir.new_(root).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_system.new_();} + @Test @Override public void ScanDir() { + super.ScanDir(); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_dir_deep_base.java b/100_core/tst/gplx/ios/IoEngine_dir_deep_base.java new file mode 100644 index 000000000..ed5057826 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_dir_deep_base.java @@ -0,0 +1,126 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public abstract class IoEngine_dir_deep_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + setup_paths(); + setup_objs(); + } protected IoEngine engine; protected Io_url fil, root; @gplx.Internal protected IoEngineFxt fx; + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + @Test @gplx.Virtual public void SearchDir() { + Io_url[] expd = paths_(src_dir0a, src_fil0a, src_dir0a_dir0a, src_dir0a_fil0a); + Io_url[] actl = IoEngine_xrg_queryDir.new_(src).Recur_().DirInclude_().ExecAsUrlAry(); + Tfds.Eq_ary(expd, actl); + } + @Test @gplx.Virtual public void MoveDirDeep() { + fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); + + engine.MoveDirDeep(IoEngine_xrg_xferDir.move_(src, trg).Recur_()); + fx.tst_ExistsPaths(false, srcTree); + fx.tst_ExistsPaths(true, trgTree); + } + @Test @gplx.Virtual public void CopyDir() { + fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); + + engine.CopyDir(src, trg); + fx.tst_ExistsPaths(true, srcTree); + fx.tst_ExistsPaths(true, trgTree); + } + @Test @gplx.Virtual public void DeleteDir() { + fx.tst_ExistsPaths(true, srcTree); + + engine.DeleteDirDeep(IoEngine_xrg_deleteDir.new_(src).Recur_()); + fx.tst_ExistsPaths(false, srcTree); + } +// @Test public virtual void CopyDir_IgnoreExisting() { +// fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); +// engine.SaveFilStr(trg_dir0a_fil0a, "x"); // NOTE: this file is different than src counterpart; should be overwritten by Copy +// fx.tst_ExistsPaths(true, trg_dir0a, trg_dir0a_fil0a); +// +// engine.CopyDir(src, trg); +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(true, trgTree); +// } +// @Test public virtual void CopyDir_IgnoreExistingReadOnlyFile() { +// fx.tst_ExistsPaths(true, srcTree); fx.tst_ExistsPaths(false, trgTree); +// engine.SaveFilStr(trg_fil0a, "x"); // NOTE: this file is different than src counterpart; should be overwritten by Copy +// fx.tst_ExistsPaths(true, trg_fil0a); +// engine.UpdateFilAttrib(trg_fil0a, IoItmAttrib.ReadOnlyFile); +// +// engine.CopyDir(src, trg); +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(true, trgTree); +// } +// @Test public void MoveDir_IgnoreExisting() { +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(false, trgTree); +// engine.SaveFilStr(trg_dir0a_fil0a, @"x"); // NOTE: this file is different than src counterpart; should be overwritten by Copy +// fx.tst_ExistsPaths(true, trg_dir0a, trg_dir0a_fil0a); +// +// engine.MoveDir(src, trg); +// +// fx.tst_ExistsPaths(true, srcTree); +// fx.tst_ExistsPaths(true, trgTree); +// } +// @Test public virtual void ProgressUi() { +// ConsoleDlg_dev dialog = ConsoleDlg_dev.new_(); +// engine.SearchDir(src).Recur_().Prog_(dialog).ExecAsDir(); +// +// Tfds.Eq(dialog.Written.Count, 3); // 3 levels +// tst_(dialog, 0, "scan", src); +// tst_(dialog, 1, "scan", src_dir0a); +// tst_(dialog, 2, "scan", src_dir0a_dir0a); +// } +// void tst_(ConsoleDlg_dev dialog, int i, String s, Io_url root) { +// Object o = dialog.Written.FetchAt(i); +// IoStatusArgs args = (IoStatusArgs)o; +// Tfds.Eq(s, args.Op); +// Tfds.Eq(root, args.Path); +// } + protected Io_url src, src_dir0a, src_dir0a_dir0a; + Io_url src_fil0a, src_dir0a_fil0a; + protected Io_url trg, trg_dir0a, trg_dir0a_dir0a; + Io_url trg_fil0a, trg_dir0a_fil0a; + Io_url[] srcTree, trgTree; + Io_url[] paths_(Io_url... ary) {return ary;} + protected void setup_paths() { + src = root.GenSubDir_nest("src"); + src_dir0a = root.GenSubDir_nest("src", "dir0a"); + src_dir0a_dir0a = root.GenSubDir_nest("src", "dir0a", "dir0a"); + src_fil0a = root.GenSubFil_nest("src", "fil0a.txt"); + src_dir0a_fil0a = root.GenSubFil_nest("src", "dir0a", "fil0a.txt"); + trg = root.GenSubDir_nest("trg"); + trg_dir0a = root.GenSubDir_nest("trg", "dir0a"); + trg_dir0a_dir0a = root.GenSubDir_nest("trg", "dir0a", "dir0a"); + trg_fil0a = root.GenSubFil_nest("trg", "fil0a.txt"); + trg_dir0a_fil0a = root.GenSubFil_nest("trg", "dir0a", "fil0a.txt"); + srcTree = new Io_url[] {src, src_dir0a, src_dir0a_dir0a, src_fil0a, src_dir0a_fil0a}; + trgTree = new Io_url[] {trg, trg_dir0a, trg_dir0a_dir0a, trg_fil0a, trg_dir0a_fil0a}; + } + void setup_objs() { + fx.run_SaveFilText(src_fil0a, "src_fil0a"); // NOTE: automatically creates src + fx.run_SaveFilText(src_dir0a_fil0a, "src_dir0a_fil0a"); // NOTE: automatically creates src_dir0a_dir0a + fx.tst_ExistsPaths(true, src_fil0a); + engine.CreateDir(src_dir0a_dir0a); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_dir_deep_memory_tst.java b/100_core/tst/gplx/ios/IoEngine_dir_deep_memory_tst.java new file mode 100644 index 000000000..d2f75c244 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_dir_deep_memory_tst.java @@ -0,0 +1,36 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_dir_deep_memory_tst extends IoEngine_dir_deep_base { + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem/root"); + } @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} + @Test @Override public void SearchDir() { + super.SearchDir(); + } + @Test @Override public void MoveDirDeep() { + super.MoveDirDeep(); + } + @Test @Override public void CopyDir() { + super.CopyDir(); + } + @Test @Override public void DeleteDir() { + super.DeleteDir(); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_dir_deep_system_tst.java b/100_core/tst/gplx/ios/IoEngine_dir_deep_system_tst.java new file mode 100644 index 000000000..79672c36f --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_dir_deep_system_tst.java @@ -0,0 +1,25 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_dir_deep_system_tst extends IoEngine_dir_deep_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + IoEngine_xrg_deleteDir.new_(root).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_.Sys;} +} diff --git a/100_core/tst/gplx/ios/IoEngine_fil_basic_base.java b/100_core/tst/gplx/ios/IoEngine_fil_basic_base.java new file mode 100644 index 000000000..98d1e6039 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_fil_basic_base.java @@ -0,0 +1,176 @@ +/* +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.ios; import gplx.*; +import org.junit.*; import gplx.texts.*;/*EncodingAdp_*/ +public abstract class IoEngine_fil_basic_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + } protected IoEngine engine; protected IoEngineFxt fx; protected Io_url fil, root; + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + @Test @gplx.Virtual public void ExistsFil() { + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void ExistsFil_deep() { + fx.tst_ExistsPaths(false, root.GenSubFil_nest("dir1", "dir2", "fil1.txt")); + } + @Test @gplx.Virtual public void SaveFilStr() { + fx.tst_ExistsPaths(false, fil, fil.OwnerDir()); + + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil, fil.OwnerDir()); + } + @Test @gplx.Virtual public void SaveFilText_autoCreateOwnerDir() { + fil = fil.OwnerDir().GenSubFil_nest("sub1", "fil1.txt"); + fx.tst_ExistsPaths(false, fil, fil.OwnerDir()); + + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil, fil.OwnerDir()); + } + @Test @gplx.Virtual public void SaveFilText_overwrite() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + fx.run_SaveFilText(fil, "changed"); + fx.tst_LoadFilStr(fil, "changed"); + } + @Test @gplx.Virtual public void SaveFilText_append() { + fx.run_SaveFilText(fil, "text"); + + engine.SaveFilText_api(IoEngine_xrg_saveFilStr.new_(fil, "appended").Append_()); + fx.tst_LoadFilStr(fil, "text" + "appended"); + } + @Test @gplx.Virtual public void SaveFilText_caseInsensitive() { + if (root.Info().CaseSensitive()) return; + Io_url lcase = root.GenSubFil_nest("dir", "fil.txt"); + Io_url ucase = root.GenSubFil_nest("DIR", "FIL.TXT"); + fx.run_SaveFilText(lcase, "text"); + + fx.tst_ExistsPaths(true, lcase, ucase); + fx.tst_LoadFilStr(lcase, "text"); + fx.tst_LoadFilStr(ucase, "text"); + } + @Test @gplx.Virtual public void SaveFilText_readOnlyFails() { + fx.run_SaveFilText(fil, "text"); + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + + try {fx.run_SaveFilText(fil, "changed");} + catch (Exception exc) { + fx.tst_LoadFilStr(fil, "text"); + Err_.Noop(exc); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void LoadFilStr() { + fx.run_SaveFilText(fil, "text"); + fx.tst_LoadFilStr(fil, "text"); + } + @Test @gplx.Virtual public void LoadFilStr_missingIgnored() { + Tfds.Eq("", engine.LoadFilStr(IoEngine_xrg_loadFilStr.new_(fil).MissingIgnored_())); + } + @Test @gplx.Virtual public void UpdateFilAttrib() { + fx.run_SaveFilText(fil, "text"); + fx.tst_QueryFilReadOnly(fil, false); + + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + fx.tst_QueryFilReadOnly(fil, true); + } + @Test @gplx.Virtual public void DeleteFil() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil)); + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void DeleteFil_missing_pass() { + fil = root.GenSubFil("fileThatDoesntExist.txt"); + + engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil).MissingFails_off()); + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void DeleteFil_readOnly_fail() { + fx.run_SaveFilText(fil, "text"); + + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + try {engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil));} + catch (Exception exc) {Err_.Noop(exc); + fx.tst_ExistsPaths(true, fil); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void DeleteFil_readOnly_pass() { + fx.run_SaveFilText(fil, "text"); + engine.UpdateFilAttrib(fil, IoItmAttrib.readOnly_()); + + engine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(fil).ReadOnlyFails_off()); + fx.tst_ExistsPaths(false, fil); + } + @Test @gplx.Virtual public void QueryFil_size() { + fx.run_SaveFilText(fil, "text"); + + fx.tst_QueryFil_size(fil, String_.Len("text")); + } + @Test @gplx.Virtual public void UpdateFilModifiedTime() { + fx.run_SaveFilText(fil, "text"); + + DateAdp time = Tfds.Now_time0_add_min(10); + engine.UpdateFilModifiedTime(fil, time); + fx.tst_QueryFil_modifiedTime(fil, time); + } + @Test @gplx.Virtual public void OpenStreamRead() { + fx.run_SaveFilText(fil, "text"); + + int textLen = String_.Len("text"); + byte[] buffer = new byte[textLen]; + IoStream stream = IoStream_.Null; + try { + stream = engine.OpenStreamRead(fil); + stream.Read(buffer, 0, textLen); + } + finally {stream.Rls();} + String actl = String_.new_utf8_(buffer); + Tfds.Eq("text", actl); + } + @Test @gplx.Virtual public void OpenStreamWrite() { + IoStream stream = IoEngine_xrg_openWrite.new_(fil).Exec(); + byte[] buffer = Bry_.new_utf8_("text"); + int textLen = String_.Len("text"); + stream.Write(buffer, 0, textLen); + stream.Rls(); + + fx.tst_LoadFilStr(fil, "text"); + } +// @Test public virtual void OpenStreamWrite_in_place() { +// byte[] buffer = Bry_.new_utf8_("a|b|c"); +// IoStream stream = IoEngine_xrg_openWrite.new_(fil).Exec(); +// stream.Write(buffer, 0, buffer.length); +// stream.Rls(); +// +// buffer = Bry_.new_utf8_("B"); +// stream = IoEngine_xrg_openWrite.new_(fil).Exec(); +// stream.Seek(2); +// stream.Write(buffer, 0, buffer.length); +// stream.Rls(); +// +// fx.tst_LoadFilStr(fil, "a|B|c"); +// } +} diff --git a/100_core/tst/gplx/ios/IoEngine_fil_basic_memory_tst.java b/100_core/tst/gplx/ios/IoEngine_fil_basic_memory_tst.java new file mode 100644 index 000000000..1706c4f15 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_fil_basic_memory_tst.java @@ -0,0 +1,57 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_fil_basic_memory_tst extends IoEngine_fil_basic_base { + @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem"); + fil = root.GenSubFil_nest("root", "fil.txt"); + } + @Test @Override public void OpenStreamRead() { + super.OpenStreamRead (); + } + @Test @Override public void SaveFilText_overwrite() { + super.SaveFilText_overwrite(); + + // bugfix: verify changed file in ownerDir's hash + IoItmDir dirItm = fx.tst_ScanDir(fil.OwnerDir(), fil); + IoItmFil_mem filItm = (IoItmFil_mem)dirItm.SubFils().FetchAt(0); + Tfds.Eq(filItm.Text(), "changed"); + } + @Test public void RecycleFil() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + IoRecycleBin bin = IoRecycleBin._; + ListAdp list = Tfds.RscDir.XtoNames(); +// foreach (String s in list) +// Tfds.Write(s); + list.DelAt(0); // remove drive + IoEngine_xrg_recycleFil recycleXrg = bin.Send_xrg(fil) + .RootDirNames_(list) + .AppName_("gplx.test").Time_(DateAdp_.parse_gplx("20100102_115559123")).Uuid_(UuidAdp_.parse_("467ffb41-cdfe-402f-b22b-be855425784b")); + recycleXrg.Exec(); + fx.tst_ExistsPaths(false, fil); + fx.tst_ExistsPaths(true, recycleXrg.RecycleUrl()); + + bin.Recover(recycleXrg.RecycleUrl()); + fx.tst_ExistsPaths(true, fil); + fx.tst_ExistsPaths(false, recycleXrg.RecycleUrl()); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_fil_basic_system_tst.java b/100_core/tst/gplx/ios/IoEngine_fil_basic_system_tst.java new file mode 100644 index 000000000..493d4bc48 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_fil_basic_system_tst.java @@ -0,0 +1,58 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_fil_basic_system_tst extends IoEngine_fil_basic_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + fil = root.GenSubFil("fil.txt"); + IoEngine_xrg_deleteDir.new_(fil.OwnerDir()).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_system.new_();} + @Test public void ExistsFil_IgnoreDifferentCasing() { + if (root.Info().CaseSensitive()) return; + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + fx.tst_ExistsPaths(true, fil.OwnerDir().GenSubFil("FIL.txt")); + } + @Test @gplx.Virtual public void RecycleFil() { + fx.run_SaveFilText(fil, "text"); + fx.tst_ExistsPaths(true, fil); + + IoRecycleBin bin = IoRecycleBin._; + ListAdp list = root.XtoNames(); list.DelAt(0); // remove drive + IoEngine_xrg_recycleFil recycleXrg = bin.Send_xrg(fil) + .RootDirNames_(list) + .AppName_("gplx.test").Time_(DateAdp_.parse_gplx("20100102_115559123")).Uuid_(UuidAdp_.parse_("467ffb41-cdfe-402f-b22b-be855425784b")); + recycleXrg.Exec(); + fx.tst_ExistsPaths(false, fil); + fx.tst_ExistsPaths(true, recycleXrg.RecycleUrl()); + + bin.Recover(recycleXrg.RecycleUrl()); + fx.tst_ExistsPaths(true, fil); + fx.tst_ExistsPaths(false, recycleXrg.RecycleUrl()); + } + @Test @Override public void DeleteFil_missing_pass() { + super.DeleteFil_missing_pass(); + } + @Test @Override public void DeleteFil_readOnly_pass() { + super.DeleteFil_readOnly_pass (); + } + @Test @Override public void SaveFilText_readOnlyFails() { + super.SaveFilText_readOnlyFails(); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_fil_xfer_base.java b/100_core/tst/gplx/ios/IoEngine_fil_xfer_base.java new file mode 100644 index 000000000..8729f870a --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_fil_xfer_base.java @@ -0,0 +1,106 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public abstract class IoEngine_fil_xfer_base { + @Before public void setup() { + engine = engine_(); + fx = IoEngineFxt.new_(); + setup_hook(); + src = root.GenSubFil("src.txt"); trg = root.GenSubFil("trg.txt"); + } protected IoEngine engine; @gplx.Internal protected IoEngineFxt fx; protected Io_url src, trg, root; + DateAdp srcModifiedTime = DateAdp_.parse_gplx("2010.04.12 20.26.01.000"), trgModifiedTime = DateAdp_.parse_gplx("2010.04.01 01.01.01.000"); + protected abstract IoEngine engine_(); + protected abstract void setup_hook(); + protected abstract Io_url AltRoot(); + @Test @gplx.Virtual public void CopyFil() { + fx.run_SaveFilText(src, "src"); fx.run_UpdateFilModifiedTime(src, srcModifiedTime); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(false, trg); + + IoEngine_xrg_xferFil.copy_(src, trg).Exec(); + fx.tst_ExistsPaths(true, src, trg); + fx.tst_LoadFilStr(trg, "src"); + fx.tst_QueryFil_modifiedTime(trg, srcModifiedTime); + } + @Test @gplx.Virtual public void CopyFil_overwrite_fail() { + fx.run_SaveFilText(src, "src"); + fx.run_SaveFilText(trg, "trg"); + + try {IoEngine_xrg_xferFil.copy_(src, trg).Exec();} + catch (Exception exc) {Err_.Noop(exc); + fx.tst_ExistsPaths(true, src, trg); + fx.tst_LoadFilStr(trg, "trg"); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void CopyFil_overwrite_pass() { + fx.run_SaveFilText(src, "src"); fx.run_UpdateFilModifiedTime(src, srcModifiedTime); + fx.run_SaveFilText(trg, "trg"); fx.run_UpdateFilModifiedTime(trg, trgModifiedTime); + + IoEngine_xrg_xferFil.copy_(src, trg).Overwrite_().Exec(); + fx.tst_ExistsPaths(true, src, trg); + fx.tst_LoadFilStr(trg, "src"); + fx.tst_QueryFil_modifiedTime(trg, srcModifiedTime); + } + @Test @gplx.Virtual public void MoveFil() { + fx.run_SaveFilText(src, "src"); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(false, trg); + + IoEngine_xrg_xferFil.move_(src, trg).Exec(); + fx.tst_ExistsPaths(false, src); + fx.tst_ExistsPaths(true, trg); + } + @Test @gplx.Virtual public void MoveFil_overwrite_fail() { + fx.run_SaveFilText(src, "src"); + fx.run_SaveFilText(trg, "trg"); + + try {IoEngine_xrg_xferFil.move_(src, trg).Exec();} + catch (Exception exc) {Err_.Noop(exc); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(true, trg); + fx.tst_LoadFilStr(trg, "trg"); + return; + } + Tfds.Fail_expdError(); + } + @Test @gplx.Virtual public void MoveFil_overwrite_pass() { + fx.run_SaveFilText(src, "src"); fx.run_UpdateFilModifiedTime(src, srcModifiedTime); + fx.run_SaveFilText(trg, "trg"); fx.run_UpdateFilModifiedTime(trg, trgModifiedTime); + + IoEngine_xrg_xferFil.move_(src, trg).Overwrite_().Exec(); + fx.tst_ExistsPaths(false, src); + fx.tst_ExistsPaths(true, trg); + fx.tst_LoadFilStr(trg, "src"); + fx.tst_QueryFil_modifiedTime(trg, srcModifiedTime); + } + @Test @gplx.Virtual public void MoveFil_betweenDrives() { + IoEngine_xrg_deleteDir.new_(AltRoot()).Recur_().ReadOnlyFails_off().Exec(); + src = root.GenSubFil_nest("dir", "fil1a.txt"); + trg = AltRoot().GenSubFil_nest("dir", "fil1b.txt"); + fx.run_SaveFilText(src, "src"); + fx.tst_ExistsPaths(true, src); + fx.tst_ExistsPaths(false, trg); + + IoEngine_xrg_xferFil.move_(src, trg).Exec(); + fx.tst_ExistsPaths(false, src); + fx.tst_ExistsPaths(true, trg); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_fil_xfer_memory_tst.java b/100_core/tst/gplx/ios/IoEngine_fil_xfer_memory_tst.java new file mode 100644 index 000000000..dff6bbec1 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_fil_xfer_memory_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoEngine_fil_xfer_memory_tst extends IoEngine_fil_xfer_base { + @Override protected void setup_hook() { + root = Io_url_.mem_dir_("mem"); + } @Override protected IoEngine engine_() {return IoEngine_.Mem_init_();} + @Override protected Io_url AltRoot() { + Io_mgr._.InitEngine_mem_("mem2"); + return Io_url_.mem_dir_("mem2"); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_fil_xfer_system_tst.java b/100_core/tst/gplx/ios/IoEngine_fil_xfer_system_tst.java new file mode 100644 index 000000000..793de5756 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_fil_xfer_system_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoEngine_fil_xfer_system_tst extends IoEngine_fil_xfer_base { + @Override protected void setup_hook() { + root = Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + IoEngine_xrg_deleteDir.new_(root.OwnerDir()).Recur_().ReadOnlyFails_off().Exec(); + } @Override protected IoEngine engine_() {return IoEngine_system.new_();} + @Override protected Io_url AltRoot() { + return Tfds.RscDir.GenSubDir_nest("100_core", "ioEngineTest", "_temp"); + } +} diff --git a/100_core/tst/gplx/ios/IoEngine_stream_xfer_tst.java b/100_core/tst/gplx/ios/IoEngine_stream_xfer_tst.java new file mode 100644 index 000000000..08dd8d4bb --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_stream_xfer_tst.java @@ -0,0 +1,49 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_stream_xfer_tst { + @Before public void setup() { + srcEngine = IoEngine_memory.new_("mock1"); + trgEngine = IoEngine_memory.new_("mock2"); + IoEnginePool._.AddReplace(srcEngine); IoEnginePool._.AddReplace(trgEngine); + IoUrlInfoRegy._.Reg(IoUrlInfo_.mem_("mem1/", srcEngine.Key())); + IoUrlInfoRegy._.Reg(IoUrlInfo_.mem_("mem2/", trgEngine.Key())); + srcDir = Io_url_.mem_dir_("mem1/dir"); trgDir = Io_url_.mem_dir_("mem2/dir"); + } + @Test public void TransferBetween() { + Io_url srcPath = srcDir.GenSubFil("fil.txt"); + Io_url trgPath = trgDir.GenSubFil("fil.txt"); + tst_TransferStreams(srcEngine, srcPath, trgEngine, trgPath); + } + void tst_TransferStreams(IoEngine srcEngine, Io_url srcPath, IoEngine trgEngine, Io_url trgPath) { + srcEngine.SaveFilText_api(IoEngine_xrg_saveFilStr.new_(srcPath, "test1")); + trgEngine.DeleteFil_api(IoEngine_xrg_deleteFil.new_(trgPath)); // make sure file is deleted + fx.tst_ExistsPaths(true, srcPath); + fx.tst_ExistsPaths(false, trgPath); + + IoEngineUtl utl = IoEngineUtl.new_(); + utl.BufferLength_set(4); + utl.XferFil(srcEngine, IoEngine_xrg_xferFil.copy_(srcPath, trgPath)); + fx.tst_ExistsPaths(true, srcPath, trgPath); + fx.tst_LoadFilStr(trgPath, "test1"); + } + IoEngineFxt fx = IoEngineFxt.new_(); + Io_url srcDir, trgDir; + IoEngine srcEngine, trgEngine; +} diff --git a/100_core/tst/gplx/ios/IoEngine_xrg_queryDir_tst.java b/100_core/tst/gplx/ios/IoEngine_xrg_queryDir_tst.java new file mode 100644 index 000000000..2b06f5207 --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_xrg_queryDir_tst.java @@ -0,0 +1,65 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoEngine_xrg_queryDir_tst { + @Before public void setup() { + engine = IoEngine_.Mem_init_(); + } IoEngine engine; Io_url[] ary; + @Test public void Basic() { + ary = save_text_(fil_("fil1.txt")); + + tst_ExecPathAry(finder_(), ary); + } + @Test public void FilPath() { + ary = save_text_(fil_("fil1.txt"), fil_("fil2.jpg"), fil_("fil3.txt")); + + tst_ExecPathAry(finder_(), ary); // default: all files + tst_ExecPathAry(finder_().FilPath_("*.txt") // findPattern of *.txt + , fil_("fil1.txt"), fil_("fil3.txt")); + } + @Test public void Recur() { + ary = save_text_(fil_("fil1.txt"), fil_("dirA", "fil1A.jpg")); + + tst_ExecPathAry(finder_(), fil_("fil1.txt")); // default: no recursion + tst_ExecPathAry(finder_().Recur_(), ary); // recurse + } + @Test public void DirPattern() { + save_text_(fil_("fil1.txt"), fil_("dirA", "fil1A.jpg")); + + tst_ExecPathAry(finder_(), fil_("fil1.txt")); // default: files only + tst_ExecPathAry(finder_().DirInclude_() // include dirs; NOTE: fil1A not returned b/c Recur_ is not true + , dir_("dirA"), fil_("fil1.txt")); + } + @Test public void SortBy() { + save_text_(fil_("fil2a.txt"), fil_("fil1.txt")); + + tst_ExecPathAry(finder_() // default: sortByAscOrder + , fil_("fil1.txt"), fil_("fil2a.txt")); + } + IoEngine_xrg_queryDir finder_() {return IoEngine_xrg_queryDir.new_(Io_url_.mem_dir_("mem/root"));}// NOTE: not in setup b/c finder must be newed several times inside test method + Io_url fil_(String... ary) {return Io_url_.mem_dir_("mem/root").GenSubFil_nest(ary);} + Io_url dir_(String... ary) {return Io_url_.mem_dir_("mem/root").GenSubDir_nest(ary);} + + Io_url[] save_text_(Io_url... ary) { + for (Io_url url : ary) + Io_mgr._.SaveFilStr(url, url.Raw()); + return ary; + } + void tst_ExecPathAry(IoEngine_xrg_queryDir finder, Io_url... expd) {Tfds.Eq_ary(expd, finder.ExecAsUrlAry());} +} diff --git a/100_core/tst/gplx/ios/IoEngine_xrg_recycleFil_tst.java b/100_core/tst/gplx/ios/IoEngine_xrg_recycleFil_tst.java new file mode 100644 index 000000000..5525f7efa --- /dev/null +++ b/100_core/tst/gplx/ios/IoEngine_xrg_recycleFil_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoEngine_xrg_recycleFil_tst { + @Before public void setup() { + IoEngine_.Mem_init_(); + } + @Test public void GenRecycleUrl() { + tst_GenRecycleUrl(recycle_(), Io_url_.mem_fil_("mem/z_trash/20100102/gplx.images;115559123;;fil.txt")); + tst_GenRecycleUrl(recycle_().Uuid_include_(), Io_url_.mem_fil_("mem/z_trash/20100102/gplx.images;115559123;467ffb41-cdfe-402f-b22b-be855425784b;fil.txt")); + } + IoEngine_xrg_recycleFil recycle_() {return IoEngine_xrg_recycleFil.gplx_(Io_url_.mem_fil_("mem/dir/fil.txt")).AppName_("gplx.images").Uuid_(UuidAdp_.parse_("467ffb41-cdfe-402f-b22b-be855425784b")).Time_(DateAdp_.parse_gplx("20100102_115559123"));} + void tst_GenRecycleUrl(IoEngine_xrg_recycleFil xrg, Io_url expd) { + Tfds.Eq(expd, xrg.RecycleUrl()); + } +} diff --git a/100_core/tst/gplx/ios/IoItmDir_FetchDeepOrNull_tst.java b/100_core/tst/gplx/ios/IoItmDir_FetchDeepOrNull_tst.java new file mode 100644 index 000000000..0ef1c9315 --- /dev/null +++ b/100_core/tst/gplx/ios/IoItmDir_FetchDeepOrNull_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoItmDir_FetchDeepOrNull_tst { + @Before public void setup() { + drive = Io_url_.mem_dir_("mem"); + rootDir = bldr.dir_(drive, bldr.dir_(drive.GenSubDir("sub1"))); + } IoItm_fxt bldr = IoItm_fxt.new_(); Io_url drive; IoItmDir rootDir; + @Test public void FetchDeepOrNull() { + tst_FetchDeepOrNull(rootDir, drive.GenSubDir("sub1"), true); + tst_FetchDeepOrNull(rootDir, drive.GenSubDir("sub2"), false); + tst_FetchDeepOrNull(rootDir.SubDirs().FetchAt(0), drive.GenSubDir("sub1"), true); + tst_FetchDeepOrNull(rootDir.SubDirs().FetchAt(0), drive.GenSubDir("sub2"), false); + } + void tst_FetchDeepOrNull(Object rootDirObj, Io_url find, boolean expdFound) { + IoItmDir rootDir = IoItmDir_.as_(rootDirObj); + IoItmDir actlDir = rootDir.FetchDeepOrNull(find); + if (actlDir == null) { + if (expdFound) Tfds.Fail("actlDir is null, but expd dir to be found"); + else return; // actlDir is null but expdFound was false; return; + } + Tfds.Eq(find.Raw(), actlDir.Url().Raw()); + } +} diff --git a/100_core/tst/gplx/ios/IoItm_fxt.java b/100_core/tst/gplx/ios/IoItm_fxt.java new file mode 100644 index 000000000..c11ba52fc --- /dev/null +++ b/100_core/tst/gplx/ios/IoItm_fxt.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +public class IoItm_fxt { + public IoItmFil fil_wnt_(String s) {return fil_(Io_url_.wnt_fil_(s));} + public IoItmFil fil_(Io_url url) {return IoItmFil_.new_(url, 1, DateAdp_.parse_gplx("2001-01-01"), DateAdp_.parse_gplx("2001-01-01"));} + public IoItmDir dir_wnt_(String s) {return dir_(Io_url_.wnt_dir_(s));} + public IoItmDir dir_(Io_url url, IoItm_base... ary) { + IoItmDir rv = IoItmDir_.top_(url); + for (IoItm_base itm : ary) { + if (itm.Type_dir()) + rv.SubDirs().Add(itm); + else + rv.SubFils().Add(itm); + } + return rv; + } + public static IoItm_fxt new_() {return new IoItm_fxt();} IoItm_fxt() {} +} diff --git a/100_core/tst/gplx/ios/IoUrlInfo_alias_tst.java b/100_core/tst/gplx/ios/IoUrlInfo_alias_tst.java new file mode 100644 index 000000000..424a7302f --- /dev/null +++ b/100_core/tst/gplx/ios/IoUrlInfo_alias_tst.java @@ -0,0 +1,58 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoUrlInfo_alias_tst { + IoUrlInfo_alias alias; + @Test public void MapWntToWnt() { + Make("usr:\\", "D:\\usr\\"); + tst_Xto_api("usr:\\dir\\fil.txt", "D:\\usr\\dir\\fil.txt"); + tst_OwnerDir("usr:\\dir\\", "usr:\\"); + tst_OwnerDir("usr:\\", ""); + tst_NameOnly("usr:\\", "usr"); + } + @Test public void MapToLnx() { + Make("usr:\\", "/home/"); + tst_Xto_api("usr:\\dir\\fil.txt", "/home/dir/fil.txt"); + } + @Test public void MapLnxToWnt() { + Make("usr:/", "C:\\usr\\"); + tst_Xto_api("usr:/dir/fil.txt", "C:\\usr\\dir\\fil.txt"); + } + @Test public void WntToWnt() { + Make("C:\\", "X:\\"); + tst_Xto_api("C:\\dir\\fil.txt", "X:\\dir\\fil.txt"); + tst_NameOnly("C:\\", "C"); + } + @Test public void WntToLnx() { + Make("C:\\", "/home/"); + tst_Xto_api("C:\\dir\\fil.txt", "/home/dir/fil.txt"); + } + @Test public void LnxToWnt() { + Make("/home/", "C:\\"); + tst_Xto_api("/home/dir/fil.txt", "C:\\dir\\fil.txt"); + tst_NameOnly("/home/", "home"); + tst_NameOnly("/", "root"); + } + void tst_Xto_api(String raw, String expd) {Tfds.Eq(expd, alias.Xto_api(raw));} + void tst_OwnerDir(String raw, String expd) {Tfds.Eq(expd, alias.OwnerDir(raw));} + void tst_NameOnly(String raw, String expd) {Tfds.Eq(expd, alias.NameOnly(raw));} + void Make(String srcDir, String trgDir) { + alias = IoUrlInfo_alias.new_(srcDir, trgDir, IoEngine_.SysKey); + } +} diff --git a/100_core/tst/gplx/ios/IoUrl_lnx_tst.java b/100_core/tst/gplx/ios/IoUrl_lnx_tst.java new file mode 100644 index 000000000..250fe8a5a --- /dev/null +++ b/100_core/tst/gplx/ios/IoUrl_lnx_tst.java @@ -0,0 +1,55 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoUrl_lnx_tst { + IoUrlFxt fx = IoUrlFxt.new_(); + @Test public void Raw() { + fx.tst_Xto_gplx(Io_url_.lnx_dir_("/home/"), "/home/"); + fx.tst_Xto_gplx(Io_url_.lnx_dir_("/home"), "/home/"); // add / + fx.tst_Xto_gplx(Io_url_.lnx_dir_("/"), "/"); + fx.tst_Xto_gplx(Io_url_.lnx_fil_("/home/fil.txt"), "/home/fil.txt"); + } + @Test public void Xto_api() { + fx.tst_Xto_api(Io_url_.lnx_fil_("/home/fil.txt"), "/home/fil.txt"); + fx.tst_Xto_api(Io_url_.lnx_dir_("/home/"), "/home"); // del / + fx.tst_Xto_api(Io_url_.lnx_dir_("/"), "/"); + } + @Test public void OwnerRoot() { + fx.tst_OwnerRoot(Io_url_.lnx_dir_("/home/fil.txt"), "/"); + fx.tst_OwnerRoot(Io_url_.lnx_dir_("/home"), "/"); + fx.tst_OwnerRoot(Io_url_.lnx_dir_("root"), "/"); + } + @Test public void XtoNames() { + fx.tst_XtoNames(Io_url_.lnx_dir_("/home/fil.txt"), fx.ary_("root", "home", "fil.txt")); + fx.tst_XtoNames(Io_url_.lnx_dir_("/home"), fx.ary_("root", "home")); + } + @Test public void IsDir() { + fx.tst_IsDir(Io_url_.lnx_dir_("/home"), true); + fx.tst_IsDir(Io_url_.lnx_fil_("/home/file.txt"), false); + } + @Test public void OwnerDir() { + fx.tst_OwnerDir(Io_url_.lnx_dir_("/home/lnxusr"), Io_url_.lnx_dir_("/home")); + fx.tst_OwnerDir(Io_url_.lnx_dir_("/fil.txt"), Io_url_.lnx_dir_("/")); + fx.tst_OwnerDir(Io_url_.lnx_dir_("/"), Io_url_.Null); + } + @Test public void NameAndExt() { + fx.tst_NameAndExt(Io_url_.lnx_fil_("/fil.txt"), "fil.txt"); + fx.tst_NameAndExt(Io_url_.lnx_dir_("/dir"), "dir/"); + } +} diff --git a/100_core/tst/gplx/ios/IoUrl_map_tst.java b/100_core/tst/gplx/ios/IoUrl_map_tst.java new file mode 100644 index 000000000..2b2a094e3 --- /dev/null +++ b/100_core/tst/gplx/ios/IoUrl_map_tst.java @@ -0,0 +1,31 @@ +/* +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.ios; import gplx.*; +import org.junit.*; +public class IoUrl_map_tst { + IoUrlFxt fx = IoUrlFxt.new_(); + @Test public void Xto_api() { + IoUrlInfo inf = IoUrlInfo_.alias_("tst:\\", "C:\\tst\\", IoEngine_.SysKey); + fx.tst_Xto_api(Io_url_.new_inf_("tst:\\dir\\fil.txt", inf), "C:\\tst\\dir\\fil.txt"); + fx.tst_Xto_api(Io_url_.new_inf_("tst:\\dir\\", inf), "C:\\tst\\dir"); // no trailing \ + } + @Test public void Xto_api_wce() { + IoUrlInfo inf = IoUrlInfo_.alias_("wce:\\", "\\SD Card\\", IoEngine_.SysKey); + fx.tst_Xto_api(Io_url_.new_inf_("wce:\\dir\\", inf), "\\SD Card\\dir"); + } +} diff --git a/100_core/tst/gplx/ios/IoUrl_wnt_tst.java b/100_core/tst/gplx/ios/IoUrl_wnt_tst.java new file mode 100644 index 000000000..341b47af2 --- /dev/null +++ b/100_core/tst/gplx/ios/IoUrl_wnt_tst.java @@ -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 . +*/ +package gplx.ios; import gplx.*; +import org.junit.*; +public class IoUrl_wnt_tst { + IoUrlFxt fx = IoUrlFxt.new_(); + @Test public void Raw() { + fx.tst_Xto_gplx(Io_url_.wnt_fil_("C:\\dir\\fil.txt"), "C:\\dir\\fil.txt"); + fx.tst_Xto_gplx(Io_url_.wnt_dir_("C:\\dir\\"), "C:\\dir\\"); + fx.tst_Xto_gplx(Io_url_.wnt_dir_("C:\\dir") , "C:\\dir\\"); // add \ + } + @Test public void Xto_api() { + fx.tst_Xto_api(Io_url_.wnt_fil_("C:\\fil.txt"), "C:\\fil.txt"); + fx.tst_Xto_api(Io_url_.wnt_dir_("C:\\dir\\"), "C:\\dir"); // del \ + fx.tst_Xto_api(Io_url_.wnt_dir_("C:"), "C:"); + } + @Test public void OwnerRoot() { + fx.tst_OwnerRoot(Io_url_.wnt_dir_("C:\\dir") , "C:\\"); + fx.tst_OwnerRoot(Io_url_.wnt_dir_("C:\\fil.png") , "C:\\"); + fx.tst_OwnerRoot(Io_url_.wnt_dir_("C:") , "C:\\"); + } + @Test public void IsDir() { + fx.tst_IsDir(Io_url_.wnt_dir_("C:\\dir\\"), true); + fx.tst_IsDir(Io_url_.wnt_fil_("C:\\dir"), false); + fx.tst_IsDir(Io_url_.wnt_fil_("C:\\fil.txt"), false); + } + @Test public void OwnerDir() { + fx.tst_OwnerDir(Io_url_.wnt_dir_("C:\\dir\\sub1"), Io_url_.wnt_dir_("C:\\dir")); + fx.tst_OwnerDir(Io_url_.wnt_fil_("C:\\fil.txt"), Io_url_.wnt_dir_("C:")); + fx.tst_OwnerDir(Io_url_.wnt_dir_("C:"), Io_url_.Null); +// fx.tst_OwnerDir(Io_url_.wnt_fil_("press enter to select this folder"), Io_url_.Null); + } + @Test public void NameAndExt() { + fx.tst_NameAndExt(Io_url_.wnt_fil_("C:\\fil.txt"), "fil.txt"); + fx.tst_NameAndExt(Io_url_.wnt_dir_("C:\\dir"), "dir\\"); + } + @Test public void NameOnly() { + fx.tst_NameOnly(Io_url_.wnt_fil_("C:\\fil.txt"), "fil"); + fx.tst_NameOnly(Io_url_.wnt_dir_("C:\\dir"), "dir"); + fx.tst_NameOnly(Io_url_.wnt_dir_("C:"), "C"); + } + @Test public void Ext() { + fx.tst_Ext(Io_url_.wnt_fil_("C:\\fil.txt"), ".txt"); // fil + fx.tst_Ext(Io_url_.wnt_fil_("C:\\fil.multiple.txt"), ".txt"); // multiple ext + fx.tst_Ext(Io_url_.wnt_fil_("C:\\fil"), ""); // no ext + fx.tst_Ext(Io_url_.wnt_dir_("C:\\dir"), "\\"); // dir + } + @Test public void GenSubDir_nest() { + fx.tst_GenSubDir_nest(Io_url_.wnt_dir_("C:"), fx.ary_("dir1", "sub1"), Io_url_.wnt_dir_("C:\\dir1\\sub1")); + } + @Test public void GenNewExt() { + fx.tst_GenNewExt(Io_url_.wnt_fil_("C:\\fil.gif"), ".png", Io_url_.wnt_fil_("C:\\fil.png")); // basic + fx.tst_GenNewExt(Io_url_.wnt_fil_("C:\\fil.tst.gif"), ".png", Io_url_.wnt_fil_("C:\\fil.tst.png")); // last in multiple dotted + } + @Test public void GenRelUrl_orEmpty() { + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_fil_("C:\\root\\fil.txt") , Io_url_.wnt_dir_("C:\\root") , "fil.txt"); // fil + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_dir_("C:\\root\\dir") , Io_url_.wnt_dir_("C:\\root") , "dir\\"); // dir + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_fil_("C:\\root\\dir\\fil.txt") , Io_url_.wnt_dir_("C:\\root") , "dir\\fil.txt"); // fil: nested1 + fx.tst_GenRelUrl_orEmpty(Io_url_.wnt_fil_("C:\\root\\dir\\fil.txt") , Io_url_.wnt_dir_("C:") , "root\\dir\\fil.txt"); // fil: nested2 + } + @Test public void GenParallel() { + fx.tst_GenParallel(Io_url_.wnt_fil_("C:\\root1\\fil.txt"), Io_url_.wnt_dir_("C:\\root1"), Io_url_.wnt_dir_("D:\\root2"), Io_url_.wnt_fil_("D:\\root2\\fil.txt")); + fx.tst_GenParallel(Io_url_.wnt_dir_("C:\\root1\\dir") , Io_url_.wnt_dir_("C:\\root1"), Io_url_.wnt_dir_("D:\\root2"), Io_url_.wnt_dir_("D:\\root2\\dir")); + } +} +class IoUrlFxt { + public void tst_Xto_api(Io_url url, String expd) {Tfds.Eq(expd, url.Xto_api());} + public void tst_OwnerRoot(Io_url url, String expd) {Tfds.Eq(expd, url.OwnerRoot().Raw());} + public void tst_XtoNames(Io_url url, String... expdAry) {Tfds.Eq_ary(expdAry, url.XtoNames().XtoStrAry());} + public void tst_NameAndExt(Io_url url, String expd) {Tfds.Eq(expd, url.NameAndExt());} + public void tst_Xto_gplx(Io_url url, String expd) {Tfds.Eq(expd, url.Raw());} + public void tst_IsDir(Io_url url, boolean expd) {Tfds.Eq(expd, url.Type_dir());} + public void tst_OwnerDir(Io_url url, Io_url expd) {Tfds.Eq_url(expd, url.OwnerDir());} + public void tst_NameOnly(Io_url url, String expd) {Tfds.Eq(expd, url.NameOnly());} + public void tst_Ext(Io_url url, String expd) {Tfds.Eq(expd, url.Ext());} + public void tst_GenSubDir_nest(Io_url rootDir, String[] parts, Io_url expd) {Tfds.Eq(expd, rootDir.GenSubDir_nest(parts));} + public void tst_GenNewExt(Io_url url, String ext, Io_url expd) {Tfds.Eq_url(expd, url.GenNewExt(ext));} + public void tst_GenRelUrl_orEmpty(Io_url url, Io_url rootDir, String expd) {Tfds.Eq(expd, url.GenRelUrl_orEmpty(rootDir));} + public void tst_GenParallel(Io_url url, Io_url oldRoot, Io_url newRoot, Io_url expd) {Tfds.Eq_url(expd, url.GenParallel(oldRoot, newRoot));} + + public String[] ary_(String... ary) {return String_.Ary(ary);} + public static IoUrlFxt new_() {return new IoUrlFxt();} IoUrlFxt() {} +} diff --git a/100_core/tst/gplx/stores/GfoNdeRdr_read_tst.java b/100_core/tst/gplx/stores/GfoNdeRdr_read_tst.java new file mode 100644 index 000000000..4154669b3 --- /dev/null +++ b/100_core/tst/gplx/stores/GfoNdeRdr_read_tst.java @@ -0,0 +1,48 @@ +/* +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.stores; import gplx.*; +import org.junit.*; +public class GfoNdeRdr_read_tst { + @Test public void ReadInt() { + rdr = rdr_(IntClassXtn._, "id", 1); + Tfds.Eq(rdr.ReadInt("id"), 1); + } + @Test public void ReadIntOr() { + rdr = rdr_(IntClassXtn._, "id", 1); + Tfds.Eq(rdr.ReadIntOr("id", -1), 1); + } + @Test public void ReadIntElse_minus1() { + rdr = rdr_(IntClassXtn._, "id", null); + Tfds.Eq(rdr.ReadIntOr("id", -1), -1); + } + @Test public void ReadInt_parse() { + rdr = rdr_(StringClassXtn._, "id", "1"); + Tfds.Eq(rdr.ReadInt("id"), 1); + } + @Test public void ReadIntElse_parse() { + rdr = rdr_(StringClassXtn._, "id", "2"); + Tfds.Eq(rdr.ReadIntOr("id", -1), 2); + } + GfoNdeRdr rdr_(ClassXtn type, String key, Object val) { // makes rdr with one row and one val + GfoFldList flds = GfoFldList_.new_().Add(key, type); + GfoNde row = GfoNde_.vals_(flds, new Object[] {val}); + boolean parse = type == StringClassXtn._; // assumes type is either StringClassXtn or IntClassXtn + return GfoNdeRdr_.leaf_(row, parse); + } + GfoNdeRdr rdr; +} diff --git a/100_core/tst/gplx/stores/GfoNdeRdr_tst.java b/100_core/tst/gplx/stores/GfoNdeRdr_tst.java new file mode 100644 index 000000000..fa3a9d4d7 --- /dev/null +++ b/100_core/tst/gplx/stores/GfoNdeRdr_tst.java @@ -0,0 +1,189 @@ +/* +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.stores; import gplx.*; +import org.junit.*; +public class GfoNdeRdr_tst { + @Test public void Subs_leafs() { + root = + fx.root_ + ( fx.row_vals_(0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ); + tst_NdeVals(root, 0, 1, 2); + } + @Test public void Subs_ndes() { + root = + fx.root_ + ( leaf_("", 0) + , leaf_("", 1) + , leaf_("", 2) + ); + tst_NdeVals(root, 0, 1, 2); + } + @Test public void Subs_mix() { + root = + fx.root_ + ( leaf_("", 0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ); + tst_NdeVals(root, 0, 1, 2); + } + @Test public void Subs_rdr() { + root = + fx.root_ + ( fx.row_vals_(0) + ); + rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr rdr = rootRdr.Subs(); + Tfds.Eq_true(rdr.MoveNextPeer()); + Tfds.Eq(0, rdr.ReadAt(0)); + Tfds.Eq_false(rdr.MoveNextPeer()); + } + @Test public void MoveNextPeer_implicit() { + root = + fx.root_ + ( fx.csv_dat_ + ( fx.row_vals_(0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ) + ); + GfoNdeRdr rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr subsRdr = rootRdr.Subs(); // pos=-1; bof + DataRdr subRdr = subsRdr.Subs(); // MoveNextPeer not needed; implicitly moves to pos=0 + tst_RdrVals(subRdr, Object_.Ary(0, 1, 2)); + } + @Test public void MoveNextPeer_explicit() { + root = + fx.root_ + ( fx.csv_dat_ + ( fx.row_vals_(0) + , fx.row_vals_(1) + , fx.row_vals_(2) + ) + ); + GfoNdeRdr rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr subsRdr = rootRdr.Subs(); // pos=-1; bof + Tfds.Eq_true(subsRdr.MoveNextPeer()); // explicitly moves to pos=0 + DataRdr subRdr = subsRdr.Subs(); + tst_RdrVals(subRdr, Object_.Ary(0, 1, 2)); + } + @Test public void Xpath_basic() { + root = fx.root_ + ( leaf_("root", 0) + , leaf_("root", 1) + , leaf_("root", 2) + ); + tst_Xpath_all(root, "root", 0, 1, 2); + } + @Test public void Xpath_nested() { + root = fx.root_ + ( fx.tbl_("owner" + , leaf_("root", 0) + , leaf_("root", 1) + , leaf_("root", 2) + )); + tst_Xpath_all(root, "owner/root", 0, 1, 2); + } + @Test public void Xpath_null() { + root = fx.root_ + ( leaf_("match", 0) + ); + rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr sub = rootRdr.Subs_byName("no_match"); + Tfds.Eq_false(sub.MoveNextPeer()); + } + @Test public void Xpath_moveFirst_basic() { + root = fx.root_ + ( leaf_("nde0", 0) + ); + tst_Xpath_first(root, "nde0", 0); + } + @Test public void Xpath_moveFirst_shallow() { + root = fx.root_ + ( leaf_("nde0", 0) + , leaf_("nde1", 1) + , leaf_("nde2", 2) + ); + tst_Xpath_first(root, "nde2", 2); + } + @Test public void Xpath_moveFirst_nested() { + root = fx.root_ + ( node_("nde0", Object_.Ary("0") + , leaf_("nde00", "00") + )); + tst_Xpath_first(root, "nde0", "0"); + tst_Xpath_first(root, "nde0/nde00", "00"); + } + @Test public void Xpath_moveFirst_nested_similarName() { + root = fx.root_ + ( node_("nde0", Object_.Ary("0") + , leaf_("nde00", "00") + ) + , node_("nde1", Object_.Ary("1") + , leaf_("nde00", "10") + )); + tst_Xpath_first(root, "nde1/nde00", "10"); + } + @Test public void Xpath_moveFirst_many() { + root = fx.root_ + ( leaf_("root", 0) + , leaf_("root", 1) + , leaf_("root", 2) + ); + tst_Xpath_first(root, "root", 0); // returns first + } + @Test public void Xpath_moveFirst_null() { + root = fx.root_ + ( leaf_("nde0", 0) + , leaf_("nde1", 1) + , leaf_("nde2", 2) + ); + rootRdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr rdr = rootRdr.Subs_byName("nde3"); + Tfds.Eq_false(rdr.MoveNextPeer()); + } + + GfoNde leaf_(String name, Object... vals) {return GfoNde_.nde_(name, vals, GfoNde_.Ary_empty);} + GfoNde node_(String name, Object[] vals, GfoNde... subs) {return GfoNde_.nde_(name, vals, subs);} + void tst_NdeVals(GfoNde nde, Object... exptVals) { + DataRdr rdr = GfoNdeRdr_.root_parseNot_(nde); + tst_RdrVals(rdr.Subs(), exptVals); + } + void tst_RdrVals(DataRdr rdr, Object[] exptVals) { + int count = 0; + while (rdr.MoveNextPeer()) { + Object actl = rdr.ReadAt(0); + Tfds.Eq(actl, exptVals[count++]); + } + Tfds.Eq(count, exptVals.length); + } + void tst_Xpath_first(GfoNde root, String xpath, Object expt) { + DataRdr rdr = GfoNdeRdr_.root_parseNot_(root); + DataRdr sel = rdr.Subs_byName_moveFirst(xpath); + Object actl = sel.ReadAt(0); + Tfds.Eq(actl, expt); + } + void tst_Xpath_all(GfoNde root, String xpath, Object... exptVals) { + DataRdr rdr = GfoNdeRdr_.root_parseNot_(root); + tst_RdrVals(rdr.Subs_byName(xpath), exptVals); + } + GfoNde root; DataRdr rootRdr; GfoNdeFxt fx = GfoNdeFxt.new_(); +} diff --git a/100_core/tst/gplx/stores/xmls/XmlDataRdr_tst.java b/100_core/tst/gplx/stores/xmls/XmlDataRdr_tst.java new file mode 100644 index 000000000..511b8115b --- /dev/null +++ b/100_core/tst/gplx/stores/xmls/XmlDataRdr_tst.java @@ -0,0 +1,102 @@ +/* +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.stores.xmls; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class XmlDataRdr_tst { + @Test public void Read() { + DataRdr rdr = fx.rdr_(""); + Tfds.Eq(rdr.NameOfNode(), "title"); + Tfds.Eq(rdr.ReadStr("name"), "first"); + Tfds.Eq(rdr.ReadInt("id"), 1); + Tfds.Eq(rdr.ReadBool("profiled"), false); + } + @Test public void None() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find/>" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "no_nde", "no_atr"); + } + @Test public void One() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find id=\"f0\" />" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "find", "id", "f0"); + } + @Test public void One_IgnoreOthers() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find id=\"f0\" />" + , "<skip id=\"s0\" />" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "find", "id", "f0"); + } + @Test public void Many() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<find id=\"f0\" />" + , "<find id=\"f1\" />" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "find", "id", "f0", "f1"); + } + @Test public void Nested() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<sub1>" + , "<find id=\"f0\" />" + , "<find id=\"f1\" />" + , "</sub1>" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "sub1/find", "id", "f0", "f1"); + } + @Test public void Nested_IgnoreOthers() { + DataRdr rdr = fx.rdr_ + ( "<root>" + , "<sub1>" + , "<find id=\"f0\" />" + , "<skip id=\"s0\" />" + , "</sub1>" + , "<sub1>" + , "<find id=\"f1\" />" // NOTE: find across ndes + , "<skip id=\"s1\" />" + , "</sub1>" + , "</root>" + ); + fx.tst_Subs_ByName(rdr, "sub1/find", "id", "f0", "f1"); + } + XmlDataRdr_fxt fx = XmlDataRdr_fxt.new_(); +} +class XmlDataRdr_fxt { + public DataRdr rdr_(String... ary) {return XmlDataRdr_.text_(String_.Concat(ary));} + public void tst_Subs_ByName(DataRdr rdr, String xpath, String key, String... expdAry) { + DataRdr subRdr = rdr.Subs_byName(xpath); + ListAdp list = ListAdp_.new_(); + while (subRdr.MoveNextPeer()) + list.Add(subRdr.Read(key)); + + String[] actlAry = list.XtoStrAry(); + Tfds.Eq_ary(actlAry, expdAry); + } + public static XmlDataRdr_fxt new_() {return new XmlDataRdr_fxt();} XmlDataRdr_fxt() {} +} diff --git a/100_core/tst/gplx/stores/xmls/XmlDataWtr_tst.java b/100_core/tst/gplx/stores/xmls/XmlDataWtr_tst.java new file mode 100644 index 000000000..d2bbde0c2 --- /dev/null +++ b/100_core/tst/gplx/stores/xmls/XmlDataWtr_tst.java @@ -0,0 +1,95 @@ +/* +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.stores.xmls; import gplx.*; import gplx.stores.*; +import org.junit.*; +public class XmlDataWtr_tst { + @Before public void setup() { + wtr = XmlDataWtr.new_(); + } + @Test public void WriteNodeBgn() { + wtr.WriteNodeBgn("chapter"); + tst_XStr(wtr, "<chapter />", String_.CrLf); + } + @Test public void Attributes() { + wtr.WriteNodeBgn("chapter"); + wtr.WriteData("id", 1); + wtr.WriteData("name", "first"); + tst_XStr(wtr, "<chapter id=\"1\" name=\"first\" />", String_.CrLf); + } + @Test public void Subs() { + wtr.WriteNodeBgn("title"); + wtr.WriteNodeBgn("chapters"); + wtr.WriteNodeBgn("chapter"); + tst_XStr(wtr + , "<title>", String_.CrLf + , "<chapters>", String_.CrLf + , "<chapter />", String_.CrLf + , "</chapters>", String_.CrLf + , "", String_.CrLf + ); + } + @Test public void Subs_Iterate() { + wtr.WriteNodeBgn("titles"); + for (int title = 1; title <= 2; title++) { + wtr.WriteNodeBgn("title"); + wtr.WriteData("id", title); + wtr.WriteNodeBgn("chapters"); + wtr.WriteNodeEnd(); // chapters + wtr.WriteNodeEnd(); // title + } + wtr.WriteNodeEnd(); //titles + tst_XStr(wtr + , "", String_.CrLf + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "", String_.CrLf + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "", String_.CrLf + , "", String_.CrLf + ); + } + @Test public void Peers() { + wtr.WriteNodeBgn("title"); + wtr.WriteNodeBgn("chapters"); + wtr.WriteNodeEnd(); + wtr.WriteNodeBgn("audioStreams"); + tst_XStr(wtr + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "<audioStreams />", String_.CrLf + , "", String_.CrLf + ); + } + @Test public void AtrsWithNesting() { + wtr.WriteNodeBgn("title"); + wtr.WriteData("id", 1); + wtr.WriteData("name", "first"); + wtr.WriteNodeBgn("chapters"); + tst_XStr(wtr + , "", String_.CrLf + , "<chapters />", String_.CrLf + , "", String_.CrLf + ); + } + void tst_XStr(XmlDataWtr wtr, String... parts) { + String expd = String_.Concat(parts); + Tfds.Eq(expd, wtr.XtoStr()); + } + XmlDataWtr wtr; +} diff --git a/100_core/xtn/gplx/Internal.java b/100_core/xtn/gplx/Internal.java new file mode 100644 index 000000000..eb976259b --- /dev/null +++ b/100_core/xtn/gplx/Internal.java @@ -0,0 +1,20 @@ +/* +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; +public @interface Internal {} + diff --git a/100_core/xtn/gplx/MainClass.java b/100_core/xtn/gplx/MainClass.java new file mode 100644 index 000000000..f50b9e082 --- /dev/null +++ b/100_core/xtn/gplx/MainClass.java @@ -0,0 +1,22 @@ +/* +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; +public class MainClass { + public static void main(String s[]) { + } +} diff --git a/100_core/xtn/gplx/New.java b/100_core/xtn/gplx/New.java new file mode 100644 index 000000000..4c0e76d55 --- /dev/null +++ b/100_core/xtn/gplx/New.java @@ -0,0 +1,20 @@ +/* +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; +public @interface New {} + diff --git a/100_core/xtn/gplx/Virtual.java b/100_core/xtn/gplx/Virtual.java new file mode 100644 index 000000000..6f019ddee --- /dev/null +++ b/100_core/xtn/gplx/Virtual.java @@ -0,0 +1,20 @@ +/* +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; +public @interface Virtual {} + diff --git a/110_gfml/.classpath b/110_gfml/.classpath new file mode 100644 index 000000000..813419156 --- /dev/null +++ b/110_gfml/.classpath @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/110_gfml/.project b/110_gfml/.project new file mode 100644 index 000000000..2ccedcf3e --- /dev/null +++ b/110_gfml/.project @@ -0,0 +1,17 @@ + + + 110_gfml + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java new file mode 100644 index 000000000..7025efd28 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr.java @@ -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 . +*/ +package gplx.gfml; import gplx.*; +import gplx.texts.*; /*CharStream*/ +public interface GfmlLxr extends GfoEvObj { + String Key(); + String[] Hooks(); + GfmlTkn CmdTkn(); + void CmdTkn_set(GfmlTkn val); // needed for lxr pragma + GfmlTkn MakeTkn(CharStream stream, int hookLength); + GfmlLxr SubLxr(); + void SubLxr_Add(GfmlLxr... lexer); +} +class GfmlLxrRegy { + public int Count() {return hash.Count();} + public void Add(GfmlLxr lxr) {hash.Add(lxr.Key(), lxr);} + public GfmlLxr Fetch(String key) {return (GfmlLxr)hash.Fetch(key);} + HashAdp hash = HashAdp_.new_(); +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java new file mode 100644 index 000000000..1ca74847d --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlLxr_.java @@ -0,0 +1,216 @@ +/* +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.gfml; import gplx.*; +import gplx.texts.*; /*CharStream*/ +public class GfmlLxr_ { + public static GfmlLxr general_(String key, GfmlTkn protoTkn) {return GfmlLxr_general.new_(key, protoTkn);} + public static GfmlLxr solo_(String key, GfmlTkn singletonTkn) {return GfmlLxr_singleton.new_(key, singletonTkn.Raw(), singletonTkn);} + public static GfmlLxr range_(String key, String[] ary, GfmlTkn protoTkn, boolean ignoreOutput) {return GfmlLxr_group.new_(key, ary, protoTkn, ignoreOutput);} + + @gplx.Internal protected static GfmlLxr symbol_(String key, String raw, String val, GfmlBldrCmd cmd) { + GfmlTkn tkn = GfmlTkn_.singleton_(key, raw, val, cmd); + return GfmlLxr_.solo_(key, tkn); + } + @gplx.Internal protected static GfmlLxr frame_(String key, GfmlFrame frame, String bgn, String end) {return GfmlLxr_frame.new_(key, frame, bgn, end, GfmlBldrCmd_pendingTkns_add._, GfmlBldrCmd_frameEnd.data_());} + public static final GfmlLxr Null = new GfmlLxr_null(); + public static final String CmdTknChanged_evt = "Changed"; + public static GfmlLxr as_(Object obj) {return obj instanceof GfmlLxr ? (GfmlLxr)obj : null;} + public static GfmlLxr cast_(Object obj) {try {return (GfmlLxr)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, GfmlLxr.class, obj);}} +} +class GfmlLxr_null implements GfmlLxr { + public String Key() {return "gfml.nullLxr";} + public GfoEvMgr EvMgr() {if (evMgr == null) evMgr = GfoEvMgr.new_(this); return evMgr;} GfoEvMgr evMgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return GfoInvkAble_.Rv_unhandled;} + public GfmlTkn CmdTkn() {return GfmlTkn_.Null;} public void CmdTkn_set(GfmlTkn val) {} + public String[] Hooks() {return String_.Ary_empty;} + public GfmlTkn MakeTkn(CharStream stream, int hookLength) {return GfmlTkn_.Null;} + public void SubLxr_Add(GfmlLxr... lexer) {} + public GfmlLxr SubLxr() {return this;} +} +class GfmlLxr_singleton implements GfmlLxr, GfoEvObj { + public GfoEvMgr EvMgr() {if (evMgr == null) evMgr = GfoEvMgr.new_(this); return evMgr;} GfoEvMgr evMgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return GfoInvkAble_.Rv_unhandled;} + public String Key() {return key;} private String key; + public GfmlTkn CmdTkn() {return singletonTkn;} GfmlTkn singletonTkn; + public void CmdTkn_set(GfmlTkn val) { + String oldRaw = singletonTkn.Raw(); + singletonTkn = val; + hooks = String_.Ary(val.Raw()); + GfoEvMgr_.PubVals(this, GfmlLxr_.CmdTknChanged_evt, KeyVal_.new_("old", oldRaw), KeyVal_.new_("new", val.Raw()), KeyVal_.new_("lxr", this)); + } + public String[] Hooks() {return hooks;} private String[] hooks; + public GfmlTkn MakeTkn(CharStream stream, int hookLength) { + stream.MoveNextBy(hookLength); + return singletonTkn; + } + public GfmlLxr SubLxr() {return subLxr;} GfmlLxr subLxr; + public void SubLxr_Add(GfmlLxr... lexer) {subLxr.SubLxr_Add(lexer);} + public static GfmlLxr_singleton new_(String key, String hook, GfmlTkn singletonTkn) { + GfmlLxr_singleton rv = new GfmlLxr_singleton(); + rv.ctor_(key, hook, singletonTkn, GfmlLxr_.Null); + return rv; + } protected GfmlLxr_singleton() {} + @gplx.Internal protected void ctor_(String key, String hook, GfmlTkn singletonTkn, GfmlLxr subLxr) { + this.key = key; + this.hooks = String_.Ary(hook); + this.subLxr = subLxr; + this.singletonTkn = singletonTkn; + } +} +class GfmlLxr_group implements GfmlLxr { + public String Key() {return key;} private String key; + public GfoEvMgr EvMgr() {if (evMgr == null) evMgr = GfoEvMgr.new_(this); return evMgr;} GfoEvMgr evMgr; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return GfoInvkAble_.Rv_unhandled;} + public GfmlTkn CmdTkn() {return outputTkn;} public void CmdTkn_set(GfmlTkn val) {} GfmlTkn outputTkn; + public String[] Hooks() {return trie.Symbols();} + public GfmlTkn MakeTkn(CharStream stream, int hookLength) { + while (stream.AtMid()) { + if (!ignoreOutput) + sb.Add_mid(stream.Ary(), stream.Pos(), hookLength); + stream.MoveNextBy(hookLength); + + String found = String_.cast_(trie.FindMatch(stream)); + if (found == null) break; + hookLength = trie.LastMatchCount; + } + if (ignoreOutput) return GfmlTkn_.IgnoreOutput; + String raw = sb.XtoStrAndClear(); + return outputTkn.MakeNew(raw, raw); + } + public GfmlLxr SubLxr() {throw Err_sublxr();} + public void SubLxr_Add(GfmlLxr... lexer) {throw Err_sublxr();} + Err Err_sublxr() {return Err_.not_implemented_msg_("group lxr does not have subLxrs").Add("key", key).Add("outputTkn", outputTkn.Raw());} + GfmlTrie trie = GfmlTrie.new_(); String_bldr sb = String_bldr_.new_(); boolean ignoreOutput; + public static GfmlLxr_group new_(String key, String[] hooks, GfmlTkn outputTkn, boolean ignoreOutput) { + GfmlLxr_group rv = new GfmlLxr_group(); + rv.key = key; + for (String hook : hooks) + rv.trie.Add(hook, hook); + rv.outputTkn = outputTkn; rv.ignoreOutput = ignoreOutput; + return rv; + } GfmlLxr_group() {} +} +class GfmlLxr_general implements GfmlLxr, GfoInvkAble { + public GfoEvMgr EvMgr() {if (evMgr == null) evMgr = GfoEvMgr.new_(this); return evMgr;} GfoEvMgr evMgr; + public String Key() {return key;} private String key; + public GfmlTkn CmdTkn() {return txtTkn;} public void CmdTkn_set(GfmlTkn val) {} GfmlTkn txtTkn; + public String[] Hooks() {return symTrie.Symbols();} + public GfmlTkn MakeTkn(CharStream stream, int firstTknLength) { + GfmlTkn rv = null; + if (symLxr != null) { // symLxr has something; produce + rv = MakeTkn_symLxr(stream); + if (rv != GfmlTkn_.IgnoreOutput) return rv; + } + while (stream.AtMid()) { // keep moving til (a) symChar or (b) endOfStream + Object result = symTrie.FindMatch(stream); + symTknLen = symTrie.LastMatchCount; + if (result == null) { // no match; must be txtChar; + txtBfr.Add(stream); + stream.MoveNext(); + } + else { // symChar + symLxr = (GfmlLxr)result; // set symLxr for next pass + if (txtBfr.Has()) // txtBfr has something: gen txtTkn + rv = txtBfr.MakeTkn(stream, txtTkn); + else { // txtBfr empty: gen symbol + rv = MakeTkn_symLxr(stream); + if (rv == GfmlTkn_.IgnoreOutput) continue; + } + return rv; + } + } + if (txtBfr.Has()) // endOfStream, but txtBfr has chars + return txtBfr.MakeTkn(stream, txtTkn); + return GfmlTkn_.EndOfStream; + } + public void SubLxr_Add(GfmlLxr... lxrs) { + for (GfmlLxr lxr : lxrs) { + for (String hook : lxr.Hooks()) + symTrie.Add(hook, lxr); + GfoEvMgr_.SubSame(lxr, GfmlLxr_.CmdTknChanged_evt, this); + } + } + public GfmlLxr SubLxr() {return this;} + GfmlTkn MakeTkn_symLxr(CharStream stream) { + GfmlLxr lexer = symLxr; symLxr = null; + int length = symTknLen; symTknLen = 0; + return lexer.MakeTkn(stream, length); + } + GfmlLxr_general_txtBfr txtBfr = new GfmlLxr_general_txtBfr(); GfmlTrie symTrie = GfmlTrie.new_(); GfmlLxr symLxr; int symTknLen; + @gplx.Internal protected static GfmlLxr_general new_(String key, GfmlTkn txtTkn) { + GfmlLxr_general rv = new GfmlLxr_general(); + rv.key = key; rv.txtTkn = txtTkn; + return rv; + } protected GfmlLxr_general() {} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, GfmlLxr_.CmdTknChanged_evt)) { + symTrie.Del(m.ReadStr("old")); + symTrie.Add(m.ReadStr("new"), m.CastObj("lxr")); + } + else return GfoInvkAble_.Rv_unhandled; + return this; + } +} +class GfmlLxr_general_txtBfr { + public int Bgn = NullPos; + public int Len; + public boolean Has() {return Bgn != NullPos;} + public void Add(CharStream stream) { + if (Bgn == NullPos) Bgn = stream.Pos(); + Len++; + } static final int NullPos = -1; + public GfmlTkn MakeTkn(CharStream stream, GfmlTkn textTkn) { + String raw = String_.new_charAry_(stream.Ary(), Bgn, Len); + Bgn = -1; Len = 0; + return textTkn.MakeNew(raw, raw); + } +} +class GfmlLxr_frame extends GfmlLxr_singleton { GfmlFrame frame; GfmlLxr endLxr, txtLxr; + public void BgnRaw_set(String val) {// needed for lxr pragma + GfmlBldrCmd_frameBgn bgnCmd = GfmlBldrCmd_frameBgn.new_(frame, txtLxr); + GfmlTkn bgnTkn = GfmlTkn_.singleton_(this.Key() + "_bgn", val, GfmlTkn_.NullVal, bgnCmd); + this.CmdTkn_set(bgnTkn); + } + public void EndRaw_set(String val) {// needed for lxr pragma + GfmlBldrCmd_frameEnd endCmd = GfmlBldrCmd_frameEnd.data_(); + GfmlTkn endTkn = GfmlTkn_.singleton_(this.Key() + "_end", val, GfmlTkn_.NullVal, endCmd); + endLxr.CmdTkn_set(endTkn); + } + public static GfmlLxr new_(String key, GfmlFrame frame, String bgn, String end, GfmlBldrCmd txtCmd, GfmlBldrCmd endCmd) { + GfmlLxr_frame rv = new GfmlLxr_frame(); + GfmlTkn txtTkn = frame.FrameType() == GfmlFrame_.Type_comment + ? GfmlTkn_.valConst_(key + "_txt", GfmlTkn_.NullVal, txtCmd) + : GfmlTkn_.cmd_(key + "_txt", txtCmd) + ; + GfmlLxr txtLxr = GfmlLxr_.general_(key + "_txt", txtTkn); + + GfmlTkn bgnTkn = GfmlTkn_.singleton_(key + "_bgn", bgn, GfmlTkn_.NullVal, GfmlBldrCmd_frameBgn.new_(frame, txtLxr)); + rv.ctor_(key, bgn, bgnTkn, txtLxr); + + GfmlTkn endTkn = GfmlTkn_.singleton_(key + "_end", end, GfmlTkn_.NullVal, endCmd); + GfmlLxr endLxr = GfmlLxr_.solo_(key + "_end", endTkn); + rv.SubLxr_Add(endLxr); + + rv.frame = frame; + rv.endLxr = endLxr; + rv.txtLxr = txtLxr; + return rv; + } GfmlLxr_frame() {} + public static GfmlLxr_frame as_(Object obj) {return obj instanceof GfmlLxr_frame ? (GfmlLxr_frame)obj : null;} + public static GfmlLxr_frame cast_(Object obj) {try {return (GfmlLxr_frame)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, GfmlLxr_frame.class, obj);}} +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java new file mode 100644 index 000000000..d9a19a05e --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlObj.java @@ -0,0 +1,29 @@ +/* +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.gfml; import gplx.*; +public interface GfmlObj { + int ObjType(); +} +class GfmlObj_ { + public static final int + Type_tkn = 1 + , Type_atr = 2 + , Type_nde = 3 + , Type_prg = 4 + ; +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java new file mode 100644 index 000000000..f3ec78286 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlObjList.java @@ -0,0 +1,25 @@ +/* +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.gfml; import gplx.*; +public class GfmlObjList extends ListAdp_base { + @gplx.New public GfmlObj FetchAt(int idx) {return (GfmlObj)FetchAt_base(idx);} + public void Add(GfmlObj tkn) {Add_base(tkn);} + public void AddAt(GfmlObj tkn, int idx) {super.AddAt_base(idx, tkn);} + public void Del(GfmlObj tkn) {Del_base(tkn);} + public static GfmlObjList new_() {return new GfmlObjList();} GfmlObjList() {} +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java new file mode 100644 index 000000000..60a65feb8 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn.java @@ -0,0 +1,45 @@ +/* +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.gfml; import gplx.*; +public interface GfmlTkn extends GfmlObj { + String TknType(); + String Raw(); + String Val(); + GfmlTkn[] SubTkns(); + GfmlBldrCmd Cmd_of_Tkn(); + GfmlTkn MakeNew(String raw, String val); +} +class GfmlTknAry_ { + public static final GfmlTkn[] Empty = new GfmlTkn[0]; + public static GfmlTkn[] ary_(GfmlTkn... ary) {return ary;} + @gplx.Internal protected static String XtoRaw(GfmlTkn[] ary) { + String_bldr sb = String_bldr_.new_(); + for (GfmlTkn tkn : ary) + sb.Add(tkn.Raw()); + return sb.XtoStr(); + } + @gplx.Internal protected static String XtoVal(GfmlTkn[] ary) {return XtoVal(ary, 0, ary.length);} + static String XtoVal(GfmlTkn[] ary, int bgn, int end) { + String_bldr sb = String_bldr_.new_(); + for (int i = bgn; i < end; i++) { + GfmlTkn tkn = ary[i]; + sb.Add(tkn.Val()); + } + return sb.XtoStr(); + } +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java new file mode 100644 index 000000000..40ce14008 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlTkn_.java @@ -0,0 +1,65 @@ +/* +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.gfml; import gplx.*; +public class GfmlTkn_ { + @gplx.Internal protected static final String NullRaw = "", NullVal = ""; static final String Type_base = "gfml.baseTkn"; + @gplx.Internal protected static final GfmlTkn + Null = new GfmlTkn_base().ctor_GfmlTkn_base("gfml.nullTkn", NullRaw, NullVal, GfmlBldrCmd_.Null) + , EndOfStream = GfmlTkn_.raw_("<>") + , IgnoreOutput = GfmlTkn_.raw_("<>"); + public static GfmlTkn as_(Object obj) {return obj instanceof GfmlTkn ? (GfmlTkn)obj : null;} + @gplx.Internal protected static GfmlTkn new_(String raw, String val) {return new GfmlTkn_base().ctor_GfmlTkn_base(Type_base, raw, val, GfmlBldrCmd_.Null);} + public static GfmlTkn raw_(String raw) {return new GfmlTkn_base().ctor_GfmlTkn_base(Type_base, raw, raw, GfmlBldrCmd_.Null);} + @gplx.Internal protected static GfmlTkn val_(String val) {return new GfmlTkn_base().ctor_GfmlTkn_base(Type_base, NullRaw, val, GfmlBldrCmd_.Null);} + @gplx.Internal protected static GfmlTkn cmd_(String tknType, GfmlBldrCmd cmd) {return new GfmlTkn_base().ctor_GfmlTkn_base(tknType, NullRaw, NullVal, cmd);} + @gplx.Internal protected static GfmlTkn valConst_(String tknType, String val, GfmlBldrCmd cmd) {return new GfmlTkn_valConst().ctor_GfmlTkn_base(tknType, GfmlTkn_.NullRaw, val, cmd);} + @gplx.Internal protected static GfmlTkn singleton_(String tknType, String raw, String val, GfmlBldrCmd cmd) {return new GfmlTkn_singleton().ctor_GfmlTkn_base(tknType, raw, val, cmd);} + @gplx.Internal protected static GfmlTkn composite_(String tknType, GfmlTkn[] ary) {return new GfmlTkn_composite(tknType, ary);} + @gplx.Internal protected static GfmlTkn composite_list_(String tknType, GfmlObjList list) { + GfmlTkn[] ary = new GfmlTkn[list.Count()]; + for (int i = 0; i < list.Count(); i++) + ary[i] = (GfmlTkn)list.FetchAt(i); + return GfmlTkn_.composite_(tknType, ary); + } +} +class GfmlTkn_base implements GfmlTkn { + public int ObjType() {return GfmlObj_.Type_tkn;} + public String TknType() {return tknType;} private String tknType; + public String Raw() {return raw;} private String raw; + public String Val() {return val;} private String val; + public GfmlBldrCmd Cmd_of_Tkn() {return cmd;} GfmlBldrCmd cmd; + public GfmlTkn[] SubTkns() {return GfmlTknAry_.Empty;} + @gplx.Virtual public GfmlTkn MakeNew(String rawNew, String valNew) {return new GfmlTkn_base().ctor_GfmlTkn_base(tknType, rawNew, valNew, cmd);} + @gplx.Internal protected GfmlTkn_base ctor_GfmlTkn_base(String tknType, String raw, String val, GfmlBldrCmd cmd) {this.tknType = tknType; this.raw = raw; this.val = val; this.cmd = cmd; return this;} +} +class GfmlTkn_valConst extends GfmlTkn_base { + @Override public GfmlTkn MakeNew(String rawNew, String valNew) {return new GfmlTkn_base().ctor_GfmlTkn_base(this.TknType(), rawNew, this.Val(), this.Cmd_of_Tkn());} +} +class GfmlTkn_singleton extends GfmlTkn_base { + @Override public GfmlTkn MakeNew(String rawNew, String valNew) {return this;} +} +class GfmlTkn_composite implements GfmlTkn { + public int ObjType() {return GfmlObj_.Type_tkn;} + public String TknType() {return tknType;} private String tknType; + public String Raw() {return GfmlTknAry_.XtoRaw(ary);} + public String Val() {return GfmlTknAry_.XtoVal(ary);} + public GfmlBldrCmd Cmd_of_Tkn() {return GfmlBldrCmd_.Null;} + public GfmlTkn[] SubTkns() {return ary;} GfmlTkn[] ary; + public GfmlTkn MakeNew(String rawNew, String valNew) {throw Err_.not_implemented_msg_(".MakeNew cannot be invoked on GfmlTkn_composite (raw is available, but not val)").Add("tknType", tknType).Add("rawNew", rawNew).Add("valNew", valNew);} + @gplx.Internal protected GfmlTkn_composite(String tknType, GfmlTkn[] ary) {this.tknType = tknType; this.ary = ary;} +} diff --git a/110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java b/110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java new file mode 100644 index 000000000..d6e40d7f8 --- /dev/null +++ b/110_gfml/src_100_tkn/gplx/gfml/GfmlTrie.java @@ -0,0 +1,114 @@ +/* +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.gfml; import gplx.*; +import gplx.texts.*; /*CharStream*/ +public class GfmlTrie { + public String[] Symbols() { + String[] rv = new String[symbols.Count()]; + for (int i = 0; i < rv.length; i++) + rv[i] = String_.cast_(symbols.FetchAt(i)); + return rv; + } OrderedHash symbols = OrderedHash_.new_(); + public int LastMatchCount; // PERF: prop is faster than method + public Object FindMatch(CharStream stream) { + Object result = null; int moveCount = 0; LastMatchCount = 0; + IntObjHash_base link = rootLink; + while (stream.AtMid()) { + Object found = link.Fetch(stream.Cur()); + if (found == null) break; // found is null; can happen for false matches; ex: . +*/ +package gplx.gfui; import gplx.*; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.awt.image.RenderedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; +public class ImageAdp_base implements ImageAdp, RlsAble { + @gplx.Internal protected ImageAdp_base(Image img) {this.under = img;} + public Gfui_kit Kit() {return kit;} public void Kit_(Gfui_kit v) {this.kit = v;} Gfui_kit kit; + public SizeAdp Size() { + if (this == ImageAdp_.Null) return SizeAdp_.Null; + if (size == null) { + size = SizeAdp_.new_(this.Width(), this.Height()); + } + return size; + } SizeAdp size = null; + public int Width() {return under.getWidth(null);} + public int Height() {return under.getHeight(null);} + public Io_url Url() {return url;} public ImageAdp Url_(Io_url v) {url = v; return this;} Io_url url = Io_url_.Null; + public Object Under() {return under;} Image under; + public boolean Disposed() {return disposed;} private boolean disposed = false; + + public void Rls() {disposed = true; under.flush();} + public void SaveAsBmp(Io_url url) {SaveAs(url, "bmp");} + public void SaveAsPng(Io_url url) {SaveAs(url, "png");} + void SaveAs(Io_url url, String fmtStr) { + Io_mgr._.CreateDirIfAbsent(url.OwnerDir()); + File fil = new File(url.Xto_api()); +// String[] formatNames = ImageIO.getWriterFormatNames(); +// for (String s : formatNames) +// Tfds.Write(s); + boolean success = false; + try {success = ImageIO.write((BufferedImage)under, fmtStr, fil);} + catch (IOException e) {} + if (!success) throw Err_.new_key_("gplx.gfui.imgs.SaveImageFailed", "save image failed").Add("srcUrl", url.Xto_api()).Add("trgFil", fil).Add("fmt", fmtStr); + //#@endif + } + public ImageAdp Extract_image(RectAdp src_rect, SizeAdp trg_size) {return Extract_image(src_rect.X(), src_rect.Y(), src_rect.Width(), src_rect.Height(), trg_size.Width(), trg_size.Height());} + public ImageAdp Extract_image(int src_x, int src_y, int src_w, int src_h, int trg_w, int trg_h) { + if (this == ImageAdp_.Null) return ImageAdp_.Null; // TODO: create ImageAdpNull class (along with ImageAdp interface) + if (disposed) return ImageAdp_.new_(1, 1); + ImageAdp rv = ImageAdp_.new_(trg_w, trg_h); + GfxAdp gfx = GfxAdp_.image_(rv); + gfx.DrawImage(this, 0, 0, trg_w, trg_h, src_x, src_y, src_w, src_h); + gfx.Rls(); + return rv; + } + public ImageAdp Resize(int width, int height) {return Extract_image(0, 0, this.Width(), this.Height(), width, height);} +} diff --git a/150_gfui/src_600_adp/gplx/gfui/ImageAdp_null.java b/150_gfui/src_600_adp/gplx/gfui/ImageAdp_null.java new file mode 100644 index 000000000..0a93effd5 --- /dev/null +++ b/150_gfui/src_600_adp/gplx/gfui/ImageAdp_null.java @@ -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 . +*/ +package gplx.gfui; import gplx.*; +public class ImageAdp_null implements ImageAdp { + public Gfui_kit Kit() {return Gfui_kit_.Mem();} + public SizeAdp Size() {return SizeAdp_.Zero;} + public int Width() {return 0;} + public int Height() {return 0;} + public Io_url Url() {return Io_url_.Null;} public ImageAdp Url_(Io_url v) {return this;} + public Object Under() {return null;} + public boolean Disposed() {return disposed;} private boolean disposed = false; + public void Rls() {disposed = true;} + public void SaveAsBmp(Io_url url) {} + public void SaveAsPng(Io_url url) {} + public ImageAdp Extract_image(RectAdp src_rect, SizeAdp trg_size) {return Extract_image(src_rect.X(), src_rect.Y(), src_rect.Width(), src_rect.Height(), trg_size.Width(), trg_size.Height());} + public ImageAdp Extract_image(int src_x, int src_y, int src_w, int src_h, int trg_w, int trg_h) {return this;} + public ImageAdp Resize(int width, int height) {return this;} + public static final ImageAdp_null _ = new ImageAdp_null(); ImageAdp_null() {} +} diff --git a/150_gfui/src_600_adp/gplx/gfui/ScreenAdp.java b/150_gfui/src_600_adp/gplx/gfui/ScreenAdp.java new file mode 100644 index 000000000..3cdfdc00e --- /dev/null +++ b/150_gfui/src_600_adp/gplx/gfui/ScreenAdp.java @@ -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 . +*/ +package gplx.gfui; import gplx.*; +public class ScreenAdp { + public int Index() {return index;} int index; + public RectAdp Rect() {return bounds;} RectAdp bounds = RectAdp_.Zero; + public SizeAdp Size() {return bounds.Size();} + public int Width() {return bounds.Width();} + public int Height() {return bounds.Height();} + public PointAdp Pos() {return bounds.Pos();} + public int X() {return bounds.X();} + public int Y() {return bounds.Y();} + + @gplx.Internal protected ScreenAdp(int index, RectAdp bounds) { + this.index = index; this.bounds = bounds; + } +} diff --git a/150_gfui/src_600_adp/gplx/gfui/ScreenAdp_.java b/150_gfui/src_600_adp/gplx/gfui/ScreenAdp_.java new file mode 100644 index 000000000..ac22ed9a7 --- /dev/null +++ b/150_gfui/src_600_adp/gplx/gfui/ScreenAdp_.java @@ -0,0 +1,58 @@ +/* +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.gfui; import gplx.*; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Toolkit; +public class ScreenAdp_ { + public static final ScreenAdp Primary = screen_(0); + public static ScreenAdp as_(Object obj) {return obj instanceof ScreenAdp ? (ScreenAdp)obj : null;} + public static ScreenAdp cast_(Object obj) {try {return (ScreenAdp)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, ScreenAdp.class, obj);}} + public static ScreenAdp parse_(String raw) { // ex: {screen{1} + try { + raw = String_.Replace(raw, "{screen{", ""); + raw = String_.Replace(raw, "}", ""); + return ScreenAdp_.screen_(Int_.parse_(raw)); + } catch(Exception exc) {throw Err_.parse_type_exc_(exc, ScreenAdp.class, raw);} + } + public static ScreenAdp from_point_(PointAdp pos) {// NOTE: not using FromPoint b/c of plat_wce + if (ScreenAdp_.Count() == 1) return Primary; + ScreenAdp screen0 = screen_(0), screen1 = screen_(1); + return pos.X() < screen1.X() ? screen0 : screen1; + } + public static ScreenAdp opposite_(int idx) { + if (ScreenAdp_.Count() == 1) return Primary; + int opposite = idx == 0 ? 1 : 0; // will ignore all screens with index > 1 + return screen_(opposite); + } + public static int Count() { + return GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices().length; +// return 1;//Screen.AllScreens.Length; + } + public static ScreenAdp screen_(int index) { + if (index >= ScreenAdp_.Count()) throw Err_.missing_idx_(index, ScreenAdp_.Count()); + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] devs = env.getScreenDevices(); + GraphicsConfiguration conf = devs[index].getDefaultConfiguration(); + ScreenAdp sd = new ScreenAdp(index, GxwCore_lang.XtoRectAdp(conf.getBounds())); + return sd; + } +//#@endif + static ScreenAdp new_(int index, RectAdp rect) {return new ScreenAdp(index, rect);} +} diff --git a/150_gfui/src_600_adp/gplx/gfui/TimerAdp.java b/150_gfui/src_600_adp/gplx/gfui/TimerAdp.java new file mode 100644 index 000000000..6fc7e9bdd --- /dev/null +++ b/150_gfui/src_600_adp/gplx/gfui/TimerAdp.java @@ -0,0 +1,53 @@ +/* +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.gfui; import gplx.*; +import javax.swing.Timer; +import java.awt.event.ActionListener; +public class TimerAdp implements RlsAble { + public TimerAdp Interval_(int interval) { + underTimer.setInitialDelay(interval); + underTimer.setDelay(interval); + return this; + } + public TimerAdp Enabled_on() {return Enabled_(true);} public TimerAdp Enabled_off() {return Enabled_(false);} + public TimerAdp Enabled_(boolean val) { + if (!Env_.Mode_testing()) { + if (val) underTimer.start(); + else underTimer.stop(); + } + return this; + } + public void Rls() {underTimer.stop();} + + Timer underTimer; + public static TimerAdp new_(GfoInvkAble invk, String msgKey, int interval, boolean enabled) { + TimerAdp rv = new TimerAdp(); + rv.underTimer = new Timer(interval, new TimerActionListener(invk, msgKey)); + rv.Interval_(interval).Enabled_(enabled); + return rv; + } + } +class TimerActionListener implements ActionListener { + public void actionPerformed(java.awt.event.ActionEvent arg0) { + GfoInvkAble_.InvkCmd(invk, key); + } + GfoInvkAble invk; String key; + public TimerActionListener(GfoInvkAble invk, String key) { + this.invk = invk; this.key = key; + } +} diff --git a/150_gfui/src_700_env/gplx/gfui/GfoFactory_gfui.java b/150_gfui/src_700_env/gplx/gfui/GfoFactory_gfui.java new file mode 100644 index 000000000..f543e2215 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/GfoFactory_gfui.java @@ -0,0 +1,39 @@ +/* +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.gfui; import gplx.*; +public class GfoFactory_gfui { + public static void Btn_MinWin(GfuiElem owner, GfoMsg appWinMsg) { + GfuiBtn_.msg_("minWin", owner, GfoMsg_.chain_(appWinMsg, GfuiWin.Invk_Minimize)).Text_("_").TipText_("minmize window").Width_(20); + } + public static void Btn_MinWin2(GfuiElem owner) { + GfuiBtn_.msg_("minWin", owner, GfoMsg_.root_(".", GfuiElemBase.Invk_OwnerWin_cmd, GfuiWin.Invk_Minimize)).Text_("_").TipText_("minmize window").Width_(20); + } + public static void Btn_MoveBox(GfuiElem owner, GfuiElem target) { + GfuiElem rv = GfuiBtn_.new_("moveBox").Owner_(owner).Text_("*").TipText_("move box").Width_(20); + GfuiMoveElemBnd bnd = GfuiMoveElemBnd.new_(); + bnd.TargetElem_set(target); + rv.Inject_(bnd); + } + public static GfuiBtn Btn_QuitWin3(GfuiElem owner) { + return (GfuiBtn)GfuiBtn_.msg_("quitWin", owner, GfoMsg_.root_(".", GfuiElemBase.Invk_OwnerWin_cmd, GfuiWin.Invk_Quit)).Text_("X").TipText_("quit win").Width_(20); + } + public static void Btn_QuitWin2(GfuiElem owner, GfoMsg quitMsg) { + GfuiBtn_.msg_("quitWin", owner, quitMsg).Text_("X").TipText_("quit win").Width_(20); + } + public static final GfoFactory_gfui _ = new GfoFactory_gfui(); GfoFactory_gfui() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/GfsLibIni_gfui.java b/150_gfui/src_700_env/gplx/gfui/GfsLibIni_gfui.java new file mode 100644 index 000000000..7d666b3e5 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/GfsLibIni_gfui.java @@ -0,0 +1,24 @@ +/* +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.gfui; import gplx.*; +public class GfsLibIni_gfui implements GfsLibIni { + public void Ini(GfsCore core) { + core.AddCmd(IptCfgRegy._, "IptBndMgr_"); + } + public static final GfsLibIni_gfui _ = new GfsLibIni_gfui(); GfsLibIni_gfui() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/GfuiEnv_.java b/150_gfui/src_700_env/gplx/gfui/GfuiEnv_.java new file mode 100644 index 000000000..478091eba --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/GfuiEnv_.java @@ -0,0 +1,109 @@ +/* +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.gfui; import gplx.*; +import gplx.gfml.*; +import gplx.threads.*; +import java.awt.AWTKeyStroke; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.KeyboardFocusManager; +import java.awt.event.KeyEvent; +import java.awt.image.BufferedImage; +import java.util.HashSet; +import java.util.Set; +import javax.swing.JOptionPane; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; +public class GfuiEnv_ { + static FontAdp system_font; + public static void Exit() {if (!Env_.Mode_testing()) System.exit(0);} + public static void Init(String[] args, String appNameAndExt, Class type) {Init(args, appNameAndExt, type, true);} + public static void Init(String[] args, String appNameAndExt, Class type, boolean swingHack) { + Env_.Init(args, appNameAndExt, type); + if (swingHack) { // TODO: move to kit dependent functionality; WHEN: swing kit + if (Op_sys.Cur().Tid_is_wnt()) { + try {UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");} + catch (ClassNotFoundException e) {e.printStackTrace();} + catch (InstantiationException e) {e.printStackTrace();} + catch (IllegalAccessException e) {e.printStackTrace();} + catch (UnsupportedLookAndFeelException e) {e.printStackTrace();} + } + Set fwdSet = new HashSet(); fwdSet.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0)); + Set bwdSet = new HashSet(); bwdSet.add(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, java.awt.event.InputEvent.SHIFT_DOWN_MASK )); + KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, fwdSet); + KeyboardFocusManager.getCurrentKeyboardFocusManager().setDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, bwdSet); + } + if (!Op_sys.Cur().Tid_is_drd()) + GxwElemFactory_.winForms_(); + + // reg interruptLnr + if (swingHack) { // TODO: move to kit dependent functionality; WHEN: swing kit + UsrDlg_._.Reg(UsrMsgWkr_.Type_Warn, GfoConsoleWin._); + UsrDlg_._.Reg(UsrMsgWkr_.Type_Stop, GfuiInterruptLnr.new_()); + } + IptBndMgr_win = IptCfg_.new_("gplx.gfui.GfuiWin"); + + // alias default dirs + Io_mgr._.AliasDir_sysEngine("app:\\", Env_.AppUrl().OwnerDir().Raw()); + + GfsCore._.MsgParser_(GfoMsgParser_gfml._); + GfsCore._.AddLib(GfsLibIni_core._); + GfsCore._.AddLib(GfsLibIni_gfui._); + Io_url iniFile = Env_.AppUrl().GenSubFil(".gfs"); + if (Io_mgr._.ExistsFil(iniFile)) + GfsCore._.ExecFile(iniFile); + } + public static void Init_swt(String[] args, Class type) { + Env_.Init_swt(args, type); + if (!Op_sys.Cur().Tid_is_drd()) GxwElemFactory_.winForms_(); + GfsCore._.MsgParser_(GfoMsgParser_gfml._); + } + public static void Gfs_init() {GfsCore._.MsgParser_(GfoMsgParser_gfml._);} + public static IptCfg IptBndMgr_win; + public static void DoEvents() {;} + public static void ShowMsg(String message) {javax.swing.JOptionPane.showMessageDialog(null, message, "", javax.swing.JOptionPane.INFORMATION_MESSAGE, null);} + public static void BringToFront(ProcessAdp process) {} + public static void DoEvents(int milliseconds) { + ThreadAdp_.Sleep(milliseconds); + } + public static void Run(GfuiWin form) {javax.swing.SwingUtilities.invokeLater(new GfuiFormRunner(form));} + public static FontAdp System_font() { + try { + if (system_font == null) { + Font label_font = (Font)UIManager.get("Label.font"); + system_font = FontAdp.new_(label_font.getFamily(), label_font.getSize(), FontStyleAdp_.Plain); + } + return system_font; + } catch (Exception e) {return FontAdp.new_("Arial", 8, FontStyleAdp_.Plain);} + } + public static final String Quit_commit_evt = "quit_commit_evt", Quit_notify_evt = "quit_notify_evt"; + public static final String Err_GfuiException = "gplx.dbs.GfuiException"; // TODO: used in JAVA. move +} +class GfuiInterruptLnr implements UsrMsgWkr { + public void ExecUsrMsg(int type, UsrMsg umsg) {GfuiEnv_.ShowMsg(umsg.XtoStr());} + public static GfuiInterruptLnr new_() {return new GfuiInterruptLnr();} GfuiInterruptLnr() {} +} +class GfuiFormRunner implements Runnable { + public GfuiFormRunner(GfuiWin form) {this.form = form;} GfuiWin form; + public void run() { + form.Show(); + } +} +//#} \ No newline at end of file diff --git a/150_gfui/src_700_env/gplx/gfui/GfuiInvkCmd.java b/150_gfui/src_700_env/gplx/gfui/GfuiInvkCmd.java new file mode 100644 index 000000000..e83dbf103 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/GfuiInvkCmd.java @@ -0,0 +1,23 @@ +/* +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.gfui; import gplx.*; +public interface GfuiInvkCmd extends GfoInvkAble, RlsAble { +} +class GfuiInvkCmd_ { + public static final String Invk_sync = "Sync"; +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_clipboard.java b/150_gfui/src_700_env/gplx/gfui/Gfui_clipboard.java new file mode 100644 index 000000000..2362b71bd --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_clipboard.java @@ -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 . +*/ +package gplx.gfui; import gplx.*; +public interface Gfui_clipboard extends GfoInvkAble, RlsAble { + void Copy(String s); +} +class Gfui_clipboard_null implements Gfui_clipboard { + public void Copy(String s) {} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} + public void Rls() {} + public static final Gfui_clipboard_null Null = new Gfui_clipboard_null(); Gfui_clipboard_null() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_clipboard_.java b/150_gfui/src_700_env/gplx/gfui/Gfui_clipboard_.java new file mode 100644 index 000000000..ccdd78340 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_clipboard_.java @@ -0,0 +1,21 @@ +/* +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.gfui; import gplx.*; +public class Gfui_clipboard_ { + public static final String Invk_copy = "copy", Invk_select_all = "select_all"; +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_file.java b/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_file.java new file mode 100644 index 000000000..bdef62e86 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_file.java @@ -0,0 +1,33 @@ +/* +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.gfui; import gplx.*; +public interface Gfui_dlg_file { + Gfui_dlg_file Init_msg_(String v); + Gfui_dlg_file Init_file_(String v); + Gfui_dlg_file Init_dir_(Io_url v); + Gfui_dlg_file Init_exts_(String... v); + String Ask(); +} +class Gfui_dlg_file_null implements Gfui_dlg_file { + public Gfui_dlg_file Init_msg_(String v) {return this;} + public Gfui_dlg_file Init_file_(String v) {return this;} + public Gfui_dlg_file Init_dir_(Io_url v) {return this;} + public Gfui_dlg_file Init_exts_(String... v) {return this;} + public String Ask() {return "";} + public static final Gfui_dlg_file_null _ = new Gfui_dlg_file_null(); Gfui_dlg_file_null() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_msg.java b/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_msg.java new file mode 100644 index 000000000..9eed1b308 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_msg.java @@ -0,0 +1,33 @@ +/* +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.gfui; import gplx.*; +public interface Gfui_dlg_msg { + Gfui_dlg_msg Init_msg_(String v); + Gfui_dlg_msg Init_ico_(int v); + Gfui_dlg_msg Init_btns_(int... ary); + int Ask(); + boolean Ask(int expd); +} +class Gfui_dlg_msg_null implements Gfui_dlg_msg { + public Gfui_dlg_msg Init_msg_(String v) {return this;} + public Gfui_dlg_msg Init_ico_(int v) {return this;} + public Gfui_dlg_msg Init_btns_(int... ary) {return this;} + public boolean Ask(int expd) {return false;} + public int Ask() {return Int_.MinValue;} + public static final Gfui_dlg_msg_null _ = new Gfui_dlg_msg_null(); Gfui_dlg_msg_null() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_msg_.java b/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_msg_.java new file mode 100644 index 000000000..ab112941e --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_dlg_msg_.java @@ -0,0 +1,22 @@ +/* +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.gfui; import gplx.*; +public class Gfui_dlg_msg_ { + public static final int Ico_error = 0, Ico_information = 1, Ico_question = 2, Ico_warning = 3, Ico_working = 4; + public static final int Btn_ok = 0, Btn_cancel = 1, Btn_yes = 2, Btn_no = 3, Retry = 4, Btn_abort = 5, Btn_ignore = 6; +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_kit.java b/150_gfui/src_700_env/gplx/gfui/Gfui_kit.java new file mode 100644 index 000000000..cbaf67d19 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_kit.java @@ -0,0 +1,49 @@ +/* +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.gfui; import gplx.*; +public interface Gfui_kit extends GfoInvkAble { + byte Tid(); + String Key(); + void Cfg_set(String type, String key, Object val); + boolean Kit_init_done(); + void Kit_init(Gfo_usr_dlg gui_wtr); + void Kit_run(); + void Kit_term(); + void Kit_term_cbk_(GfoInvkAbleCmd cmd); + Gfui_clipboard Clipboard(); + void Ask_ok(String grp_key, String msg_key, String fmt, Object... args); + boolean Ask_yes_no(String grp_key, String msg_key, String fmt, Object... args); + boolean Ask_ok_cancel(String grp_key, String msg_key, String fmt, Object... args); + int Ask_yes_no_cancel(String grp_key, String msg_key, String fmt, Object... args); + GfuiInvkCmd New_cmd_sync(GfoInvkAble invk); + GfuiInvkCmd New_cmd_async(GfoInvkAble invk); + GfuiWin New_win_app(String key, KeyVal... args); + GfuiWin New_win_utl(String key, GfuiWin owner, KeyVal... args); + Gfui_html New_html(String key, GfuiElem owner, KeyVal... args); + Gfui_tab_mgr New_tab_mgr(String key, GfuiElem owner, KeyVal... args); + GfuiTextBox New_text_box(String key, GfuiElem owner, KeyVal... args); + GfuiBtn New_btn(String key, GfuiElem owner, KeyVal... args); + Gfui_dlg_file New_dlg_file(byte type, String msg); + Gfui_dlg_msg New_dlg_msg(String msg); + ImageAdp New_img_load(Io_url path); + Object New_color(int a, int r, int g, int b); + Gfui_mnu_grp New_mnu_popup(String key, GfuiElem owner); + Gfui_mnu_grp New_mnu_bar(String key, GfuiWin owner); + void Set_mnu_popup(GfuiElem owner, Gfui_mnu_grp grp); + float Calc_font_height(GfuiElem elem, String s); +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_kit_.java b/150_gfui/src_700_env/gplx/gfui/Gfui_kit_.java new file mode 100644 index 000000000..6cd3bb63d --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_kit_.java @@ -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 . +*/ +package gplx.gfui; import gplx.*; +public class Gfui_kit_ { + public static final byte Mem_tid = 0, Swing_tid = 1, Swt_tid = 2, Android_tid = 3; + public static Gfui_kit Mem() {return mem_kit;} private static final Gfui_kit mem_kit = Mem_kit._; + public static Gfui_kit Swt() {if (swt_kit == null) swt_kit = Swt_kit._; return swt_kit;} private static Gfui_kit swt_kit; // NOTE: late-binding else swing apps will fail (since swt jar is not deployed) + public static Gfui_kit Swing() {if (swing_kit == null) swing_kit = Swing_kit._; return swing_kit;} private static Gfui_kit swing_kit; + public static Gfui_kit Get_by_key(String key) { + if (String_.Eq(key, Mem().Key())) return Mem(); + else if (String_.Eq(key, Swt().Key())) return Swt(); + else if (String_.Eq(key, Swing().Key())) return Swing(); + else throw Err_.unhandled(key); + } + public static final String Cfg_HtmlBox = "HtmlBox"; + public static final byte File_dlg_type_open = 0, File_dlg_type_save = 1; +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_kit_base.java b/150_gfui/src_700_env/gplx/gfui/Gfui_kit_base.java new file mode 100644 index 000000000..46454b22d --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_kit_base.java @@ -0,0 +1,104 @@ +/* +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.gfui; import gplx.*; +public abstract class Gfui_kit_base implements Gfui_kit { + private KeyValHash ctor_args = KeyValHash.new_(); + public abstract byte Tid(); + public abstract String Key(); + public abstract GxwElemFactory_base Factory(); + public GfuiWin Main_win() {return main_win;} public Gfui_kit Main_win_(GfuiWin v) {main_win = v; return this;} private GfuiWin main_win; + public Gfui_clipboard Clipboard() {return Gfui_clipboard_null.Null;} + public GfoInvkAbleCmd Kit_term_cbk() {return kit_term_cbk;} public void Kit_term_cbk_(GfoInvkAbleCmd v) {kit_term_cbk = v;} private GfoInvkAbleCmd kit_term_cbk; + public void Cfg_set(String type, String key, Object val) {} + public boolean Kit_init_done() {return true;} + public void Kit_init(Gfo_usr_dlg gui_wtr) {} + @gplx.Virtual public void Kit_run() {} + @gplx.Virtual public void Kit_term() {kit_term_cbk.Invk();} + @gplx.Virtual public void Ask_ok(String grp_key, String msg_key, String fmt, Object... args) {} + public boolean Ask_yes_no(String grp_key, String msg_key, String fmt, Object... args) {return false;} + public int Ask_yes_no_cancel(String grp_key, String msg_key, String fmt, Object... args) {return Gfui_dlg_msg_.Btn_cancel;} + public boolean Ask_ok_cancel(String grp_key, String msg_key, String fmt, Object... args) {return false;} + public void Btn_img_(GfuiBtn btn, IconAdp v) {} + public GfuiInvkCmd New_cmd_sync(GfoInvkAble invk) {return new Gfui_kit_cmd_sync(invk);} + public GfuiInvkCmd New_cmd_async(GfoInvkAble invk) {return new Gfui_kit_cmd_async(invk);} + public GfuiWin New_win_app(String key, KeyVal... args) { + GfuiWin rv = GfuiWin_.kit_(this, key, this.Factory().win_app_(), ctor_args); + main_win = rv; + return rv; + } + public GfuiWin New_win_utl(String key, GfuiWin owner, KeyVal... args) {return GfuiWin_.kit_(this, key, this.Factory().win_tool_(ctor_args), ctor_args);} + @gplx.Virtual public Gfui_html New_html(String key, GfuiElem owner, KeyVal... args) { + Gfui_html rv = Gfui_html.kit_(this, key, this.New_html_impl(), ctor_args); + owner.SubElems().Add(rv); + return rv; + } + public Gfui_tab_mgr New_tab_mgr(String key, GfuiElem owner, KeyVal... args) { + Gfui_tab_mgr rv = Gfui_tab_mgr.kit_(this, key, this.New_tab_mgr_impl(), ctor_args); + owner.SubElems().Add(rv); + return rv; + } + public Gfui_tab_itm New_tab_itm(String key, Gfui_tab_mgr owner, KeyVal... args) { + Gfui_tab_itm rv = Gfui_tab_itm.kit_(this, key, this.New_tab_itm_impl(), ctor_args); + owner.SubElems().Add(rv); + return rv; + } + public GfuiTextBox New_text_box(String key, GfuiElem owner, KeyVal... args) { + GfuiTextBox rv = GfuiTextBox_.kit_(this, key, this.Factory().text_fld_(), ctor_args); + owner.SubElems().Add(rv); + return rv; + } + @gplx.Virtual public GfuiBtn New_btn(String key, GfuiElem owner, KeyVal... args) { + GfuiBtn rv = GfuiBtn_.kit_(this, key, New_btn_impl(), ctor_args); + owner.SubElems().Add(rv); + return rv; + } + @gplx.Virtual public GfuiStatusBox New_status_box(String key, GfuiElem owner, KeyVal... args) { + GfuiStatusBox rv = GfuiStatusBox_.kit_(this, key, this.Factory().text_memo_()); + owner.SubElems().Add(rv); + return rv; + } + public void Set_mnu_popup(GfuiElem owner, Gfui_mnu_grp grp) {} + protected abstract Gxw_html New_html_impl(); + protected abstract Gxw_tab_mgr New_tab_mgr_impl(); + protected abstract Gxw_tab_itm New_tab_itm_impl(); + protected abstract GxwElem New_btn_impl(); + @gplx.Virtual public Gfui_dlg_file New_dlg_file(byte type, String msg) {return Gfui_dlg_file_null._;} + @gplx.Virtual public Gfui_dlg_msg New_dlg_msg(String msg) {return Gfui_dlg_msg_null._;} + @gplx.Virtual public Gfui_mnu_grp New_mnu_popup(String key, GfuiElem owner) {return Gfui_mnu_grp_null.Null;} + @gplx.Virtual public Gfui_mnu_grp New_mnu_bar(String key, GfuiWin owner) {return Gfui_mnu_grp_null.Null;} + public abstract ImageAdp New_img_load(Io_url url); + public Object New_color(int a, int r, int g, int b) {return null;} + public float Calc_font_height(GfuiElem elem, String s) {return 13;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + return this; + } +} +class Gfui_kit_cmd_sync implements GfuiInvkCmd { + public Gfui_kit_cmd_sync(GfoInvkAble target) {this.target = target;} private GfoInvkAble target; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + return target.Invk(ctx, ikey, k, m); + } + public void Rls() {target = null;} +} +class Gfui_kit_cmd_async implements GfuiInvkCmd { + public Gfui_kit_cmd_async(GfoInvkAble target) {this.target = target;} private GfoInvkAble target; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + return target.Invk(ctx, ikey, k, m); + } + public void Rls() {target = null;} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_grp.java b/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_grp.java new file mode 100644 index 000000000..adacabb5e --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_grp.java @@ -0,0 +1,46 @@ +/* +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.gfui; import gplx.*; +public interface Gfui_mnu_grp extends Gfui_mnu_itm { + String Root_key(); + void Itms_clear(); + Gfui_mnu_itm Itms_add_btn_cmd (String txt, ImageAdp img, GfoInvkAble invk, String invk_cmd); + Gfui_mnu_itm Itms_add_btn_msg (String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg); + Gfui_mnu_itm Itms_add_chk_msg (String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg_n, GfoMsg msg_y); + Gfui_mnu_itm Itms_add_rdo_msg (String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg); + Gfui_mnu_grp Itms_add_grp (String txt, ImageAdp img); + Gfui_mnu_itm Itms_add_separator(); +} +class Gfui_mnu_grp_null implements Gfui_mnu_grp { + public String Uid() {return "";} + public int Tid() {return Gfui_mnu_itm_.Tid_grp;} + public boolean Enabled() {return true;} public void Enabled_(boolean v) {} + public String Text() {return null;} public void Text_(String v) {} + public ImageAdp Img() {return null;} public void Img_(ImageAdp v) {} + public boolean Selected() {return true;} public void Selected_(boolean v) {} + public String Root_key() {return "null";} + public Object Under() {return null;} + public void Itms_clear() {} + public Gfui_mnu_itm Itms_add_btn_cmd (String txt, ImageAdp img, GfoInvkAble invk, String invk_cmd) {return Gfui_mnu_itm_null.Null;} + public Gfui_mnu_itm Itms_add_btn_msg (String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg invk_msg) {return Gfui_mnu_itm_null.Null;} + public Gfui_mnu_itm Itms_add_chk_msg (String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg_n, GfoMsg msg_y) {return Gfui_mnu_itm_null.Null;} + public Gfui_mnu_itm Itms_add_rdo_msg (String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg) {return Gfui_mnu_itm_null.Null;} + public Gfui_mnu_grp Itms_add_grp(String txt, ImageAdp img) {return Gfui_mnu_grp_null.Null;} + public Gfui_mnu_itm Itms_add_separator() {return Gfui_mnu_itm_null.Null;} + public static final Gfui_mnu_grp_null Null = new Gfui_mnu_grp_null(); Gfui_mnu_grp_null() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_itm.java b/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_itm.java new file mode 100644 index 000000000..9b4e8e8e6 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_itm.java @@ -0,0 +1,37 @@ +/* +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.gfui; import gplx.*; +public interface Gfui_mnu_itm { + int Tid(); + String Uid(); + boolean Enabled(); void Enabled_(boolean v); + String Text(); void Text_(String v); + ImageAdp Img(); void Img_(ImageAdp v); + boolean Selected(); void Selected_(boolean v); + Object Under(); +} +class Gfui_mnu_itm_null implements Gfui_mnu_itm { + public String Uid() {return "";} + public int Tid() {return Gfui_mnu_itm_.Tid_btn;} + public boolean Enabled() {return true;} public void Enabled_(boolean v) {} + public String Text() {return text;} public void Text_(String v) {text = v;} private String text; + public ImageAdp Img() {return img;} public void Img_(ImageAdp v) {img = v;} private ImageAdp img; + public boolean Selected() {return true;} public void Selected_(boolean v) {} + public Object Under() {return null;} + public static final Gfui_mnu_itm_null Null = new Gfui_mnu_itm_null(); Gfui_mnu_itm_null() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_itm_.java b/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_itm_.java new file mode 100644 index 000000000..49c75e69d --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Gfui_mnu_itm_.java @@ -0,0 +1,22 @@ +/* +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.gfui; import gplx.*; +public class Gfui_mnu_itm_ { + public static String Gen_uid() {return "mnu_" + Int_.XtoStr(++uid_next);} private static int uid_next = 0; + public static final int Tid_nil = 0, Tid_grp = 1, Tid_spr = 2, Tid_btn = 3, Tid_chk = 4, Tid_rdo = 5; +} diff --git a/150_gfui/src_700_env/gplx/gfui/Mem_html.java b/150_gfui/src_700_env/gplx/gfui/Mem_html.java new file mode 100644 index 000000000..4322552b5 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Mem_html.java @@ -0,0 +1,138 @@ +/* +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.gfui; import gplx.*; +class Mem_html extends GxwTextMemo_lang implements Gxw_html { public String Html_doc_html() { + return String_.Replace(this.TextVal(), "\r\n", "\n"); + } + public String Html_doc_selected_get_sub_atr(String tag, String sub_tag, int sub_idx, String sub_atr) {return "";} + public String Html_doc_selected_get(String host, String page) {return "";} + public String Html_doc_selected_get_text_or_href() {return "";} + public String Html_doc_selected_get_href_or_text() {return "";} + public String Html_doc_selected_get_src_or_empty() {return "";} + public boolean Html_window_print_preview() {return false;} + public void Html_invk_src_(GfoEvObj v) {} + public String Html_elem_atr_get_str(String elem_id, String atr_key) { + if (String_.Eq(atr_key, Gfui_html.Atr_value)) return String_.Replace(this.TextVal(), "\r\n", "\n"); + else throw Err_.unhandled(atr_key); + } + public Object Html_elem_atr_get_obj(String elem_id, String atr_key) { + if (String_.Eq(atr_key, Gfui_html.Atr_value)) return String_.Replace(this.TextVal(), "\r\n", "\n"); + else throw Err_.unhandled(atr_key); + } + public boolean Html_elem_atr_get_bool(String elem_id, String atr_key) { + if (String_.Eq(atr_key, Gfui_html.Atr_value)) return Bool_.parse_(String_.Replace(this.TextVal(), "\r\n", "\n")); + else throw Err_.unhandled(atr_key); + } + public boolean Html_elem_atr_set(String elem_id, String atr_key, String v) { + if (String_.Eq(atr_key, Gfui_html.Atr_value)) this.TextVal_set(v); + else throw Err_.unhandled(atr_key); + return true; + } + public boolean Html_elem_atr_set_append(String elem_id, String atr_key, String append) { + if (String_.Eq(atr_key, Gfui_html.Atr_value)) this.TextVal_set(this.TextVal() + append); + else throw Err_.unhandled(atr_key); + return true; + } + public void Html_doc_html_(String s) { +// this.Core().ForeColor_set(plainText ? ColorAdp_.Black : ColorAdp_.Gray); + s = String_.Replace(s, "\r", ""); + s = String_.Replace(s, "\n", "\r\n"); + this.TextVal_set(s); + this.SelBgn_set(0); + } + public String Html_active_atr_get_str(String atrKey, String or) { // NOTE: fuzzy way of finding current href; EX: b + String txt = this.TextVal(); + int pos = this.SelBgn(); + String rv = ExtractAtr(atrKey, txt, pos); + return rv == null ? or : rv; + } + public void Html_doc_body_focus() {} + public String Html_window_vpos() {return "";} + public boolean Html_window_vpos_(String v) {return true;} + public boolean Html_elem_focus(String v) {return true;} + public boolean Html_elem_img_update(String elem_id, String elem_src, int elem_width, int elem_height) {return true;} + public boolean Html_elem_delete(String elem_id) {return true;} + public boolean Html_elem_replace_html(String id, String html) {return true;} + public boolean Html_gallery_packed_exec() {return true;} + public String Html_js_eval_script(String script) {return "";} + String ExtractAtr(String key, String txt, int pos) { + int key_pos = String_.FindBwd(txt, key, pos); if (key_pos == String_.Find_none) return null; + int q0 = String_.FindFwd(txt, "\"", key_pos); if (q0 == String_.Find_none) return null; + int q1 = String_.FindFwd(txt, "\"", q0 + 1); if (q1 == String_.Find_none) return null; + if (!Int_.Between(pos, q0, q1)) return null; // current pos is not between nearest quotes + return String_.Mid(txt, q0 + 1, q1); + } + public boolean Html_doc_find(String elem_id, String find, boolean dir_fwd, boolean case_match, boolean wrap_find) { +// String txt = this.TextVal(); +// int pos = this.SelBgn(); +// int bgn = String_.FindFwd(txt, find, pos); if (bgn == String_.Find_none) return false; +// if (bgn == pos) { +// bgn = String_.FindFwd(txt, find, pos + 1); +// if (bgn == String_.Find_none) { +// bgn = String_.FindFwd(txt, find, 0); +// if (bgn == String_.Find_none) return false; +// } +// } +// this.SelBgn_set(bgn); +// this.SelLen_set(String_.Len(find)); +// this.ScrollTillSelectionStartIsFirstLine(); + txtFindMgr.Text_(this.TextVal()); + int cur = this.SelBgn(); + int[] ary = txtFindMgr.FindByUi(find, this.SelBgn(), this.SelLen(), false); + if (ary[0] != cur) { + this.SelBgn_set(ary[0]); + this.SelLen_set(ary[1]); + this.ScrollTillCaretIsVisible(); + } + else { + ary = txtFindMgr.FindByUi(find, this.SelBgn() + 1, 0, false); + if (ary[0] != 0) { + this.SelBgn_set(ary[0]); + this.SelLen_set(ary[1]); + this.ScrollTillCaretIsVisible(); +// this.ScrollTillSelectionStartIsFirstLine(); + } + } + return true; + } + public boolean Html_elem_scroll_into_view(String id) {return false;} + public void Html_js_enabled_(boolean v) {} + public void Html_js_eval_proc(String proc, String... args) {} + public void Html_js_cbks_add(String js_func_name, GfoInvkAble invk) {} + private TxtFindMgr txtFindMgr = new TxtFindMgr(); + public Mem_html() { + this.ctor_MsTextBoxMultiline_(); + } +} +class Mem_tab_mgr extends GxwElem_mock_base implements Gxw_tab_mgr { public ColorAdp Btns_selected_color() {return btns_selected_color;} public void Btns_selected_color_(ColorAdp v) {btns_selected_color = v;} private ColorAdp btns_selected_color; + public ColorAdp Btns_unselected_color() {return btns_unselected_color;} public void Btns_unselected_color_(ColorAdp v) {btns_unselected_color = v;} private ColorAdp btns_unselected_color; + public Gxw_tab_itm Tabs_add(Gfui_tab_itm_data tab_data) {return new Mem_tab_itm();} + public void Tabs_select_by_idx(int i) {} + public void Tabs_close_by_idx(int i) {} + public void Tabs_switch(int src, int trg) {} + public int Btns_height() {return 0;} public void Btns_height_(int v) {} + public boolean Btns_place_on_top() {return false;} public void Btns_place_on_top_(boolean v) {} + public boolean Btns_curved() {return false;} public void Btns_curved_(boolean v) {} + public boolean Btns_close_visible() {return false;} public void Btns_close_visible_(boolean v) {} + public boolean Btns_unselected_close_visible() {return false;} public void Btns_unselected_close_visible_(boolean v) {} +} +class Mem_tab_itm extends GxwElem_mock_base implements Gxw_tab_itm { public void Subs_add(GfuiElem sub) {} + public Gfui_tab_itm_data Tab_data() {return tab_data;} private Gfui_tab_itm_data tab_data = new Gfui_tab_itm_data("null", -1); + public String Tab_name() {return tab_name;} public void Tab_name_(String v) {tab_name = v;} private String tab_name; + public String Tab_tip_text() {return tab_tip_text;} public void Tab_tip_text_(String v) {tab_tip_text = v;} private String tab_tip_text; +} diff --git a/150_gfui/src_700_env/gplx/gfui/Mem_kit.java b/150_gfui/src_700_env/gplx/gfui/Mem_kit.java new file mode 100644 index 000000000..9431a54bd --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Mem_kit.java @@ -0,0 +1,38 @@ +/* +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.gfui; import gplx.*; +public class Mem_kit extends Gfui_kit_base { + @Override public byte Tid() {return Gfui_kit_.Mem_tid;} + @Override public String Key() {return "mem";} + @Override public GxwElemFactory_base Factory() {return factory;} private GxwElemFactory_cls_mock factory = new GxwElemFactory_cls_mock(); + public void New_html_impl_prototype_(Gxw_html v) {html_impl_prototype = v;} private Gxw_html html_impl_prototype; + @Override public Gfui_html New_html(String key, GfuiElem owner, KeyVal... args) { + if (html_impl_prototype == null) + return super.New_html(key, owner, args); + else { + Gfui_html rv = Gfui_html.mem_(key, html_impl_prototype); + return rv; + } + } + @Override protected Gxw_html New_html_impl() {return html_impl_prototype == null ? new Mem_html(): html_impl_prototype;} + @Override protected Gxw_tab_mgr New_tab_mgr_impl() {return new Mem_tab_mgr();} + @Override protected Gxw_tab_itm New_tab_itm_impl() {return new Mem_tab_itm();} + @Override protected GxwElem New_btn_impl() {return factory.control_();} + @Override public ImageAdp New_img_load(Io_url url) {return ImageAdp_null._;} + public static final Mem_kit _ = new Mem_kit(); Mem_kit() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Swing_kit.java b/150_gfui/src_700_env/gplx/gfui/Swing_kit.java new file mode 100644 index 000000000..a671c8932 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Swing_kit.java @@ -0,0 +1,33 @@ +/* +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.gfui; import gplx.*; +public class Swing_kit extends Gfui_kit_base { + private Bry_fmtr ask_fmtr = Bry_fmtr.new_(); private Bry_bfr ask_bfr = Bry_bfr.new_(); + @Override public byte Tid() {return Gfui_kit_.Swing_tid;} + @Override public String Key() {return "swing";} + @Override public GxwElemFactory_base Factory() {return factory;} private GxwElemFactory_cls_lang factory = new GxwElemFactory_cls_lang(); + @Override public void Ask_ok(String grp_key, String msg_key, String fmt, Object... args) {GfuiEnv_.ShowMsg(ask_fmtr.Bld_str_many(ask_bfr, fmt, args));} + @Override public void Kit_run() {GfuiEnv_.Run(this.Main_win());} + @Override public void Kit_term() {this.Kit_term_cbk().Invk(); GfuiEnv_.Exit();} + @Override public ImageAdp New_img_load(Io_url url) {return ImageAdp_.file_(url);} + @Override protected Gxw_html New_html_impl() {return new Mem_html();} + @Override protected Gxw_tab_mgr New_tab_mgr_impl() {return new Mem_tab_mgr();} + @Override protected Gxw_tab_itm New_tab_itm_impl() {return new Mem_tab_itm();} + @Override protected GxwElem New_btn_impl() {return factory.control_();} + public static final Swing_kit _ = new Swing_kit(); Swing_kit() {} +} diff --git a/150_gfui/src_700_env/gplx/gfui/Swt_kit.java b/150_gfui/src_700_env/gplx/gfui/Swt_kit.java new file mode 100644 index 000000000..46d904901 --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/Swt_kit.java @@ -0,0 +1,280 @@ +/* +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.gfui; import gplx.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Cursor; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +public class Swt_kit implements Gfui_kit { + private String xulRunnerPath = null; + private KeyValHash ctor_args = KeyValHash.new_(); private HashAdp kit_args = HashAdp_.new_(); + private KeyValHash nullArgs = KeyValHash.new_(); + public byte Tid() {return Gfui_kit_.Swt_tid;} + public String Key() {return "swt";} + public Gfui_clipboard Clipboard() {return clipboard;} private Swt_clipboard clipboard; + public Display Swt_display() {return display;} private Display display; + public boolean Mode_is_shutdown() {return mode_is_shutdown;} private boolean mode_is_shutdown = false; + public Gfui_html_cfg Html_cfg() {return html_cfg;} private Gfui_html_cfg html_cfg = new Gfui_html_cfg(); + public void Cfg_set(String type, String key, Object val) { + if (String_.Eq(type, Gfui_kit_.Cfg_HtmlBox)) { + if (String_.Eq(key, "XulRunnerPath")) { + xulRunnerPath = (String)val; + return; + } + } + KeyValHash typeCfg = (KeyValHash)kit_args.Fetch(type); + if (typeCfg == null) { + typeCfg = KeyValHash.new_(); + kit_args.Add(type, typeCfg); + } + typeCfg.AddReplace(key, val); + } + public boolean Kit_init_done() {return kit_init_done;} private boolean kit_init_done; + public void Kit_init(Gfo_usr_dlg gui_wtr) { + this.gui_wtr = gui_wtr; + usrMsgWkr_Stop = new Swt_UsrMsgWkr_Stop(this, gui_wtr); + display = new Display(); + UsrDlg_._.Reg(UsrMsgWkr_.Type_Warn, GfoConsoleWin._); + UsrDlg_._.Reg(UsrMsgWkr_.Type_Stop, usrMsgWkr_Stop); + clipboard = new Swt_clipboard(display); + if (xulRunnerPath != null) System.setProperty("org.eclipse.swt.browser.XULRunnerPath", xulRunnerPath); + kit_init_done = true; + gui_wtr.Log_many("", "", "swt.kit.init.done"); + } private Gfo_usr_dlg gui_wtr; + public void Kit_term_cbk_(GfoInvkAbleCmd v) {this.term_cbk = v;} GfoInvkAbleCmd term_cbk = GfoInvkAbleCmd.Null; + public void Kit_run() { + shell.addListener(SWT.Close, new Swt_lnr_shell_close(this)); + shell.open(); + Cursor cursor = new Cursor(display, SWT.CURSOR_ARROW); + shell.setCursor(cursor); // set cursor to hand else cursor defaults to Hourglass until mouse is moved; DATE: 2014-01-31 + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + cursor.dispose(); + Kit_term(); + } + public void Kit_term() { + mode_is_shutdown = true; // NOTE: must mark kit as shutting down, else writing to status.bar will create stack overflow exception; DATE:2014-05-05 + usrMsgWkr_Stop.Rls(); + clipboard.Rls(); + display.dispose(); + } private Swt_UsrMsgWkr_Stop usrMsgWkr_Stop; + public boolean Ask_yes_no(String grp_key, String msg_key, String fmt, Object... args) { + Swt_dlg_msg dlg = (Swt_dlg_msg)New_dlg_msg(ask_fmtr.Bld_str_many(ask_bfr, fmt, args)).Init_btns_(Gfui_dlg_msg_.Btn_yes, Gfui_dlg_msg_.Btn_no).Init_ico_(Gfui_dlg_msg_.Ico_question); + display.syncExec(dlg); + return dlg.Ask_rslt == Gfui_dlg_msg_.Btn_yes; + } + public boolean Ask_ok_cancel(String grp_key, String msg_key, String fmt, Object... args) { + Swt_dlg_msg dlg = (Swt_dlg_msg)New_dlg_msg(ask_fmtr.Bld_str_many(ask_bfr, fmt, args)).Init_btns_(Gfui_dlg_msg_.Btn_ok, Gfui_dlg_msg_.Btn_cancel).Init_ico_(Gfui_dlg_msg_.Ico_question); + display.syncExec(dlg); + return dlg.Ask_rslt == Gfui_dlg_msg_.Btn_ok; + } Bry_fmtr ask_fmtr = Bry_fmtr.new_().Fail_when_invalid_escapes_(false); Bry_bfr ask_bfr = Bry_bfr.new_(); + public int Ask_yes_no_cancel(String grp_key, String msg_key, String fmt, Object... args) { + Swt_dlg_msg dlg = (Swt_dlg_msg)New_dlg_msg(ask_fmtr.Bld_str_many(ask_bfr, fmt, args)).Init_btns_(Gfui_dlg_msg_.Btn_yes, Gfui_dlg_msg_.Btn_no, Gfui_dlg_msg_.Btn_cancel).Init_ico_(Gfui_dlg_msg_.Ico_question); + display.syncExec(dlg); + return dlg.Ask_rslt; + } + public void Ask_ok(String grp_key, String msg_key, String fmt, Object... args) { + Swt_dlg_msg dlg = (Swt_dlg_msg)New_dlg_msg(ask_fmtr.Bld_str_many(ask_bfr, fmt, args)).Init_btns_(Gfui_dlg_msg_.Btn_ok).Init_ico_(Gfui_dlg_msg_.Ico_information); + display.syncExec(dlg); + } + public GfuiInvkCmd New_cmd_sync(GfoInvkAble invk) {return new Swt_gui_cmd(this, gui_wtr, display, invk, Bool_.N);} + public GfuiInvkCmd New_cmd_async(GfoInvkAble invk) {return new Swt_gui_cmd(this, gui_wtr, display, invk, Bool_.Y);} + public GfuiWin New_win_utl(String key, GfuiWin owner, KeyVal... args) {return GfuiWin_.kit_(this, key, new Swt_win(shell), nullArgs); } + public GfuiWin New_win_app(String key, KeyVal... args) { + Swt_win win = new Swt_win(display); + this.shell = win.UnderShell(); + shell.setLayout(null); + GfuiWin rv = GfuiWin_.kit_(this, key, win, nullArgs); + main_win = rv; + return rv; + } Shell shell; GfuiWin main_win; + public GfuiBtn New_btn(String key, GfuiElem owner, KeyVal... args) { + GfuiBtn rv = GfuiBtn_.kit_(this, key, new Swt_btn_no_border(Swt_control_.cast_or_fail(owner), ctor_args), ctor_args); + owner.SubElems().Add(rv); + return rv; + } + public Gfui_html New_html(String key, GfuiElem owner, KeyVal... args) { + ctor_args.Clear(); + Object htmlBox_args_obj = kit_args.Fetch(Gfui_kit_.Cfg_HtmlBox); + if (htmlBox_args_obj != null) { + KeyValHash htmlBox_args = (KeyValHash)htmlBox_args_obj; + KeyVal browser_type = htmlBox_args.FetchOrNull(Cfg_Html_BrowserType); + if (browser_type != null) ctor_args.Add(browser_type); + } + Gfui_html rv = Gfui_html.kit_(this, key, new Swt_html(this, Swt_control_.cast_or_fail(owner), ctor_args), ctor_args); + ((Swt_html)rv.UnderElem()).Under_control().addMenuDetectListener(new Swt_lnr__menu_detect(rv)); + rv.Owner_(owner); + return rv; + } + public Gfui_tab_mgr New_tab_mgr(String key, GfuiElem owner, KeyVal... args) { + ctor_args.Clear(); + Swt_tab_mgr rv_swt = new Swt_tab_mgr(this, Swt_control_.cast_or_fail(owner), ctor_args); + Gfui_tab_mgr rv = Gfui_tab_mgr.kit_(this, key, rv_swt, ctor_args); + rv.Owner_(owner); + rv_swt.EvMgr_(rv.EvMgr()); + return rv; + } + public GfuiTextBox New_text_box(String key, GfuiElem owner, KeyVal... args) { + ctor_args.Clear(); + int args_len = args.length; + for (int i = 0; i < args_len; i++) + ctor_args.Add(args[i]); + boolean border_on = Bool_.cast_(ctor_args.FetchValOr(GfuiTextBox.CFG_border_on_, true)); + GxwTextFld under = new Swt_text_w_border(Swt_control_.cast_or_fail(owner), New_color(border_on ? ColorAdp_.LightGray : ColorAdp_.White), ctor_args); + GfuiTextBox rv = GfuiTextBox_.kit_(this, key, under, ctor_args); + rv.Owner_(owner); + ctor_args.Clear(); + return rv; + } + public GfuiStatusBox New_status_box(String key, GfuiElem owner, KeyVal... args) { + ctor_args.Clear(); + GfuiStatusBox rv = GfuiStatusBox_.kit_(this, key, new Swt_text(Swt_control_.cast_or_fail(owner), ctor_args)); + rv.Owner_(owner); + return rv; + } + public Gfui_dlg_file New_dlg_file(byte type, String msg) {return new Swt_dlg_file(type, shell).Init_msg_(msg);} + public Gfui_dlg_msg New_dlg_msg(String msg) {return new Swt_dlg_msg(shell).Init_msg_(msg);} + public ImageAdp New_img_load(Io_url url) { + if (url == Io_url_.Null) return ImageAdp_.Null; + Image img = new Image(display, url.Raw()); + Rectangle rect = img.getBounds(); + return new Swt_img(this, img, rect.width, rect.height).Url_(url); + } + public Color New_color(ColorAdp v) {return (Color)New_color(v.Alpha(), v.Red(), v.Green(), v.Blue());} + public Object New_color(int a, int r, int g, int b) {return new Color(display, r, g, b);} + public Gfui_mnu_grp New_mnu_popup(String key, GfuiElem owner) {return Swt_popup_grp.new_popup(key, owner);} + public Gfui_mnu_grp New_mnu_bar(String key, GfuiWin owner) {return Swt_popup_grp.new_bar(key, owner);} + public float Calc_font_height(GfuiElem elem, String s) { + if (String_.Len_eq_0(s)) return 8; + String old_text = elem.Text(); + elem.Text_(s); + float rv = ((Swt_text_w_border)(elem.UnderElem())).Under_text().getFont().getFontData()[0].height; + shell.setText(old_text); + return rv; + } + public void Set_mnu_popup(GfuiElem owner, Gfui_mnu_grp grp) { + Control control = Swt_control_.cast_or_fail(owner).Under_menu_control(); + Swt_popup_grp popup = (Swt_popup_grp)grp; + control.setMenu(popup.Under_menu()); + } + public static final Swt_kit _ = new Swt_kit(); private Swt_kit() {} // singleton b/c of following line "In particular, some platforms which SWT supports will not allow more than one active display" (http://help.eclipse.org/indigo/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Display.html) + public static final String Cfg_Html_BrowserType = "BrowserType"; + public static int Cfg_Html_BrowserType_parse(String v) { + if (String_.Eq(v, "mozilla")) return Swt_html.Browser_tid_mozilla; + else return Swt_html.Browser_tid_none; + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (String_.Eq(k, Invk_Cfg_add)) { + String type = m.ReadStrOr("type", ""); + String key = m.ReadStrOr("key", ""); + String val = m.ReadStrOr("val", ""); + if (ctx.Deny()) return this; + if (String_.Eq(type, Gfui_kit_.Cfg_HtmlBox)) { + if (String_.Eq(key, "XulRunnerPath")) + xulRunnerPath = val; + else if (String_.Eq(key, Swt_kit.Cfg_Html_BrowserType)) + Cfg_set(type, Swt_kit.Cfg_Html_BrowserType, Cfg_Html_BrowserType_parse(val)); + } + } + else if (String_.Eq(k, Invk_HtmlBox)) {return html_cfg;} + else if (String_.Eq(k, Invk_ask_file)) return this.New_dlg_file(Gfui_kit_.File_dlg_type_open, m.Args_getAt(0).Val_to_str_or_empty()).Ask(); + return this; + } public static final String Invk_Cfg_add = "Cfg_add", Invk_HtmlBox = "HtmlBox", Invk_ask_file = "ask_file"; + public static boolean Html_box_focus_automatically = false; + public static FontAdp Control_font_get(Font font, GxwCore_base owner) { + FontData fontData = font.getFontData()[0]; + FontAdp rv = FontAdp.new_(fontData.getName(), fontData.getHeight(), FontStyleAdp_.lang_(fontData.getStyle())); // NOTE: swt style constants match swing + rv.OwnerGxwCore_(owner); + return rv; + } + public static void Control_font_set(FontAdp font, GxwCore_base owner, Control control) { + font.OwnerGxwCore_(owner); + FontData fontData = new FontData(font.Name(), (int)font.size, font.Style().Val()); + Font rv = new Font(control.getDisplay(), fontData); + control.setFont(rv); + } +} +class Swt_lnr_shell_close implements Listener { + public Swt_lnr_shell_close(Swt_kit kit) {this.kit = kit;} private Swt_kit kit; + @Override public void handleEvent(Event event) { + if (kit.term_cbk.Cmd() == null) return; // close_cmd not defined + boolean rslt = Bool_.cast_(kit.term_cbk.Invk()); + if (!rslt) + event.doit = false; + } +} +class Swt_UsrMsgWkr_Stop implements UsrMsgWkr, RlsAble { + public Swt_UsrMsgWkr_Stop(Swt_kit kit, Gfo_usr_dlg gui_wtr) {this.kit = kit; this.gui_wtr = gui_wtr;} Swt_kit kit; Gfo_usr_dlg gui_wtr; + @Override public void Rls() {this.kit = null;} + public void ExecUsrMsg(int type, UsrMsg umsg) { + String msg = umsg.XtoStr(); + kit.Ask_ok("xowa.gui", "stop", msg); + gui_wtr.Log_many("", "", msg); + } +} +class Swt_gui_cmd implements GfuiInvkCmd, Runnable { + private Swt_kit kit; private Gfo_usr_dlg usr_dlg; private GfoInvkAble target; private Display display; private boolean async; + private GfsCtx invk_ctx; private int invk_ikey; private String invk_key; private GfoMsg invk_msg; + public Swt_gui_cmd(Swt_kit kit, Gfo_usr_dlg usr_dlg, Display display, GfoInvkAble target, boolean async) { + this.kit = kit; this.usr_dlg = usr_dlg; this.display = display; this.target = target; this.async = async; + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + this.invk_ctx = ctx; this.invk_ikey = ikey ; this.invk_key = k; this.invk_msg = m; + if (async) + display.asyncExec(this); + else + display.syncExec(this); + return this; + } + @Override public void run() { + try { + target.Invk(invk_ctx, invk_ikey, invk_key, invk_msg); + } + catch (Exception e) { + if (kit.Mode_is_shutdown()) return; // NOTE: if shutting down, don't warn; warn will try to write to status.bar, which will fail b/c SWT is shutting down; failures will try to write to status.bar again, causing StackOverflow exception; DATE:2014-05-04 + usr_dlg.Warn_many("", "", "fatal error while running; key=~{0} err=~{1}", invk_key, Err_.Message_gplx_brief(e)); + } + } + public void Rls() { + usr_dlg = null; target = null; display = null; + invk_ctx = null; invk_key = null; invk_msg = null; + } +} diff --git a/150_gfui/src_700_env/gplx/gfui/TxtFindMgr.java b/150_gfui/src_700_env/gplx/gfui/TxtFindMgr.java new file mode 100644 index 000000000..e0dfc7cbe --- /dev/null +++ b/150_gfui/src_700_env/gplx/gfui/TxtFindMgr.java @@ -0,0 +1,48 @@ +/* +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.gfui; import gplx.*; +public class TxtFindMgr { + public String Text() {return text;} + public TxtFindMgr Text_(String v) { + if (!caseSensitive) v = String_.Lower(v); + text = v; + return this; + } String text; + public boolean CaseSensitive() {return caseSensitive;} public TxtFindMgr CaseSensitive_(boolean v) {caseSensitive = v; return this;} private boolean caseSensitive = false; + public int[] FindByUi(String findText, int selBgn, int selLen, boolean keyIsEnter) { + int[] rv = new int[2]; + if (String_.Eq(findText, "")) return rv; // make newSel = 0 b/c all text deleted; else, find will continue from last selBgn; easy way to "reset" + rv[0] = selBgn; rv[1] = selLen; // make newSel = curSel + int adj = keyIsEnter ? 1 : 0; // if enter, search next, else search from cur; else will add to selLen if at match; ex: ab->c at abc will keep same selBgn, but increase selLen to 3 + int findPos = FindNext(findText, selBgn + adj); + if (findPos == String_.Find_none) { // nothing found; set selLen to 0 and return + rv[1] = 0; + return rv; + } + rv[0] = findPos; + rv[1] = String_.Len(findText); + return rv; + } + public int FindNext(String find, int guiPos) { + if (!caseSensitive) find = String_.Lower(find); + int findPos = String_.FindFwd(text, find, guiPos); + if (findPos == String_.Find_none && guiPos != 0) + findPos = String_.FindFwd(text, find, 0); + return findPos; + } +} diff --git a/150_gfui/tst/gplx/gfui/ClipboardAdp__tst.java b/150_gfui/tst/gplx/gfui/ClipboardAdp__tst.java new file mode 100644 index 000000000..24ff8a4cb --- /dev/null +++ b/150_gfui/tst/gplx/gfui/ClipboardAdp__tst.java @@ -0,0 +1,26 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class ClipboardAdp__tst { + @Test public void Basic() { + ClipboardAdp_.SetText("test"); + Tfds.Eq(true, ClipboardAdp_.IsText()); + Tfds.Eq("test", ClipboardAdp_.GetText()); + } +} diff --git a/150_gfui/tst/gplx/gfui/GfuiBorderMgr_tst.java b/150_gfui/tst/gplx/gfui/GfuiBorderMgr_tst.java new file mode 100644 index 000000000..fe699713e --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfuiBorderMgr_tst.java @@ -0,0 +1,53 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class GfuiBorderMgr_tst { + @Before public void setup() { + borderMgr = GfuiBorderMgr.new_(); + } + @Test public void NullToEdge() { // all null -> one edge + tst_Eq(borderMgr, null, null, null, null, null); + + borderMgr.Top_(red); + tst_Eq(borderMgr, null, red, null, null, null); + } + @Test public void EdgeToAll() { // one edge -> all edge + borderMgr.Top_(red); + tst_Eq(borderMgr, null, red, null, null, null); + + borderMgr.All_(black); + tst_Eq(borderMgr, black, null, null, null, null); + } + @Test public void AllToEdge() { // all edge -> one new; three old + borderMgr.All_(red); + tst_Eq(borderMgr, red, null, null, null, null); + + borderMgr.Top_(black); + tst_Eq(borderMgr, null, black, red, red, red); + } + void tst_Eq(GfuiBorderMgr borderMgr, PenAdp all, PenAdp top, PenAdp left, PenAdp right, PenAdp bottom) { + Tfds.Eq(borderMgr.All(), all); + Tfds.Eq(borderMgr.Top(), top); + Tfds.Eq(borderMgr.Left(), left); + Tfds.Eq(borderMgr.Right(), right); + Tfds.Eq(borderMgr.Bot(), bottom); + } + GfuiBorderMgr borderMgr; + PenAdp black = PenAdp_.black_(), red = PenAdp_.new_(ColorAdp_.Red, 1); +} diff --git a/150_gfui/tst/gplx/gfui/GfuiClickKeyMgr_tst.java b/150_gfui/tst/gplx/gfui/GfuiClickKeyMgr_tst.java new file mode 100644 index 000000000..9b8d37b92 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfuiClickKeyMgr_tst.java @@ -0,0 +1,31 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class GfuiClickKeyMgr_tst { + @Test public void ExtractKeyFromText() { + tst_ExtractKey("&click", IptKey_.C); + tst_ExtractKey("&", IptKey_.None); + tst_ExtractKey("trailing &", IptKey_.None); + tst_ExtractKey("me & you", IptKey_.None); + } + void tst_ExtractKey(String text, IptKey expd) { + IptKey actl = GfuiWinKeyCmdMgr.ExtractKeyFromText(text); + Tfds.Eq(expd, actl); + } +} diff --git a/150_gfui/tst/gplx/gfui/GfuiFocusOrderer_tst.java b/150_gfui/tst/gplx/gfui/GfuiFocusOrderer_tst.java new file mode 100644 index 000000000..6b05257dc --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfuiFocusOrderer_tst.java @@ -0,0 +1,87 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class GfuiFocusOrderer_tst { + @Before public void setup() { + owner = GfuiElem_.new_(); + list = ListAdp_.new_(); // list of all controls + } + @Test public void Horizontal() { + ini_Subs(owner, list, xy_(40, 0), xy_(20, 0), xy_(0, 0)); + tst_FocusIndxs(owner, list, 0, 1, 2); + + GfuiFocusOrderer.OrderByX(owner); + tst_FocusIndxs(owner, list, 2, 1, 0); + } + @Test public void Vertical() { + ini_Subs(owner, list, xy_(0, 40), xy_(0, 20), xy_(0, 0)); + tst_FocusIndxs(owner, list, 0, 1, 2); + + GfuiFocusOrderer.OrderByY(owner); + tst_FocusIndxs(owner, list, 2, 1, 0); + } + @Test public void Grid() { + ini_Subs(owner, list, xy_(20, 20), xy_(0, 20), xy_(20, 0), xy_(0, 0)); + tst_FocusIndxs(owner, list, 0, 1, 2, 3); + + GfuiFocusOrderer.OrderByX(owner); + tst_FocusIndxs(owner, list, 3, 2, 1, 0); + } + @Test public void Deep() { + ini_Subs(owner, list, xy_(20, 0), xy_(0, 0)); + GfuiElem sub0 = sub_(owner, 0), sub1 = sub_(owner, 1); + ini_Subs(sub0, list, xy_(20, 0), xy_(0, 0)); + ini_Subs(sub1, list, xy_(20, 0), xy_(0, 0)); + tst_FocusIndxs(owner, list, 0, 1, 0, 1, 0, 1); // 2 owner controls (0, 1); each has two subs (0, 1) + + GfuiFocusOrderer.OrderByX(owner); + tst_FocusIndxs(owner, list, 3, 0, 5, 4, 2, 1); + } + @Test public void Manusl() { + ini_Subs(owner, list, xy_(0, 0), xy_(20, 0)); + tst_FocusIndxs(owner, list, 0, 1); + + GfuiElem sub1 = owner.SubElems().FetchAt(0); + GfuiElem sub2 = owner.SubElems().FetchAt(1); + sub1.Focus_idx_(1); + sub2.Focus_idx_(0); + + GfuiFocusOrderer.OrderByX(owner); + tst_FocusIndxs(owner, list, 1, 0); + } + PointAdp xy_(int x, int y) {return PointAdp_.new_(x, y);} + GfuiElem sub_(GfuiElem owner, int i) {return owner.SubElems().FetchAt(i);} + void ini_Subs(GfuiElem owner, ListAdp list, PointAdp... points) { + for (int i = 0; i < points.length; i++) { + GfuiElem sub = GfuiElem_.sub_(Int_.XtoStr(i), owner); + sub.Pos_(points[i]); + sub.UnderElem().Core().Focus_index_set(i); + list.Add(sub); + } + } + void tst_FocusIndxs(GfuiElem owner, ListAdp list, int... expd) { + int[] actl = new int[list.Count()]; + for (int i = 0; i < actl.length; i++) { + GfuiElem sub = (GfuiElem)list.FetchAt(i); + actl[i] = sub.UnderElem().Core().Focus_index(); + } + Tfds.Eq_ary(expd, actl); + } + GfuiElem owner; ListAdp list; +} diff --git a/150_gfui/tst/gplx/gfui/GfuiMoveElemBtn_tst.java b/150_gfui/tst/gplx/gfui/GfuiMoveElemBtn_tst.java new file mode 100644 index 000000000..3927c1f37 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfuiMoveElemBtn_tst.java @@ -0,0 +1,35 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class GfuiMoveElemBtn_tst { + @Before public void setup() { + form = GfuiWin_.app_("form"); form.Size_(100, 100); + moveBtn = GfuiBtn_.new_("moveBtn"); + GfuiMoveElemBnd bnd = GfuiMoveElemBnd.new_(); bnd.TargetElem_set(form); + moveBtn.IptBnds().Add(bnd); + } + @Test public void Basic() { + Tfds.Eq(form.X(), 0); + IptEventMgr.ExecKeyDown(moveBtn, IptEvtDataKey.test_(MoveRightArg())); + Tfds.Eq(form.X(), 10); + } + + IptKey MoveRightArg() {return IptKey_.Ctrl.Add(IptKey_.Right);} + GfuiWin form; GfuiBtn moveBtn; +} diff --git a/150_gfui/tst/gplx/gfui/GfxAdpMok.java b/150_gfui/tst/gplx/gfui/GfxAdpMok.java new file mode 100644 index 000000000..f68836a65 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxAdpMok.java @@ -0,0 +1,49 @@ +/* +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.gfui; import gplx.*; +public class GfxAdpMok implements GfxAdp { + public GfxItmList SubItms() {return subItms;} GfxItmList subItms = new GfxItmList(); + public void DrawStringXtn(String s, FontAdp font, SolidBrushAdp brush, float x, float y, float width, float height, GfxStringData sd) { + float[] sizeAry = MeasureStringXtn(s, font, null); + SizeAdp size = SizeAdp_.new_((int)sizeAry[0], (int)sizeAry[1]); + GfxStringItm str = GfxStringItm.new_(PointAdp_.new_((int)x, (int)y), size, s, font, brush); + subItms.Add(str); + } + public void DrawRect(PenAdp pen, PointAdp location, SizeAdp size) {this.DrawRect(pen, location.X(), location.Y(), size.Width(), size.Height());} + public void DrawRect(PenAdp pen, RectAdp rect) {this.DrawRect(pen, rect.X(), rect.Y(), rect.Width(), rect.Height());} + public void DrawRect(PenAdp pen, int x, int y, int width, int height) { + GfxRectItm rect = GfxRectItm.new_(PointAdp_.new_(x, y), SizeAdp_.new_(width, height), pen.Width(), pen.Color()); + subItms.Add(rect); + } + public void DrawLine(PenAdp pen, PointAdp src, PointAdp trg) { + GfxLineItm line = GfxLineItm.new_(src, trg, pen.Width(), pen.Color()); + subItms.Add(line); + } + public void DrawImage(ImageAdp image, PointAdp location) { + // gfx.DrawImage(image, width, height); + } + public void DrawImage(ImageAdp img, int trg_x, int trg_y, int trg_w, int trg_h, int src_x, int src_y, int src_w, int src_h) { + // gfx.DrawImage(image, dst, src, GraphicsUnit.Pixel); + } + public void FillRect(SolidBrushAdp brush, int x, int y, int width, int height) { + // gfx.FillRect(brush, x, y, width, height); + } + public float[] MeasureStringXtn(String s, FontAdp font, GfxStringData str) {return new float[] {13 * String_.Len(s), 17};} + public void Rls() {} + public static GfxAdpMok new_() {return new GfxAdpMok();} GfxAdpMok() {} +} diff --git a/150_gfui/tst/gplx/gfui/GfxItm.java b/150_gfui/tst/gplx/gfui/GfxItm.java new file mode 100644 index 000000000..103fe0678 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxItm.java @@ -0,0 +1,19 @@ +/* +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.gfui; import gplx.*; +public interface GfxItm {} diff --git a/150_gfui/tst/gplx/gfui/GfxItmList.java b/150_gfui/tst/gplx/gfui/GfxItmList.java new file mode 100644 index 000000000..cec13f7ca --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxItmList.java @@ -0,0 +1,30 @@ +/* +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.gfui; import gplx.*; +public class GfxItmList extends ListAdp_base { + @gplx.New public GfxItm FetchAt(int i) {return (GfxItm)FetchAt_base(i);} + public void Add(GfxItm gfxItm) {Add_base(gfxItm);} +} +class GfxItmListFxt { + public void tst_SubItm_count(GfxAdpMok gfx, int expd) {Tfds.Eq(expd, gfx.SubItms().Count());} + public void tst_SubItm(GfxAdpMok gfx, int i, GfxItm expd) { + GfxItm actl = gfx.SubItms().FetchAt(i); + Tfds.Eq(expd, actl); + } + public static GfxItmListFxt new_() {return new GfxItmListFxt();} GfxItmListFxt() {} +} diff --git a/150_gfui/tst/gplx/gfui/GfxItm_base.java b/150_gfui/tst/gplx/gfui/GfxItm_base.java new file mode 100644 index 000000000..7aaf88c31 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxItm_base.java @@ -0,0 +1,33 @@ +/* +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.gfui; import gplx.*; +public abstract class GfxItm_base implements GfxItm { + public PointAdp Pos() {return pos;} PointAdp pos = PointAdp_.Zero; + public SizeAdp Size() {return size;} SizeAdp size = SizeAdp_.Zero; + @Override public String toString() {return String_bldr_.new_().Add_kv_obj("pos", pos).Add_kv_obj("size", size).XtoStr();} + @Override public int hashCode() {return this.toString().hashCode();} + @Override public boolean equals(Object obj) { + GfxItm_base comp = GfxItm_base.as_(obj); if (comp == null) return false; + return Object_.Eq(pos, comp.pos) && Object_.Eq(size, comp.size); + } + @gplx.Virtual public void ctor_GfxItmBase(PointAdp posVal, SizeAdp sizeVal) { + pos = posVal; size = sizeVal; + } + public static GfxItm_base as_(Object obj) {return obj instanceof GfxItm_base ? (GfxItm_base)obj : null;} + public static GfxItm_base cast_(Object obj) {try {return (GfxItm_base)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, GfxItm_base.class, obj);}} +} diff --git a/150_gfui/tst/gplx/gfui/GfxLineItm.java b/150_gfui/tst/gplx/gfui/GfxLineItm.java new file mode 100644 index 000000000..56ff872ef --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxLineItm.java @@ -0,0 +1,39 @@ +/* +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.gfui; import gplx.*; +public class GfxLineItm implements GfxItm { + public PointAdp Src() {return src;} PointAdp src = PointAdp_.Zero; + public PointAdp Trg() {return trg;} PointAdp trg = PointAdp_.Zero; + public float Width() {return width;} float width; + public ColorAdp Color() {return color;} ColorAdp color; + + @Override public String toString() {return String_bldr_.new_().Add_kv_obj("src", src).Add_kv_obj("trg", trg).Add_kv_obj("width", width).Add_kv_obj("color", color.XtoHexStr()).XtoStr();} + @Override public int hashCode() {return this.toString().hashCode();} + @Override public boolean equals(Object obj) { + GfxLineItm comp = GfxLineItm.as_(obj); if (comp == null) return false; + return src.Eq(comp.src) && trg.Eq(comp.trg) && width == comp.width && color.Eq(comp.color); + } + public static GfxLineItm new_(PointAdp src, PointAdp trg, float width, ColorAdp color) { + GfxLineItm rv = new GfxLineItm(); + rv.src = src; rv.trg = trg; + rv.width = width; rv.color = color; + return rv; + } GfxLineItm() {} + public static GfxLineItm as_(Object obj) {return obj instanceof GfxLineItm ? (GfxLineItm)obj : null;} + public static GfxLineItm cast_(Object obj) {try {return (GfxLineItm)obj;} catch(Exception exc) {throw Err_.type_mismatch_exc_(exc, GfxLineItm.class, obj);}} +} diff --git a/150_gfui/tst/gplx/gfui/GfxRectItm.java b/150_gfui/tst/gplx/gfui/GfxRectItm.java new file mode 100644 index 000000000..e7d8d2a2c --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxRectItm.java @@ -0,0 +1,36 @@ +/* +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.gfui; import gplx.*; +public class GfxRectItm extends GfxItm_base { + public float Width() {return width;} float width; + public ColorAdp Color() {return color;} ColorAdp color; + + @Override public String toString() {return String_.Concat(super.toString(), String_bldr_.new_().Add_kv_obj("width", width).Add_kv("color", color.XtoHexStr()).XtoStr());} + @Override public int hashCode() {return this.toString().hashCode();} + @Override public boolean equals(Object obj) { + GfxRectItm comp = GfxRectItm.as_(obj); if (comp == null) return false; + return super.equals(comp) && width == comp.width && color.Eq(comp.color); + } + public static GfxRectItm new_(PointAdp pos, SizeAdp size, float width, ColorAdp color) { + GfxRectItm rv = new GfxRectItm(); + rv.ctor_GfxItmBase(pos, size); + rv.width = width; rv.color = color; + return rv; + } GfxRectItm() {} + @gplx.New public static GfxRectItm as_(Object obj) {return obj instanceof GfxRectItm ? (GfxRectItm)obj : null;} +} diff --git a/150_gfui/tst/gplx/gfui/GfxStringItm.java b/150_gfui/tst/gplx/gfui/GfxStringItm.java new file mode 100644 index 000000000..92e9d5b0c --- /dev/null +++ b/150_gfui/tst/gplx/gfui/GfxStringItm.java @@ -0,0 +1,38 @@ +/* +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.gfui; import gplx.*; +public class GfxStringItm extends GfxItm_base { + public String Text() {return text;} private String text; + public FontAdp Font() {return font;} FontAdp font; + public SolidBrushAdp Brush() {return brush;} SolidBrushAdp brush; + @Override public int hashCode() {return this.toString().hashCode();} + @Override public boolean equals(Object obj) { + GfxStringItm comp = GfxStringItm.as_(obj); if (comp == null) return false; + return super.equals(obj) && String_.Eq(text, comp.text) && font.Eq(comp.font) && brush.Eq(comp.brush); + } + public static GfxStringItm new_(PointAdp pos, SizeAdp size, String text, FontAdp font, SolidBrushAdp brush) { + GfxStringItm rv = new GfxStringItm(); + rv.ctor_GfxItmBase(pos, size); + rv.text = text; rv.font = font; rv.brush = brush; + return rv; + } GfxStringItm() {} + public static GfxStringItm test_(String text, FontAdp font, SolidBrushAdp brush) { + return GfxStringItm.new_(PointAdp_.Null, SizeAdp_.Null, text, font, brush); + } + @gplx.New public static GfxStringItm as_(Object obj) {return obj instanceof GfxStringItm ? (GfxStringItm)obj : null;} +} diff --git a/150_gfui/tst/gplx/gfui/ImageAdp_tst.java b/150_gfui/tst/gplx/gfui/ImageAdp_tst.java new file mode 100644 index 000000000..c139b152e --- /dev/null +++ b/150_gfui/tst/gplx/gfui/ImageAdp_tst.java @@ -0,0 +1,45 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +import gplx.ios.*; +import gplx.security.*; +public class ImageAdp_tst { + @Before public void setup() { + load = Tfds.RscDir.GenSubFil_nest("150_gfui", "imgs", "strawberry_java.bmp"); + } ImageAdp img; Io_url load; + @Test public void load_() { + img = ImageAdp_.file_(load); + Tfds.Eq(80, img.Width()); + Tfds.Eq(80, img.Height()); + Tfds.Eq("80,80", img.Size().toString()); + Tfds.Eq(img.Url(), load); + } + @Test public void SaveAsBmp() { + img = ImageAdp_.file_(load); + Io_url save = load.GenNewNameOnly("strawberry_temp"); + DateAdp beforeModifiedTime = Io_mgr._.QueryFil(save).ModifiedTime(); + img.SaveAsBmp(save); + DateAdp afterModifiedTime = Io_mgr._.QueryFil(save).ModifiedTime(); + Tfds.Eq_true(CompareAble_.Is_more(afterModifiedTime, beforeModifiedTime)); + + String loadHash = HashAlgo_.Md5.CalcHash(ConsoleDlg_.Null, Io_mgr._.OpenStreamRead(load)); + String saveHash = HashAlgo_.Md5.CalcHash(ConsoleDlg_.Null, Io_mgr._.OpenStreamRead(save)); + Tfds.Eq(loadHash, saveHash); + } +} diff --git a/150_gfui/tst/gplx/gfui/IptArg_parser_tst.java b/150_gfui/tst/gplx/gfui/IptArg_parser_tst.java new file mode 100644 index 000000000..db8255320 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/IptArg_parser_tst.java @@ -0,0 +1,63 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class IptArg_parser_tst { + @Test public void KeyBasic() { + tst_parse_Key_("key.a", IptKey_.A); + tst_parse_Key_("key.d0", IptKey_.D0); + tst_parse_Key_("key.semicolon", IptKey_.Semicolon); + tst_parse_Key_("key.equal", IptKey_.Equal); + tst_parse_Key_("key.pageUp", IptKey_.PageUp); + tst_parse_Key_("key.ctrl", IptKey_.Ctrl); + tst_parse_Key_("key.none", IptKey_.None); + } void tst_parse_Key_(String raw, IptKey expd) {Tfds.Eq(expd.Val(), IptKey_.parse_(raw).Val());} + @Test public void KbdCmdModifiers() { + tst_parse_Key_("key.ctrl+key.enter", IptKey_.Ctrl.Add(IptKey_.Enter)); + tst_parse_Key_("key.alt+key.escape", IptKey_.Alt.Add(IptKey_.Escape)); + tst_parse_Key_("key.shift+key.f1", IptKey_.Shift.Add(IptKey_.F1)); + tst_parse_Key_("key.shift+key.ctrl", IptKey_.Ctrl.Add(IptKey_.Shift)); + tst_parse_Key_("key.ctrl+key.alt+key.slash", IptKey_.Ctrl.Add(IptKey_.Alt).Add(IptKey_.Slash)); + } + @Test public void KeyWhitespace() { + tst_parse_Key_("key.ctrl + key.alt + key.slash", IptKey_.Ctrl.Add(IptKey_.Alt).Add(IptKey_.Slash)); + } + @Test public void MouseBtn() { + tst_parse_MouseBtn_("mouse.left", IptMouseBtn_.Left); + tst_parse_MouseBtn_("mouse.right", IptMouseBtn_.Right); + tst_parse_MouseBtn_("mouse.middle", IptMouseBtn_.Middle); + tst_parse_MouseBtn_("mouse.x1", IptMouseBtn_.X1); + tst_parse_MouseBtn_("mouse.x2", IptMouseBtn_.X2); + } void tst_parse_MouseBtn_(String raw, IptMouseBtn expd) {Tfds.Eq(expd, IptMouseBtn_.parse_(raw));} + @Test public void MouseWheel() { + tst_parse_MouseWheel_("wheel.up", IptMouseWheel_.Up); + tst_parse_MouseWheel_("wheel.down", IptMouseWheel_.Down); + } void tst_parse_MouseWheel_(String raw, IptMouseWheel expd) {Tfds.Eq(expd, IptMouseWheel_.parse_(raw));} + @Test public void Mod() { + tst_parse_("mod.c", IptKey_.Ctrl); + tst_parse_("mod.cs", IptKey_.add_(IptKey_.Ctrl, IptKey_.Shift)); + tst_parse_("mod.cas", IptKey_.add_(IptKey_.Ctrl, IptKey_.Alt, IptKey_.Shift)); + tst_parse_("mod.c+key.c", IptKey_.add_(IptKey_.Ctrl, IptKey_.C)); + } + @Test public void All() { + tst_parse_("key.c", IptKey_.C); + tst_parse_("mouse.left", IptMouseBtn_.Left); + tst_parse_("wheel.up", IptMouseWheel_.Up); + tst_parse_("mod.c", IptKey_.Ctrl); + } void tst_parse_(String raw, IptArg expd) {Tfds.Eq(expd, IptArg_.parse_(raw));} +} diff --git a/150_gfui/tst/gplx/gfui/IptEventType_tst.java b/150_gfui/tst/gplx/gfui/IptEventType_tst.java new file mode 100644 index 000000000..59656ed9f --- /dev/null +++ b/150_gfui/tst/gplx/gfui/IptEventType_tst.java @@ -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 . +*/ +package gplx.gfui; import gplx.*; +import org.junit.*; +public class IptEventType_tst { + @Test public void Has() { + tst_Has(IptEventType_.KeyDown, IptEventType_.KeyDown, true); + tst_Has(IptEventType_.KeyUp, IptEventType_.KeyDown, false); + tst_Has(IptEventType_.None, IptEventType_.KeyDown, false); + tst_Has(IptEventType_.KeyDown, IptEventType_.None, false); + tst_Has(IptEventType_.KeyDown.Add(IptEventType_.KeyUp), IptEventType_.KeyDown, true); + tst_Has(IptEventType_.MouseDown.Add(IptEventType_.MouseUp), IptEventType_.KeyDown, false); + tst_Has(IptEventType_.KeyDown.Add(IptEventType_.KeyUp), IptEventType_.None, false); + } void tst_Has(IptEventType val, IptEventType find, boolean expd) {Tfds.Eq(expd, IptEventType_.Has(val, find));} + @Test public void add_() { + tst_add(IptEventType_.KeyDown, IptEventType_.KeyDown, IptEventType_.KeyDown.Val()); + tst_add(IptEventType_.KeyDown, IptEventType_.KeyUp, IptEventType_.KeyDown.Val() + IptEventType_.KeyUp.Val()); + } void tst_add(IptEventType lhs, IptEventType rhs, int expd) {Tfds.Eq(expd, IptEventType_.add_(lhs, rhs).Val());} +} diff --git a/150_gfui/tst/gplx/gfui/ScreenAdp_tst.java b/150_gfui/tst/gplx/gfui/ScreenAdp_tst.java new file mode 100644 index 000000000..f45e15ccc --- /dev/null +++ b/150_gfui/tst/gplx/gfui/ScreenAdp_tst.java @@ -0,0 +1,29 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class ScreenAdp_tst { + @Test public void parse_() { + ScreenAdp actl = ScreenAdp_.parse_("{screen{0}"); + Tfds.Eq(0, actl.Index()); + } + @Test public void opposite_() { + ScreenAdp actl = ScreenAdp_.from_point_(PointAdp_.new_(2000, 2000)); + Tfds.Eq(0, actl.Index()); + } +} diff --git a/150_gfui/tst/gplx/gfui/TabBox_tst.java b/150_gfui/tst/gplx/gfui/TabBox_tst.java new file mode 100644 index 000000000..ee19e1853 --- /dev/null +++ b/150_gfui/tst/gplx/gfui/TabBox_tst.java @@ -0,0 +1,131 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class TabBox_tst { +// @Before public void setup() { +// fx = TabBoxFxt.new_(); +// } TabBoxFxt fx; + @Test public void Add() { +// fx.Make(1).tst_Selected("0").FetchBtnAt(0).tst_X(0); +// fx.Make(3).tst_Selected("2").FetchBtnAt(2).tst_X(160); + } +// @Test public void DelAt() { +// fx.Make(2).DelAt(1).tst_Btns("0"); +// fx.Make(2).DelAt(0).tst_Btns("1"); +// fx.Make(3).DelAt(0).tst_Btns("1", "2"); +// fx.Make(3).DelAt(1).tst_Btns("0", "2"); +// fx.Make(3).DelAt(2).tst_Btns("0", "1"); + +// fx.Make(3).Select(1).DelAt(1).tst_Selected("2"); // 1 deleted; 2 shifted down into slot +// fx.Make(3).Select(1).DelAt(0).tst_Selected("1"); // 0 deleted; 1 still remains active (but will have idx of 0 +// fx.Make(3).Select(2).DelAt(2).tst_Selected("1"); // 2 deleted; 1 selected +// } +// @Test public void Selected_byAdd() { +// fx.Make(2).Select(0).tst_Selected("0").Select(1).tst_Selected("1"); +// } +// @Test public void Selected_byBtn() { +// fx.Make(2).tst_Selected("1"); +// +// GfuiBtn btn = fx.TabBox().SubBtnArea().FetchAt(0); +// btn.Click(); +// fx.tst_Selected("0"); +// } +// @Test public void ReorderTab() { +// fx.Make(3).Reorder(0, -1).tst_Raised(false); +// fx.Make(3).Reorder(2, 1).tst_Raised(false); +// fx.Make(3).Reorder(0, 1).tst_Btns("1", "0", "2").tst_Raised(true).tst_FocusOrder(); +// fx.Make(3).Reorder(0, 2).tst_Btns("1", "2", "0").tst_Raised(true).tst_FocusOrder(); +// fx.Make(3).Reorder(2, -1).tst_Btns("0", "2", "1").tst_Raised(true).tst_FocusOrder(); +// fx.Make(3).Reorder(0, 1).Reorder(1, 2).tst_Btns("0", "2", "1").tst_Raised(true);//.tst_FocusOrder(); // FIXME: broken after FocusOrder set for entire form (instead of per container) +// } +} +class GfuiElemFxt { + public GfuiElem UnderElem() {return underElem;} GfuiElem underElem; + @gplx.Internal protected GfuiElemFxt tst_X(int expd) {Tfds.Eq(expd, underElem.X()); return this;} + public static GfuiElemFxt new_(GfuiElem elem) { + GfuiElemFxt rv = new GfuiElemFxt(); + rv.underElem = elem; + return rv; + } GfuiElemFxt() {} +} +class TabBoxFxt implements GfoInvkAble { + @gplx.Internal protected TabBox TabBox() {return tabBox;} + @gplx.Internal protected TabBoxFxt Make(int count) { + for (int i = 0; i < tabBox.Tabs_Count(); i++) + tabBox.Tabs_DelAt(0); + for (int i = 0; i < count; i++) + tabBox.Tabs_Add(Int_.XtoStr(i), Int_.XtoStr(i)); + return this; + } + @gplx.Internal protected TabBoxFxt DelAt(int index) {tabBox.Tabs_DelAt(index); return this;} +// @gplx.Internal protected TabBoxFxt Select(int index) {tabBox.Tabs_Select(index); return this;} + @gplx.Internal protected GfuiElemFxt FetchBtnAt(int index) { + GfuiBtn btn = (GfuiBtn)tabBox.BtnBox().SubElems().FetchAt(index); + GfuiElemFxt fx_elem = GfuiElemFxt.new_(btn); + return fx_elem; + } +// @gplx.Internal protected TabBoxFxt tst_BtnX(int idx, int expdX) { +// Tfds.Eq(expdX, tabBox.SubBtnArea().FetchAt(idx).X()); +// return this; +// } + @gplx.Internal protected TabBoxFxt tst_Selected(String expd) { + TabPnlItm curTab = tabBox.Tabs_SelectedItm(); + GfuiBtn btn = (GfuiBtn)tabBox.BtnBox().SubElems().FetchAt(curTab.Idx()); + Tfds.Eq(expd, btn.Text()); + return this; + } + @gplx.Internal protected TabBoxFxt tst_Btns(String... expd) { + String[] actl = new String[tabBox.Tabs_Count() ]; + for (int i = 0; i < tabBox.Tabs_Count() ; i++) { + GfuiBtn button = (GfuiBtn)tabBox.BtnBox().SubElems().FetchAt(i); + actl[i] = button.TextMgr().Val(); + } + Tfds.Eq_ary(expd, actl); + return this; + } +// @gplx.Internal protected TabBoxFxt tst_Raised(boolean expd) {Tfds.Eq(expd, received != null); return this;} +// @gplx.Internal protected TabBoxFxt Reorder(int i, int delta) { +// tabBox.Width_(240); // needed for lytMgr +// TabBnd_reorderTab reorderBnd = TabBnd_reorderTab._; +// received = null; +// TabPnl pnl = tabBox.Tabs_FetchAt(i); +// reorderBnd.MoveTab(pnl.SubTabBtn(), delta); +// return this; +// } +// @gplx.Internal protected TabBoxFxt tst_FocusOrder() { +// for (int i = 0; i < tabBox.SubBtnArea().SubZones().FetchAt(0).Count(); i++) { +// GfuiElem subBtn = (GfuiElem)tabBox.SubBtnArea().SubZones().FetchAt(0).FetchAt(i); +// Tfds.Eq(i, subBtn.UnderElem().Core().Focus_index()); +// } +// return this; +// } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, OrderChangedReceived_cmd)) OrderChangedReceived(m); + else return GfoInvkAble_.Rv_unhandled; + return this; + } public static final String OrderChangedReceived_cmd = "OrderChangedReceived"; + TabBox tabBox; + public static TabBoxFxt new_() { + TabBoxFxt rv = new TabBoxFxt(); + rv.tabBox = TabBox_.new_(); + return rv; + } TabBoxFxt() {} + void OrderChangedReceived(GfoMsg msg) { + } //int[] received = null; +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_btn.java b/150_gfui/xtn/gplx/gfui/Swt_btn.java new file mode 100644 index 000000000..74b3fa498 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_btn.java @@ -0,0 +1,102 @@ +/* +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.gfui; import gplx.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +class Swt_btn implements GxwElem, Swt_control { + private Button btn; + public Swt_btn(Swt_control owner, KeyValHash ctorArgs) { + btn = new Button(owner.Under_composite(), SWT.FLAT | SWT.PUSH); + core = new Swt_core_cmds(btn); + btn.addKeyListener(new Swt_lnr_key(this)); + btn.addMouseListener(new Swt_lnr_mouse(this)); + } + @Override public Control Under_control() {return btn;} + @Override public Control Under_menu_control() {return btn;} + @Override public String TextVal() {return btn.getText();} @Override public void TextVal_set(String v) {btn.setText(v);} + @Override public GxwCore_base Core() {return core;} GxwCore_base core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public Composite Under_composite() {return null;} + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return null;} +} +class Swt_btn_no_border implements GxwElem, Swt_control { + public Swt_btn_no_border(Swt_control owner_control, KeyValHash ctorArgs) { + Composite owner = owner_control.Under_composite(); + Make_btn_no_border(owner.getDisplay(), owner.getShell(), owner); + core = new Swt_core_cmds(box_btn); + box_btn.addKeyListener(new Swt_lnr_key(this)); + box_btn.addMouseListener(new Swt_lnr_mouse(this)); + } + @Override public Control Under_control() {return box_btn;} + @Override public Control Under_menu_control() {return box_btn;} + @Override public String TextVal() {return box_btn.getText();} @Override public void TextVal_set(String v) {box_btn.setText(v);} + @Override public GxwCore_base Core() {return core;} Swt_core_cmds core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public Composite Under_composite() {return null;} + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, GfuiBtn.Invk_btn_img)) return btn_img; + else if (ctx.Match(k, GfuiBtn.Invk_btn_img_)) Btn_img_((ImageAdp)m.CastObj("v")); + return null; + } + void Btn_img_(ImageAdp v) { + if (box_btn == null || v == null) return; + SizeAdp size = core.Size(); + int dif = 6; + box_btn.setImage((Image)v.Resize(size.Width() - dif, size.Height() - dif).Under()); + } + ImageAdp btn_img; + Composite box_grp; + Label box_btn; + void Make_btn_no_border(Display display, Shell shell, Control owner) { + box_grp = new Composite(shell, SWT.FLAT); + box_btn = new Label(shell, SWT.FLAT); + box_btn.setSize(25, 25); + box_btn.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); + box_btn.addFocusListener(new Swt_clabel_lnr_focus(box_grp)); + } +} +class Swt_clabel_lnr_focus implements FocusListener { + public Swt_clabel_lnr_focus(Control v) {this.surrogate = v;} Control surrogate; + @Override public void focusGained(org.eclipse.swt.events.FocusEvent e) { + surrogate.forceFocus(); + } + @Override public void focusLost(org.eclipse.swt.events.FocusEvent arg0) {} +} \ No newline at end of file diff --git a/150_gfui/xtn/gplx/gfui/Swt_clipboard.java b/150_gfui/xtn/gplx/gfui/Swt_clipboard.java new file mode 100644 index 000000000..8df569968 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_clipboard.java @@ -0,0 +1,58 @@ +/* +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.gfui; import gplx.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +class Swt_clipboard implements Gfui_clipboard { + public Swt_clipboard(Display display) { + this.display = display; + clipboard = new Clipboard(display); + } Display display; Clipboard clipboard; + public void Copy(String v) { + if (String_.Len_eq_0(v)) return; + TextTransfer textTransfer = TextTransfer.getInstance(); + clipboard.setContents(new Object[]{v}, new Transfer[]{textTransfer}); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Gfui_clipboard_.Invk_copy)) Send_key(IptKey_.Ctrl, 'C'); + else if (ctx.Match(k, Gfui_clipboard_.Invk_select_all)) Send_key(IptKey_.Ctrl, 'A'); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + @Override public void Rls() {clipboard.dispose();} + int Xto_keycode(IptKey modifier) { + switch (modifier.Val()) { + case IptKey_.KeyCode_Ctrl: return SWT.CTRL; + case IptKey_.KeyCode_Alt: return SWT.ALT; + case IptKey_.KeyCode_Shift: return SWT.SHIFT; + default: return SWT.NONE; + } + } + public void Send_key(IptKey mod, char key_press_char) { + Event event = new Event(); + int modifier_key_code = Xto_keycode(mod); + event.keyCode = modifier_key_code; event.type = SWT.KeyDown; display.post(event); + event.keyCode = 0; event.character = key_press_char; display.post(event); + event.type = SWT.KeyUp; display.post(event); + event.keyCode = modifier_key_code; event.character = 0; display.post(event); + } +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_control.java b/150_gfui/xtn/gplx/gfui/Swt_control.java new file mode 100644 index 000000000..c69da862b --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_control.java @@ -0,0 +1,44 @@ +/* +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.gfui; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +interface Swt_control extends GxwElem { + Control Under_control(); + Composite Under_composite(); + Control Under_menu_control(); +} +class Swt_control_ { + public static void X_set(Control c, int v) {Point point = c.getLocation(); c.setLocation(v, point.y);} + public static void Y_set(Control c, int v) {Point point = c.getLocation(); c.setLocation(point.x, v);} + public static void W_set(Control c, int v) {Point point = c.getSize(); c.setSize(v, point.y);} + public static void H_set(Control c, int v) {Point point = c.getSize(); c.setSize(point.x, v);} + public static void Pos_set(Control c, PointAdp v) {c.setLocation(v.X(), v.Y());} + public static void Pos_set(Control c, int x, int y) {c.setLocation(x, y);} + public static void Size_set(Control c, SizeAdp v) {c.setSize(v.Width(), v.Height());} + public static void Size_set(Control c, int w, int h) {c.setSize(w, h);} + public static void Rect_set(Control c, RectAdp v) {c.setBounds(Xto_rectangle(v));} + public static void Rect_set(Control c, int x, int y, int w, int h) {c.setBounds(Xto_rectangle(x, y, w, h));} + public static void Rect_add(Control c, RectAdp v, int x, int y, int w, int h) {c.setBounds(Xto_rectangle(v.X() + x, v.Y() + y, v.Width() + w, v.Height()+ h));} + public static Rectangle Xto_rectangle(int x, int y, int w, int h) {return new Rectangle(x, y, w, h);} + public static Rectangle Xto_rectangle(RectAdp v) {return new Rectangle(v.X(), v.Y(), v.Width(), v.Height());} + public static Swt_control cast_or_fail(GfuiElem elem) {return (Swt_control)elem.UnderElem();} +} \ No newline at end of file diff --git a/150_gfui/xtn/gplx/gfui/Swt_core_cmds.java b/150_gfui/xtn/gplx/gfui/Swt_core_cmds.java new file mode 100644 index 000000000..f06092dd1 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_core_cmds.java @@ -0,0 +1,243 @@ +/* +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.gfui; +import gplx.Err_; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +class Swt_core_cmds extends GxwCore_base { + public Swt_core_cmds(Control control) { + compositeAble = control instanceof Composite; + this.control = control; + } Control control; boolean compositeAble = false; + @Override public int Width() {return control.getSize().x;} @Override public void Width_set(int v) {if (Cfg_resize_disabled) return; control.setSize(v, this.Height());} + @Override public int Height() {return control.getSize().y;} @Override public void Height_set(int v) {if (Cfg_resize_disabled) return; control.setSize(this.Width(), v);} + @Override public int X() {return control.getLocation().x;} @Override public void X_set(int v) {control.setLocation(v, this.Y());} + @Override public int Y() {return control.getLocation().y;} @Override public void Y_set(int v) {control.setLocation(this.X(), v);} + @Override public SizeAdp Size() {return SizeAdp_.new_(this.Width(), this.Height());} @Override public void Size_set(SizeAdp v) {if (Cfg_resize_disabled) return; control.setSize(v.Width(), v.Height());} + @Override public PointAdp Pos() {return PointAdp_.new_(this.X(), this.Y());} @Override public void Pos_set(PointAdp v) {control.setLocation(v.X(), v.Y());} + @Override public RectAdp Rect() {return RectAdp_.new_(this.X(), this.Y(), this.Width(), this.Height());} + @Override public void Rect_set(RectAdp v) { + if (Cfg_resize_disabled) + control.setLocation(v.X(), v.Y()); + else + control.setBounds(v.X(), v.Y(), v.Width(), v.Height()); + } + @Override public boolean Visible() {return control.isVisible();} + @Override public void Visible_set(boolean v) {control.setVisible(v);} + @Override public ColorAdp BackColor() {return XtoColorAdp(control.getBackground());} @Override public void BackColor_set(ColorAdp v) {control.setBackground(XtoColor(v));} + @Override public ColorAdp ForeColor() {return XtoColorAdp(control.getForeground());} @Override public void ForeColor_set(ColorAdp v) {control.setForeground(XtoColor(v));} + public boolean Cfg_resize_disabled = false; + ColorAdp XtoColorAdp(Color v) {return ColorAdp_.new_(0, v.getRed(), v.getGreen(), v.getBlue());} + Color XtoColor(ColorAdp v) {return new Color(control.getDisplay(), v.Red(), v.Green(), v.Blue());} + @Override public FontAdp TextFont() { + if (prv_font != null) return prv_font; + prv_font = Swt_kit.Control_font_get(control.getFont(), this); + return prv_font; + } FontAdp prv_font; + @Override public void TextFont_set(FontAdp v) { + Swt_kit.Control_font_set(v, this, control); + prv_font = v; + } + @Override public String TipText() {return control.getToolTipText();} @Override public void TipText_set(String v) {control.setToolTipText(v);} + @Override public void Controls_add(GxwElem sub) { + if (!compositeAble) throw Err_.new_("cannot add sub to control"); + Composite owner_as_composite = (Composite)control; + Swt_control sub_as_WxSwt = (Swt_control)sub; + Control sub_as_swt = sub_as_WxSwt.Under_control(); + sub_as_swt.setParent(owner_as_composite); + } + @Override public void Controls_del(GxwElem sub) { + if (!compositeAble) throw Err_.new_("cannot add sub to control"); + Swt_control sub_as_WxSwt = (Swt_control)sub; + Control sub_as_swt = sub_as_WxSwt.Under_control(); + sub_as_swt.dispose(); // SWT_NOTE: no way to officially remove sub from control; can only dispose + } + @Override public boolean Focus_has() {return control.isFocusControl();} + @Override public boolean Focus_able() {return focus_able;} boolean focus_able; + @Override public void Focus_able_(boolean v) {focus_able = v;} + @Override public int Focus_index() {return focusIndex;} @Override public void Focus_index_set(int v) {focusIndex = v;} int focusIndex; + @Override public void Focus() { + if (Focus_able()) + control.forceFocus(); + } + @Override public void Select_exec() { + control.setFocus(); + } + @Override public void Zorder_front() { +// Canvas c; c.moveAbove(arg0); + } + @Override public void Zorder_back() { +// Canvas c; c.moveBelow(arg0); + } + @Override public void Invalidate() {control.redraw(); control.update();} + @Override public void Dispose() {control.dispose();} +} +class Swt_core_cmds_dual extends GxwCore_base { + public Swt_core_cmds_dual(Composite outer, Control inner, int inner_adj_x, int inner_adj_y, int inner_adj_w, int inner_adj_h) { + this.outer = outer; this.inner = inner; + outer_is_composite = outer instanceof Composite; + this.inner_adj_x = inner_adj_x; this.inner_adj_y = inner_adj_y; this.inner_adj_w = inner_adj_w; this.inner_adj_h = inner_adj_h; + } Control outer, inner; boolean outer_is_composite = false; int inner_adj_x, inner_adj_y, inner_adj_w, inner_adj_h; + @Override public int X() {return outer.getLocation().x;} @Override public void X_set(int v) {Swt_control_.X_set(outer, v);} + @Override public int Y() {return outer.getLocation().y;} @Override public void Y_set(int v) {Swt_control_.Y_set(outer, v);} + @Override public int Width() {return outer.getSize().x;} @Override public void Width_set(int v) {Swt_control_.W_set(outer, v); Swt_control_.W_set(outer, v + inner_adj_w);} + @Override public int Height() {return outer.getSize().y;} @Override public void Height_set(int v) {Swt_control_.H_set(outer, v); Swt_control_.H_set(outer, v + inner_adj_h);} + @Override public SizeAdp Size() {return SizeAdp_.new_(this.Width(), this.Height());} @Override public void Size_set(SizeAdp v) {Swt_control_.Size_set(outer, v); Swt_control_.Size_set(inner, v.Width() + inner_adj_w, v.Height() + inner_adj_h);} + @Override public PointAdp Pos() {return PointAdp_.new_(this.X(), this.Y());} @Override public void Pos_set(PointAdp v) {Swt_control_.Pos_set(outer, v);} + @Override public RectAdp Rect() {return RectAdp_.new_(this.X(), this.Y(), this.Width(), this.Height());} @Override public void Rect_set(RectAdp v) {Swt_control_.Rect_set(outer, v); Swt_control_.Size_set(inner, v.Width() + inner_adj_w, v.Height() + inner_adj_h);} + @Override public boolean Visible() {return outer.isVisible();} + @Override public void Visible_set(boolean v) {outer.setVisible(v);} + @Override public ColorAdp BackColor() {return XtoColorAdp(inner.getBackground());} @Override public void BackColor_set(ColorAdp v) {inner.setBackground(XtoColor(v));} + @Override public ColorAdp ForeColor() {return XtoColorAdp(inner.getForeground());} @Override public void ForeColor_set(ColorAdp v) {inner.setForeground(XtoColor(v));} + ColorAdp XtoColorAdp(Color v) {return ColorAdp_.new_(0, v.getRed(), v.getGreen(), v.getBlue());} + Color XtoColor(ColorAdp v) {return new Color(outer.getDisplay(), v.Red(), v.Green(), v.Blue());} + @Override public FontAdp TextFont() { + if (prv_font != null) return prv_font; + prv_font = Swt_kit.Control_font_get(inner.getFont(), this); + return prv_font; + } FontAdp prv_font; + @Override public void TextFont_set(FontAdp v) { + Swt_kit.Control_font_set(v, this, inner); + prv_font = v; + } + @Override public String TipText() {return inner.getToolTipText();} @Override public void TipText_set(String v) {inner.setToolTipText(v);} + @Override public void Controls_add(GxwElem sub) { + if (!outer_is_composite) throw Err_.new_("cannot add sub to outer"); + Composite owner_as_composite = (Composite)outer; + Swt_control sub_as_WxSwt = (Swt_control)sub; + Control sub_as_swt = sub_as_WxSwt.Under_control(); + sub_as_swt.setParent(owner_as_composite); + } + @Override public void Controls_del(GxwElem sub) { + if (!outer_is_composite) throw Err_.new_("cannot add sub to outer"); + Swt_control sub_as_WxSwt = (Swt_control)sub; + Control sub_as_swt = sub_as_WxSwt.Under_control(); + sub_as_swt.dispose(); // SWT_NOTE: no way to officially remove sub from outer; can only dispose + } + @Override public boolean Focus_has() {return inner.isFocusControl();} + @Override public boolean Focus_able() {return focus_able;} boolean focus_able; + @Override public void Focus_able_(boolean v) {focus_able = v;} + @Override public int Focus_index() {return focusIndex;} @Override public void Focus_index_set(int v) {focusIndex = v;} int focusIndex; + @Override public void Focus() { + if (Focus_able()) + inner.forceFocus(); + } + @Override public void Select_exec() { + inner.setFocus(); + } + @Override public void Zorder_front() {} + @Override public void Zorder_back() {} + @Override public void Invalidate() {outer.update(); inner.update();} + @Override public void Dispose() {outer.dispose(); inner.dispose();} +} +interface Swt_core_cmds_frames_itm { + Control Itm(); + void Rect_set(int w, int h); +} +class Swt_core_cmds_frames_itm_manual implements Swt_core_cmds_frames_itm { + public Swt_core_cmds_frames_itm_manual(Control control, int x, int y, int w, int h) { + this.control = control; this.x = x; this.y = y; this.w = w; this.h = h; + } Control control; int x, y, w, h; + public Control Itm() {return control;} + public void Rect_set(int new_w, int new_h) { + Swt_control_.Rect_set(control, x, y, new_w + w, new_h + h); + } +} +class Swt_core_cmds_frames_itm_center_v implements Swt_core_cmds_frames_itm { + public Swt_core_cmds_frames_itm_center_v(Control control, Swt_text_w_border margin_owner) {this.control = control; this.margin_owner = margin_owner;} Control control; Swt_text_w_border margin_owner; + public Control Itm() {return control;} + public void Rect_set(int new_w, int new_h) { + int margin_t = margin_owner.margins_t; + int margin_b = margin_owner.margins_b; + Swt_control_.Rect_set(control, 0, margin_t, new_w, new_h - (margin_t + margin_b)); + } +} +class Swt_core_cmds_frames extends GxwCore_base { + public Swt_core_cmds_frames(Composite outer, Swt_core_cmds_frames_itm[] frames) { + this.outer = outer; this.frames = frames; + frames_len = frames.length; + this.inner = frames[frames_len - 1].Itm(); + } Composite outer; Control inner; Swt_core_cmds_frames_itm[] frames; int frames_len; + void Frames_w_set(int v) { + for (int i = 0; i < frames_len; i++) + frames[i].Rect_set(v, this.Height()); + } + void Frames_h_set(int v) { + for (int i = 0; i < frames_len; i++) + frames[i].Rect_set(this.Width(), v); + } + void Frames_size_set(SizeAdp v) { + for (int i = 0; i < frames_len; i++) + frames[i].Rect_set(v.Width(), v.Height()); + } + @Override public int X() {return outer.getLocation().x;} @Override public void X_set(int v) {Swt_control_.X_set(outer, v);} + @Override public int Y() {return outer.getLocation().y;} @Override public void Y_set(int v) {Swt_control_.Y_set(outer, v);} + @Override public int Width() {return outer.getSize().x;} @Override public void Width_set(int v) {Swt_control_.W_set(outer, v); Frames_w_set(v);} + @Override public int Height() {return outer.getSize().y;} @Override public void Height_set(int v) {Swt_control_.H_set(outer, v); Frames_h_set(v);} + @Override public SizeAdp Size() {return SizeAdp_.new_(this.Width(), this.Height());} @Override public void Size_set(SizeAdp v) {Swt_control_.Size_set(outer, v); Frames_size_set(v);} + @Override public PointAdp Pos() {return PointAdp_.new_(this.X(), this.Y());} @Override public void Pos_set(PointAdp v) {Swt_control_.Pos_set(outer, v);} + @Override public RectAdp Rect() {return RectAdp_.new_(this.X(), this.Y(), this.Width(), this.Height());} @Override public void Rect_set(RectAdp v) {Swt_control_.Rect_set(outer, v); Frames_size_set(v.Size());} + @Override public boolean Visible() {return outer.isVisible();} + @Override public void Visible_set(boolean v) {outer.setVisible(v);} + @Override public ColorAdp BackColor() {return XtoColorAdp(inner.getBackground());} + @Override public void BackColor_set(ColorAdp v) { + Color color = XtoColor(v); +// outer.setBackground(color); + for (int i = 0; i < frames_len; i++) + frames[i].Itm().setBackground(color); + } + @Override public ColorAdp ForeColor() {return XtoColorAdp(inner.getForeground());} @Override public void ForeColor_set(ColorAdp v) {inner.setForeground(XtoColor(v));} + ColorAdp XtoColorAdp(Color v) {return ColorAdp_.new_(0, v.getRed(), v.getGreen(), v.getBlue());} + Color XtoColor(ColorAdp v) {return new Color(outer.getDisplay(), v.Red(), v.Green(), v.Blue());} + @Override public FontAdp TextFont() { + if (prv_font != null) return prv_font; + prv_font = Swt_kit.Control_font_get(inner.getFont(), this); + return prv_font; + } FontAdp prv_font; + @Override public void TextFont_set(FontAdp v) { + Swt_kit.Control_font_set(v, this, inner); + prv_font = v; + } + @Override public String TipText() {return inner.getToolTipText();} @Override public void TipText_set(String v) {inner.setToolTipText(v);} + @Override public void Controls_add(GxwElem sub) {throw Err_.not_implemented_();} + @Override public void Controls_del(GxwElem sub) {} + @Override public boolean Focus_has() {return inner.isFocusControl();} + @Override public boolean Focus_able() {return focus_able;} boolean focus_able; + @Override public void Focus_able_(boolean v) {focus_able = v;} + @Override public int Focus_index() {return focusIndex;} @Override public void Focus_index_set(int v) {focusIndex = v;} int focusIndex; + @Override public void Focus() { + if (Focus_able()) + inner.forceFocus(); + } + @Override public void Select_exec() { + inner.setFocus(); + } + @Override public void Zorder_front() {} + @Override public void Zorder_back() {} + @Override public void Invalidate() { + inner.redraw(); + inner.update(); + } + @Override public void Dispose() {outer.dispose(); inner.dispose();} +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_core_lnrs.java b/150_gfui/xtn/gplx/gfui/Swt_core_lnrs.java new file mode 100644 index 000000000..2181329f0 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_core_lnrs.java @@ -0,0 +1,139 @@ +/* +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.gfui; +import gplx.Byte_ascii; +import gplx.Enm_; +import gplx.Err_; +import gplx.GfoEvMgr_; +import gplx.GfoInvkAble_; +import gplx.GfoMsg_; +import gplx.GfsCtx; +import gplx.String_; +import gplx.Tfds; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.ToolItem; +class Swt_lnr_show implements Listener { + boolean shown = false; + @Override public void handleEvent(Event ev) { + if (shown) return; + win.Opened(); + } + public Swt_lnr_show(Swt_win win) {this.win = win;} Swt_win win; +} +class Swt_lnr_resize implements Listener { + @Override public void handleEvent(Event ev) { +// win.Host().SizeChangedCbk(); + GfoEvMgr_.Pub((GfuiWin)win.Host(), Gfui_html.Evt_win_resized); + } + public Swt_lnr_resize(Swt_win win) {this.win = win;} Swt_win win; +} +class Swt_lnr_key implements KeyListener { + public Swt_lnr_key(GxwElem elem) {this.elem = elem;} GxwElem elem; +// static int counter = 0; + @Override public void keyPressed(KeyEvent ev) { + IptEvtDataKey data = XtoKeyData(ev); + if (!elem.Host().KeyDownCbk(data)) { + ev.doit = false; + } + } + @Override public void keyReleased(KeyEvent ev) { + if (!elem.Host().KeyUpCbk(XtoKeyData(ev))) ev.doit = false; + } + IptEvtDataKey XtoKeyData(KeyEvent ev) { + int val = ev.keyCode; + switch (val) { + case Byte_ascii.CarriageReturn: val = 10; break; // enter key is 13 whereas .net/swing is 10 + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + val -= 32; // lowercase keys are transmitted as ascii value, instead of key value; EX: "a" is 97 instead of 65 + break; + case 16777217: val = IptKey_.Up.Val(); break; + case 16777218: val = IptKey_.Down.Val(); break; + case 16777219: val = IptKey_.Left.Val(); break; + case 16777220: val = IptKey_.Right.Val(); break; + case 16777221: val = IptKey_.PageUp.Val(); break; + case 16777222: val = IptKey_.PageDown.Val(); break; + case 16777223: val = IptKey_.Home.Val(); break; + case 16777224: val = IptKey_.End.Val(); break; + case 16777226: val = IptKey_.F1.Val(); break; + case 16777227: val = IptKey_.F2.Val(); break; + case 16777228: val = IptKey_.F3.Val(); break; + case 16777229: val = IptKey_.F4.Val(); break; + case 16777230: val = IptKey_.F5.Val(); break; + case 16777231: val = IptKey_.F6.Val(); break; + case 16777232: val = IptKey_.F7.Val(); break; + case 16777233: val = IptKey_.F8.Val(); break; + case 16777234: val = IptKey_.F9.Val(); break; + case 16777235: val = IptKey_.F10.Val(); break; + case 16777236: val = IptKey_.F11.Val(); break; + case 16777237: val = IptKey_.F12.Val(); break; + case 16777300: val = IptKey_.ScrollLock.Val(); break; + case 16777301: val = IptKey_.Pause.Val(); break; + case 327680: val = IptKey_.Insert.Val(); break; + } + if (Has_ctrl(ev.stateMask)) val |= IptKey_.KeyCode_Ctrl; + if (Enm_.HasInt(ev.stateMask, IptKey_.KeyCode_Shift)) val |= IptKey_.KeyCode_Alt; + if (Enm_.HasInt(ev.stateMask, IptKey_.KeyCode_Ctrl)) val |= IptKey_.KeyCode_Shift; +// Tfds.Write(String_.Format("val={4} keyCode={0} stateMask={1} keyLocation={2} character={3}", ev.keyCode, ev.stateMask, ev.keyLocation, ev.character, val)); + return IptEvtDataKey.int_(val); + } + public static boolean Has_ctrl(int val) {return Enm_.HasInt(val, IptKey_.KeyCode_Alt);} // NOTE:SWT's ctrl constant is different from SWING's +} +class Swt_lnr_mouse implements MouseListener { + public Swt_lnr_mouse(GxwElem elem) {this.elem = elem;} GxwElem elem; + @Override public void mouseDown(MouseEvent ev) {elem.Host().MouseDownCbk(XtoMouseData(ev));} + @Override public void mouseUp(MouseEvent ev) {elem.Host().MouseUpCbk(XtoMouseData(ev));} + @Override public void mouseDoubleClick(MouseEvent ev) {} + IptEvtDataMouse XtoMouseData(MouseEvent ev) { + IptMouseBtn btn = null; + switch (ev.button) { + case 1: btn = IptMouseBtn_.Left; break; + case 2: btn = IptMouseBtn_.Middle; break; + case 3: btn = IptMouseBtn_.Right; break; + case 4: btn = IptMouseBtn_.X1; break; + case 5: btn = IptMouseBtn_.X2; break; + } + return IptEvtDataMouse.new_(btn, IptMouseWheel_.None, ev.x, ev.y); + } +// private static int X_to_swing(int v) { +// switch (v) { +// case gplx.gfui.IptMouseBtn_.Tid_left : return java.awt.event.InputEvent.BUTTON1_MASK; +// case gplx.gfui.IptMouseBtn_.Tid_middle : return java.awt.event.InputEvent.BUTTON2_MASK; +// case gplx.gfui.IptMouseBtn_.Tid_right : return java.awt.event.InputEvent.BUTTON3_MASK; +// default : throw Err_.unhandled(v); +// } +// } +} +class Swt_lnr_toolitem implements Listener { + public Swt_lnr_toolitem(ToolItem itm, GxwElem elem) {this.itm = itm; this.elem = elem;} ToolItem itm; GxwElem elem; + @Override public void handleEvent(Event arg0) { + Rectangle rect = itm.getBounds(); + elem.Host().MouseUpCbk(IptEvtDataMouse.new_(IptMouseBtn_.Left, IptMouseWheel_.None, rect.x, rect.y)); + } +} \ No newline at end of file diff --git a/150_gfui/xtn/gplx/gfui/Swt_demo_main.java b/150_gfui/xtn/gplx/gfui/Swt_demo_main.java new file mode 100644 index 000000000..29a78b88a --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_demo_main.java @@ -0,0 +1,199 @@ +/* +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.gfui; +import org.eclipse.swt.*; +import org.eclipse.swt.browser.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; +public class Swt_demo_main { + public static void main(String[] args) { +// Drag_drop(); +// List_fonts(); + keystrokes(args); + } + static void Drag_drop() { + final Display display = new Display(); + final Shell shell = new Shell(display); + shell.setLayout(new GridLayout()); + final CTabFolder folder = new CTabFolder(shell, SWT.BORDER); + folder.setLayoutData(new GridData(GridData.FILL_BOTH)); + for (int i = 0; i < 10; i++) { + CTabItem item = new CTabItem(folder, SWT.NONE); + item.setText("item "+i); + Text text = new Text(folder, SWT.BORDER | SWT.MULTI | SWT.VERTICAL); + text.setText("Text control for "+i); + item.setControl(text); + if (i == 9) { + item.setShowClose(false); + item.setText("+"); +// item.setImage(new Image(Display.getDefault(), "J:\\gplx\\xowa\\user\\anonymous\\app\\img\\edit\\format-bold-A.png")); + } + } + ToolBar t = new ToolBar( folder, SWT.FLAT ); + ToolItem i = new ToolItem( t, SWT.PUSH ); + i.setText( "add" ); + folder.setTopRight( t, SWT.RIGHT ); + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + } + static void keystrokes(String[] args) + { + + Display display = new Display (); + + final Shell shell = new Shell (display); + +// display.addFilter(SWT.KeyDown, new Listener() { +// +// public void handleEvent(Event e) { +// if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f')) +// { +// System.out.println("From Display I am the Key down !!" + e.keyCode); +// } +// } +// }); + shell.addKeyListener(new KeyListener() { + public void keyReleased(KeyEvent e) { +// if(((e.stateMask & SWT.CTRL) == SWT.CTRL) && (e.keyCode == 'f')) +// { +// shell.setBackground(orig); +// System.out.println("Key up !!"); +// } + System.out.println(e.stateMask + " " + e.keyCode); + } + public void keyPressed(KeyEvent e) { +// System.out.println(e.stateMask + " " + e.keyCode); + } + }); + shell.addMouseListener(new MouseListener() { + @Override + public void mouseUp(MouseEvent arg0) { + // TODO Auto-generated method stub + System.out.println(arg0.button); + } + + @Override + public void mouseDown(MouseEvent arg0) { + // TODO Auto-generated method stub + + } + + @Override + public void mouseDoubleClick(MouseEvent arg0) { + // TODO Auto-generated method stub + + } + }); + + shell.setSize (200, 200); + shell.open (); + while (!shell.isDisposed()) { + if (!display.readAndDispatch ()) display.sleep (); + } + display.dispose (); + + } + static void List_fonts() { + java.awt.GraphicsEnvironment e = java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(); + java.awt.Font[] fonts = e.getAllFonts(); // Get the fonts + for (java.awt.Font f : fonts) { + System.out.println(f.getFontName()); + } + } + static void Permission_denied() { + String html + = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + " click to call permissionDeniedExample -> will throw error and not show sel.rangeCount
\n" + + " click to call permissionDeniedExample inside a setTimeout -> will show sel.rangeCount
\n" + + "\n" + + "\n" + ; + + System.setProperty + ( "org.eclipse.swt.browser.XULRunnerPath" + // ADJUST THIS PATH AS NECESSARY ON YOUR MACHINE + , "C:\\xowa\\bin\\windows\\xulrunner" + ); + Display display = new Display(); + Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + final Browser browser; + try { + browser = new Browser(shell, SWT.MOZILLA); // changed from none + browser.addLocationListener(new LocationListener() { + @Override + public void changing(LocationEvent arg0) { + if (arg0.location.equals("about:blank")) return; + arg0.doit = false; + } + + @Override + public void changed(LocationEvent arg0) { + String location = arg0.location; + if (location.equals("about:blank")) return; + + // build code + String code = "alert('unknown_link:" + location + "')"; + if (location.contains("direct_call_fails")) + code = "permissionDeniedExample();"; + else if (location.contains("wrapped_call_works")) + code = "setTimeout(function(){permissionDeniedExample();}, 1);"; + + // evaluate code + try { + browser.evaluate(code); + } catch (Exception e) { + System.out.println(e); + } + arg0.doit = false; + } + }); + } catch (SWTError e) { + System.out.println("Could not instantiate Browser: " + e.getMessage()); + display.dispose(); + return; + } + browser.setText(html); + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) + display.sleep(); + } + display.dispose(); + } +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_dlg_msg.java b/150_gfui/xtn/gplx/gfui/Swt_dlg_msg.java new file mode 100644 index 000000000..784327479 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_dlg_msg.java @@ -0,0 +1,96 @@ +/* +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.gfui; import gplx.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.SWT; +class Swt_dlg_file implements Gfui_dlg_file { + private FileDialog under; + public Swt_dlg_file(byte type, Shell shell) { + int file_dialog_type + = type == Gfui_kit_.File_dlg_type_save + ? SWT.SAVE + : SWT.OPEN + ; + under = new FileDialog(shell, file_dialog_type); + } + public Gfui_dlg_file Init_msg_(String v) {under.setText(v); return this;} + public Gfui_dlg_file Init_file_(String v) {under.setFileName(v); return this;} + public Gfui_dlg_file Init_dir_(Io_url v) {under.setFilterPath(v.Xto_api()); return this;} + public Gfui_dlg_file Init_exts_(String... v) {under.setFilterExtensions(v); return this;} + public String Ask() {return under.open();} +} +class Swt_dlg_msg implements Gfui_dlg_msg, Runnable { + public Swt_dlg_msg(Shell shell) {this.shell = shell;} Shell shell; + public Gfui_dlg_msg Init_msg_(String v) {msg = v; return this;} String msg; + public Gfui_dlg_msg Init_ico_(int v) {ico = Xto_swt_ico(v); return this;} int ico = -1; + public Gfui_dlg_msg Init_btns_(int... ary) { + int ary_len = ary.length; + btns = -1; + for (int i = 0; i < ary_len; i++) { + int swt_btn = Xto_swt_btn(ary[i]); + if (btns == -1) btns = swt_btn; + else btns |= swt_btn; + } + return this; + } int btns = -1; + public int Ask_rslt; + @Override public void run() { + Ask_rslt = this.Ask(); + } + public boolean Ask(int expd) {return Ask() == expd;} + public int Ask() { + int ctor_ico = ico == -1 ? SWT.ICON_INFORMATION : ico; + int ctor_btn = btns == -1 ? SWT.OK : btns; + MessageBox mb = new MessageBox(shell, ctor_ico | ctor_btn); + if (msg != null) mb.setMessage(msg); + int rv = mb.open(); + return Xto_gfui_btn(rv); + } + int Xto_swt_ico(int v) { + switch (v) { + case Gfui_dlg_msg_.Ico_error: return SWT.ICON_ERROR; + case Gfui_dlg_msg_.Ico_information: return SWT.ICON_INFORMATION; + case Gfui_dlg_msg_.Ico_question: return SWT.ICON_QUESTION; + case Gfui_dlg_msg_.Ico_warning: return SWT.ICON_WARNING; + case Gfui_dlg_msg_.Ico_working: return SWT.ICON_WORKING; + default: throw Err_mgr._.unhandled_(v); + } + } + int Xto_swt_btn(int v) { + switch (v) { + case Gfui_dlg_msg_.Btn_ok: return SWT.OK; + case Gfui_dlg_msg_.Btn_yes: return SWT.YES; + case Gfui_dlg_msg_.Btn_no: return SWT.NO; + case Gfui_dlg_msg_.Btn_ignore: return SWT.IGNORE; + case Gfui_dlg_msg_.Btn_abort: return SWT.ABORT; + case Gfui_dlg_msg_.Btn_cancel: return SWT.CANCEL; + default: throw Err_mgr._.unhandled_(v); + } + } + int Xto_gfui_btn(int v) { + switch (v) { + case SWT.OK: return Gfui_dlg_msg_.Btn_ok; + case SWT.YES: return Gfui_dlg_msg_.Btn_yes; + case SWT.NO: return Gfui_dlg_msg_.Btn_no; + case SWT.IGNORE: return Gfui_dlg_msg_.Btn_ignore; + case SWT.ABORT: return Gfui_dlg_msg_.Btn_abort; + case SWT.CANCEL: return Gfui_dlg_msg_.Btn_cancel; + default: throw Err_mgr._.unhandled_(v); + } + } +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_html.java b/150_gfui/xtn/gplx/gfui/Swt_html.java new file mode 100644 index 000000000..50dd303ba --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_html.java @@ -0,0 +1,263 @@ +/* +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.gfui; +import gplx.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.browser.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; +class Swt_html implements Gxw_html, Swt_control, FocusListener { + private Swt_html_lnr_location lnr_location; private Swt_html_lnr_status lnr_status; + public Swt_html(Swt_kit kit, Swt_control owner_control, KeyValHash ctorArgs) { + this.kit = kit; + lnr_location = new Swt_html_lnr_location(this); + lnr_status = new Swt_html_lnr_status(this); + int browser_type = Swt_html.Browser_tid_none; + Object browser_type_obj = ctorArgs.FetchValOr(Swt_kit.Cfg_Html_BrowserType, null); + if (browser_type_obj != null) browser_type = Int_.cast_(browser_type_obj); + browser = new Browser(owner_control.Under_composite(), browser_type); + core = new Swt_core_cmds_html(this, browser); + browser.addKeyListener(new Swt_lnr_key(this)); + browser.addMouseListener(new Swt_html_lnr_mouse(this, browser, kit)); + browser.addLocationListener(lnr_location); + browser.addProgressListener(new Swt_html_lnr_progress(this)); + browser.addStatusTextListener(lnr_status); + browser.addFocusListener(this); + browser.addTitleListener(new Swt_html_lnr_title(this)); +// browser.addTraverseListener(new Swt_html_lnr_Traverse(this)); + } + public Swt_kit Kit() {return kit;} private Swt_kit kit; + @Override public Control Under_control() {return browser;} private Browser browser; + @Override public Composite Under_composite() {return null;} + @Override public Control Under_menu_control() {return browser;} + public String Html_doc_html() {return Eval_script_as_str(kit.Html_cfg().Doc_html());} + public void Html_doc_html_(String s) {browser.setText(s);} // DBG: Io_mgr._.SaveFilStr(Io_url_.new_fil_("C:\\temp.txt"), s) + public String Html_doc_selected_get_text_or_href() {return Eval_script_as_str(kit.Html_cfg().Doc_selected_get_text_or_href());} + public String Html_doc_selected_get_href_or_text() {return Eval_script_as_str(kit.Html_cfg().Doc_selected_get_href_or_text());} + public String Html_doc_selected_get_src_or_empty() {return Eval_script_as_str(kit.Html_cfg().Doc_selected_get_src_or_empty());} + public void Html_doc_body_focus() {Eval_script_as_exec(kit.Html_cfg().Doc_body_focus());} + public String Html_elem_atr_get_str(String elem_id, String atr_key) {return Eval_script_as_str(kit.Html_cfg().Elem_atr_get(elem_id, atr_key));} + public boolean Html_elem_atr_get_bool(String elem_id, String atr_key) {return Bool_.parse_((String)Eval_script(kit.Html_cfg().Elem_atr_get_toString(elem_id, atr_key)));} + public Object Html_elem_atr_get_obj(String elem_id, String atr_key) {return Eval_script(kit.Html_cfg().Elem_atr_get(elem_id, atr_key));} + public boolean Html_elem_atr_set(String elem_id, String atr_key, String atr_val){return Eval_script_as_exec(kit.Html_cfg().Elem_atr_set(elem_id, atr_key, Escape_quotes(atr_val)));} + public boolean Html_elem_atr_set_append(String elem_id, String atr_key, String atr_val) + {return Eval_script_as_exec(kit.Html_cfg().Elem_atr_set_append(elem_id, atr_key, Escape_quotes(atr_val)));} + public boolean Html_elem_delete(String elem_id) {return Eval_script_as_exec(kit.Html_cfg().Elem_delete(elem_id));} + public boolean Html_elem_replace_html(String id, String html) {return Eval_script_as_exec(kit.Html_cfg().Elem_replace_html(id, html));} + public boolean Html_gallery_packed_exec() {return Eval_script_as_exec(kit.Html_cfg().Gallery_packed_exec());} + public boolean Html_elem_focus(String elem_id) {return Eval_script_as_exec(kit.Html_cfg().Elem_focus(elem_id));} + public boolean Html_elem_scroll_into_view(String id) {return Eval_script_as_bool(kit.Html_cfg().Elem_scroll_into_view(Escape_quotes(id)));} + public String Html_window_vpos() {return Eval_script_as_str(kit.Html_cfg().Window_vpos());} + public boolean Html_window_print_preview() {return Eval_script_as_bool(kit.Html_cfg().Window_print_preview());} + public void Html_js_enabled_(boolean v) {browser.setJavascriptEnabled(v);} + public void Html_js_cbks_add(String func_name, GfoInvkAble invk) {new Swt_html_func(browser, func_name, invk);} + public String Html_js_eval_script(String script) {return Eval_script_as_str(script);} + public boolean Html_elem_img_update(String elem_id, String elem_src, int elem_width, int elem_height) { + elem_src = Escape_quotes(elem_src); + return Eval_script_as_bool(kit.Html_cfg().Elem_img_update(elem_id, elem_src, elem_width, elem_height)); + } + public String Html_active_atr_get_str(String atr_key, String or) { + Object rv_obj = Eval_script(kit.Html_cfg().Active_atr_get_str(atr_key)); + String rv = (String)rv_obj; + return rv == null || !eval_rslt.Result_pass() ? or : rv; + } + public void Html_js_eval_proc(String proc, String... args) { + Bry_fmtr fmtr = kit.Html_cfg().Js_scripts_get(proc); + String script = fmtr.Bld_str_many(args); + Eval_script(script); + } + public boolean Html_window_vpos_(String v) { + Gfui_html_cfg.Html_window_vpos_parse(v, scroll_top, node_path); + return Eval_script_as_exec(kit.Html_cfg().Window_vpos_(node_path.Val(), scroll_top.Val())); + } private String_obj_ref scroll_top = String_obj_ref.null_(), node_path = String_obj_ref.null_(); + public boolean Html_doc_find(String elem_id, String find, boolean dir_fwd, boolean case_match, boolean wrap_find) { + if (String_.Eq(find, String_.Empty)) return false; + find = String_.Replace(find, "\\", "\\\\"); // escape \ -> \\ + find = String_.Replace(find, "'", "\\'"); // escape ' -> \'; NOTE: \\' instead of \' + boolean search_text_is_diff = !String_.Eq(find, prv_find_str); + prv_find_str = find; + String script = String_.Eq(elem_id, Gfui_html.Elem_id_body) + ? kit.Html_cfg().Doc_find_html(find, dir_fwd, case_match, wrap_find, search_text_is_diff, prv_find_bgn) + : kit.Html_cfg().Doc_find_edit(find, dir_fwd, case_match, wrap_find, search_text_is_diff, prv_find_bgn); + Object result_obj = Eval_script(script); + try {prv_find_bgn = (int)Double_.cast_(result_obj);} + catch (Exception e) {Err_.Noop(e); return false;} + return true; + } private String prv_find_str = ""; private int prv_find_bgn; + public void Html_invk_src_(GfoEvObj invk) {lnr_location.Host_set(invk); lnr_status.Host_set(invk);} + private String Escape_quotes(String v) {return String_.Replace(String_.Replace(v, "'", "\\'"), "\"", "\\\"");} + @Override public GxwCore_base Core() {return core;} private GxwCore_base core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public String TextVal() {return browser.getText();} + @Override public void TextVal_set(String v) {browser.setText(v);} + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return GfoInvkAble_.Rv_unhandled;} + private boolean Eval_script_as_bool(String script) { + Object result_obj = Eval_script(script); + return eval_rslt.Result_pass() && Bool_.cast_or_(result_obj, false); + } + private boolean Eval_script_as_exec(String script) {Eval_script(script); return eval_rslt.Result_pass();} + private String Eval_script_as_str(String script) {return (String)Eval_script(script);} + public Object Eval_script(String script) { + eval_rslt.Clear(); + try { + eval_rslt.Result_set(browser.evaluate(script)); + return eval_rslt.Result(); + } + catch (Exception e) {eval_rslt.Error_set(e.getMessage()); return eval_rslt.Error();} + } private Swt_html_eval_rslt eval_rslt = new Swt_html_eval_rslt(); + @Override public void focusGained(FocusEvent arg0) { +// if (!focus_acquired && Swt_kit.Html_box_focus_automatically) { +// browser.forceFocus(); +// focus_acquired = true; +// HtmlBox_focus(); +// } + } //boolean focus_acquired = false; + @Override public void focusLost(FocusEvent arg0) { +// focus_acquired = false; + } + public static final int + Browser_tid_none = SWT.NONE + , Browser_tid_mozilla = SWT.MOZILLA + , Browser_tid_webKit = SWT.WEBKIT + ; +} +class Swt_core_cmds_html extends Swt_core_cmds { + public Swt_core_cmds_html(Swt_html html_box, Control control) {super(control); this.html_box = html_box;} Swt_html html_box; + @Override public void Focus() { + if (Focus_able()) + control.forceFocus(); + } + @Override public void Select_exec() { + this.Focus(); + } +} +class Swt_html_eval_rslt { + public void Clear() {error = null; result = null;} + public boolean Result_pass() {return error == null;} + public Object Result() {return result;} public void Result_set(Object v) {result = v; error = null;} Object result; + public String Error () {return error;} public void Error_set(String v) {error = v; result = null;} String error; +} +class Swt_html_lnr_Traverse implements TraverseListener { + public Swt_html_lnr_Traverse(Swt_html html_box) {this.html_box = html_box;} Swt_html html_box; + @Override public void keyTraversed(TraverseEvent arg0) {} +} +class Swt_html_lnr_title implements TitleListener { + public Swt_html_lnr_title(Swt_html html_box) {this.html_box = html_box;} Swt_html html_box; + @Override public void changed(TitleEvent ev) { + try {UsrDlg_._.Note(ev.title);} + catch (Exception e) {html_box.Kit().Ask_ok("xowa.swt.html_box", "title.fail", Err_.Message_gplx_brief(e));} // NOTE: must catch error or will cause app to lock; currently called inside displaySync + } +} +class Swt_html_func extends BrowserFunction { + public Swt_html_func(Browser browser, String name, GfoInvkAble invk) { + super (browser, name); + this.browser = browser; + this.invk = invk; + } Browser browser; GfoInvkAble invk; + public Object function (Object[] args) { + try { + return gplx.gfui.Gfui_html.Js_args_exec(invk, args); + } + catch (Exception e) { + return Err_.Message_gplx_brief(e); + } + } +} +class Swt_html_lnr_status implements StatusTextListener { + public Swt_html_lnr_status(Swt_html html_box) {this.html_box = html_box;} Swt_html html_box; + public void Host_set(GfoEvObj host) {this.host = host;} GfoEvObj host; + @Override public void changed(StatusTextEvent ev) { + if (html_box.Kit().Mode_is_shutdown()) + return; // shutting down raises status changed events; ignore, else SWT exception thrown; DATE:2014-05-29 + String ev_text = ev.text; +// if (String_.Has(ev_text, "Loading [MathJax]")) return; // suppress MathJax messages; // NOTE: disabled for 2.1 (which no longer outputs messages to status); DATE:2013-05-03 + try {if (host != null) GfoEvMgr_.PubObj(host, Gfui_html.Evt_link_hover, "v", ev_text);} + catch (Exception e) {html_box.Kit().Ask_ok("xowa.gui.html_box", "status.fail", Err_.Message_gplx_brief(e));} // NOTE: must catch error or will cause app to lock; currently called inside displaySync + } +} +class Swt_html_lnr_progress implements ProgressListener { + public Swt_html_lnr_progress(Swt_html html_box) {this.html_box = html_box;} Swt_html html_box; + @Override public void changed(ProgressEvent arg0) {} + @Override public void completed(ProgressEvent arg0) { +// UsrDlg_._.Note("done"); + } +} +class Swt_html_lnr_location implements LocationListener { + public Swt_html_lnr_location(Swt_html html_box) {this.html_box = html_box;} Swt_html html_box; + public void Host_set(GfoEvObj host) {this.host = host;} GfoEvObj host; + @Override public void changed(LocationEvent arg) {Pub_evt(arg, Gfui_html.Evt_location_changed);} + @Override public void changing(LocationEvent arg) {Pub_evt(arg, Gfui_html.Evt_location_changing);} + void Pub_evt(LocationEvent arg, String evt) { + String location = arg.location; + if (String_.Eq(location, "about:blank")) return; // location changing event fires once when page is loaded; ignore + try { + GfoEvMgr_.PubObj(host, evt, "v", location); + arg.doit = false; // cancel navigation event, else there will be an error when trying to go to invalid location + } + catch (Exception e) {html_box.Kit().Ask_ok("xowa.gui.html_box", evt, Err_.Message_gplx_brief(e));} // NOTE: must catch error or will cause app to lock; currently called inside displaySync + } +} +class Swt_html_lnr_mouse implements MouseListener { + public Swt_html_lnr_mouse(GxwElem elem, Browser browser, Swt_kit kit) {this.elem = elem; this.browser = browser; this.kit = kit;} GxwElem elem; Browser browser; Swt_kit kit; + @Override public void mouseDown(MouseEvent ev) { + if (Is_at_scrollbar_area()) return; + elem.Host().MouseDownCbk(XtoMouseData(ev)); + } + @Override public void mouseUp(MouseEvent ev) { + if (Is_at_scrollbar_area()) return; + elem.Host().MouseUpCbk(XtoMouseData(ev)); + } + boolean Is_at_scrollbar_area() { + // WORKAROUND.SWT: SEE:NOTE_1:browser scrollbar and click + Point browser_size = browser.getSize(); + Point click_pos = kit.Swt_display().getCursorLocation(); + return click_pos.x >= browser_size.x - 12; + } + @Override public void mouseDoubleClick(MouseEvent ev) {} + IptEvtDataMouse XtoMouseData(MouseEvent ev) { + IptMouseBtn btn = null; + switch (ev.button) { + case 1: btn = IptMouseBtn_.Left; break; + case 2: btn = IptMouseBtn_.Middle; break; + case 3: btn = IptMouseBtn_.Right; break; + case 4: btn = IptMouseBtn_.X1; break; + case 5: btn = IptMouseBtn_.X2; break; + } + return IptEvtDataMouse.new_(btn, IptMouseWheel_.None, ev.x, ev.y); + } +} +/* +NOTE_1:browser scrollbar and click +a click in the scrollbar area will raise a mouse-down/mouse-up event in content-editable mode +. a click should be consumed by the scrollbar and not have any effect elsewhere on the window +. instead, a click event is raised, and counted twice + 1) for the scroll bar this will scroll the area. + 2) for the window. if keyboard-focus is set on a link, then it will activate the link. + +swt does not expose any scrollbar information (visible, width), b/c the scrollbar is controlled by the underlying browser +so, assume: +. scrollbar is always present +. scrollbar has arbitrary width (currently 12) +. and discard if click is in this scrollbar area + +two issues still occur with the workaround +1) even if the scrollbar is not present, any click on the right-hand edge of the screen will be ignored +2) click -> hold -> move mouse over to left -> release; the mouse up should be absorbed, but it is not due to position of release +*/ diff --git a/150_gfui/xtn/gplx/gfui/Swt_img.java b/150_gfui/xtn/gplx/gfui/Swt_img.java new file mode 100644 index 000000000..04752a8ce --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_img.java @@ -0,0 +1,47 @@ +/* +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.gfui; import gplx.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; + +class Swt_img implements ImageAdp { + public Swt_img(Gfui_kit kit, Image under, int w, int h) {this.kit = kit; this.under = under; this.width = w; this.height = h;} + public Gfui_kit Kit() {return kit;} Gfui_kit kit; + public SizeAdp Size() {if (size == null) size = SizeAdp_.new_(width, height); return size;} SizeAdp size; + public int Width() {return width;} int width; + public int Height() {return height;} int height; + public Io_url Url() {return url;} public ImageAdp Url_(Io_url v) {url = v; return this;} Io_url url = Io_url_.Null; + public Object Under() {return under;} Image under; + public boolean Disposed() {return under.isDisposed();} + public void Rls() {under.dispose();} + public void SaveAsBmp(Io_url url) {throw Err_.not_implemented_();} + public void SaveAsPng(Io_url url) {throw Err_.not_implemented_();} + public ImageAdp Resize(int trg_w, int trg_h) {return Extract_image(0, 0, width, height, trg_w, trg_h);} + public ImageAdp Extract_image(RectAdp src_rect, SizeAdp trg_size) {return Extract_image(src_rect.X(), src_rect.Y(), src_rect.Width(), src_rect.Height(), trg_size.Width(), trg_size.Height());} + public ImageAdp Extract_image(int src_x, int src_y, int src_w, int src_h, int trg_w, int trg_h) { + Image trg_img = new Image(Display.getDefault(), trg_w, trg_h); + GC gc = new GC(trg_img); + gc.setAntialias(SWT.ON); + gc.setInterpolation(SWT.HIGH); + gc.drawImage(under, src_x, src_y, src_w, src_h, 0, 0, trg_w, trg_h); + gc.dispose(); + return new Swt_img(kit, trg_img, trg_w, trg_h); + } +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_popup_grp.java b/150_gfui/xtn/gplx/gfui/Swt_popup_grp.java new file mode 100644 index 000000000..4c590450f --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_popup_grp.java @@ -0,0 +1,220 @@ +/* +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.gfui; import java.security.acl.Owner; + +import gplx.*; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.events.MenuDetectEvent; +import org.eclipse.swt.events.MenuDetectListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Decorations; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.swt.widgets.Shell; +class Swt_popup_grp implements Gfui_mnu_grp { + private Decorations owner_win; private Control owner_box; private boolean menu_is_bar = false; + Swt_popup_grp(String root_key){this.root_key = root_key;} + @Override public int Tid() {return Gfui_mnu_itm_.Tid_grp;} + @Override public String Uid() {return uid;} private String uid = Gfui_mnu_itm_.Gen_uid(); + @Override public boolean Enabled() {return menu.getEnabled();} @Override public void Enabled_(boolean v) {menu.setEnabled(v);} + @Override public String Text() {return menu_item.getText();} @Override public void Text_(String v) {menu_item.setText(v);} + @Override public ImageAdp Img() {return img;} @Override public void Img_(ImageAdp v) { + img = v; + if (v == ImageAdp_.Null) + menu_item.setImage(null); + else + menu_item.setImage((Image)v.Under()); + } private ImageAdp img; + @Override public boolean Selected() {return menu_item.getSelection();} @Override public void Selected_(boolean v) {menu_item.setSelection(v);} + public String Root_key() {return root_key;} private String root_key; + public Menu Under_menu() {return menu;} private Menu menu; + public MenuItem Under_menu_item() {return menu_item;} private MenuItem menu_item; + public Object Under() {return menu;} + @Override public void Itms_clear() { + menu.dispose(); + if (menu_is_bar) { + menu = new Menu(owner_win, SWT.BAR); + owner_win.setMenuBar(menu); + } + else { + menu = new Menu(owner_box); + owner_box.setMenu(menu); + } + } + @Override public Gfui_mnu_itm Itms_add_btn_cmd(String txt, ImageAdp img, GfoInvkAble invk, String cmd) { + Swt_popup_itm itm = new Swt_popup_itm(menu); + itm.Text_(txt); + if (img != null) itm.Img_(img); + itm.Invk_set_cmd(invk, cmd); + return itm; + } + @Override public Gfui_mnu_itm Itms_add_btn_msg(String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg invk_msg) { + Swt_popup_itm itm = new Swt_popup_itm(menu); + itm.Text_(txt); + if (img != null) itm.Img_(img); + itm.Invk_set_msg(root_wkr, invk, invk_msg); + return itm; + } + @Override public Gfui_mnu_itm Itms_add_chk_msg(String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg_n, GfoMsg msg_y) { + Swt_popup_itm itm = new Swt_popup_itm(menu, SWT.CHECK); + itm.Text_(txt); + if (img != null) itm.Img_(img); + itm.Invk_set_chk(root_wkr, invk, msg_n, msg_y); + return itm; + } + @Override public Gfui_mnu_itm Itms_add_rdo_msg(String txt, ImageAdp img, GfoInvkAble invk, GfoInvkRootWkr root_wkr, GfoMsg msg) { + Swt_popup_itm itm = new Swt_popup_itm(menu, SWT.RADIO); + itm.Text_(txt); + if (img != null) itm.Img_(img); + itm.Invk_set_msg(root_wkr, invk, msg); + return itm; + } + @Override public Gfui_mnu_itm Itms_add_separator() { + new MenuItem(menu, SWT.SEPARATOR); + return null; + } + @Override public Gfui_mnu_grp Itms_add_grp(String txt, ImageAdp img) { + Swt_popup_itm itm = new Swt_popup_itm(menu, SWT.CASCADE); + if (img != null) itm.Img_(img); + itm.Text_(txt); + + Swt_popup_grp grp = new_grp(root_key, owner_win); + grp.menu_item = itm.Under_menu_item(); + itm.Text_(txt); + + itm.Under_menu_item().setMenu(grp.Under_menu()); + return grp; + } + public static Swt_popup_grp new_popup(String key, GfuiElem owner_elem) { + Swt_popup_grp rv = new Swt_popup_grp(key); + Shell owner_win = cast_to_shell(owner_elem.OwnerWin()); + Control owner_box = ((Swt_control)owner_elem.UnderElem()).Under_control(); + rv.owner_win = owner_win; rv.owner_box = owner_box; + rv.menu = new Menu(owner_box); + owner_box.setMenu(rv.menu); + return rv; + } + + private static Swt_popup_grp new_grp(String key, Decorations owner_win) { + Swt_popup_grp rv = new Swt_popup_grp(key); + rv.owner_win = owner_win; + rv.menu = new Menu(owner_win, SWT.DROP_DOWN); + return rv; + } + public static Swt_popup_grp new_bar(String key, GfuiWin win) { + Swt_popup_grp rv = new Swt_popup_grp(key); + Shell owner_win = cast_to_shell(win); + rv.owner_win = owner_win; + rv.menu_is_bar = true; + rv.menu = new Menu(owner_win, SWT.BAR); + owner_win.setMenuBar(rv.menu); + return rv; + } + private static Shell cast_to_shell(GfuiWin win) { + return ((Swt_win)win.UnderElem()).UnderShell(); + } +} +class Swt_lnr__menu_detect implements MenuDetectListener { + private GfuiElem elem; + public Swt_lnr__menu_detect(GfuiElem elem) {this.elem = elem;} + @Override public void menuDetected(MenuDetectEvent arg0) { + GfoEvMgr_.Pub(elem, GfuiElemKeys.Evt_menu_detected); + } +} +class Swt_popup_itm implements Gfui_mnu_itm { + private Menu menu; + public Swt_popup_itm(Menu menu) { + this.menu = menu; itm = new MenuItem(menu, SWT.NONE); + this.tid = Gfui_mnu_itm_.Tid_btn; + } + public Swt_popup_itm(Menu menu, int swt_type) { + this.menu = menu; itm = new MenuItem(menu, swt_type); + switch (swt_type) { + case SWT.CASCADE: this.tid = Gfui_mnu_itm_.Tid_grp; break; + case SWT.CHECK: this.tid = Gfui_mnu_itm_.Tid_chk; break; + case SWT.RADIO: this.tid = Gfui_mnu_itm_.Tid_rdo; break; + default: throw Err_.unhandled(swt_type); + } + } + @Override public int Tid() {return tid;} private int tid; + @Override public String Uid() {return uid;} private String uid = Gfui_mnu_itm_.Gen_uid(); + @Override public boolean Enabled() {return itm.getEnabled();} @Override public void Enabled_(boolean v) {itm.setEnabled(v);} + @Override public String Text() {return itm.getText();} @Override public void Text_(String v) {itm.setText(v);} + @Override public ImageAdp Img() {return img;} @Override public void Img_(ImageAdp v) { + img = v; + if (v != ImageAdp_.Null) + itm.setImage((Image)v.Under()); + } private ImageAdp img; + @Override public boolean Selected() {return itm.getSelection();} + @Override public void Selected_(boolean v) { + selected_changing = true; + itm.setSelection(v); + selected_changing = false; + } + public boolean Selected_changing() {return selected_changing;} private boolean selected_changing; + @Override public Object Under() {return menu;} + public MenuItem Under_menu_item() {return itm;} private MenuItem itm; + public void Invk_set_cmd(GfoInvkAble invk, String cmd) { + itm.addListener(SWT.Selection, new Swt_lnr__menu_btn_cmd(invk, cmd)); + } + public void Invk_set_msg(GfoInvkRootWkr root_wkr, GfoInvkAble invk, GfoMsg msg) { + itm.addListener(SWT.Selection, new Swt_lnr__menu_btn_msg(root_wkr, invk, msg)); + } + public void Invk_set_chk(GfoInvkRootWkr root_wkr, GfoInvkAble invk, GfoMsg msg_n, GfoMsg msg_y) { + itm.addListener(SWT.Selection, new Swt_lnr__menu_chk_msg(this, root_wkr, invk, msg_n, msg_y)); + } +} +class Swt_lnr__menu_btn_cmd implements Listener { + public Swt_lnr__menu_btn_cmd(GfoInvkAble invk, String cmd) {this.invk = invk; this.cmd = cmd;} GfoInvkAble invk; String cmd; + public void handleEvent(Event ev) { + try {GfoInvkAble_.InvkCmd(invk, cmd);} + catch (Exception e) {Swt_kit._.Ask_ok("", "", "error while invoking command: cmd=~{0} err=~{1}", cmd, Err_.Message_gplx_brief(e));} + } +} +class Swt_lnr__menu_btn_msg implements Listener { + private GfoInvkRootWkr root_wkr; private GfoInvkAble invk; private GfoMsg msg; + public Swt_lnr__menu_btn_msg(GfoInvkRootWkr root_wkr, GfoInvkAble invk, GfoMsg msg) {this.root_wkr = root_wkr; this.invk = invk; this.msg = msg;} + public void handleEvent(Event ev) { + try { + msg.Args_reset(); + root_wkr.Run_str_for(invk, msg); + } + catch (Exception e) {Swt_kit._.Ask_ok("", "", "error while invoking command: cmd=~{0} err=~{1}", msg.Key(), Err_.Message_gplx_brief(e));} + } +} +class Swt_lnr__menu_chk_msg implements Listener { + private Swt_popup_itm mnu_itm; private GfoInvkRootWkr root_wkr; private GfoInvkAble invk; private GfoMsg msg_n, msg_y; + public Swt_lnr__menu_chk_msg(Swt_popup_itm mnu_itm, GfoInvkRootWkr root_wkr, GfoInvkAble invk, GfoMsg msg_n, GfoMsg msg_y) { + this.mnu_itm = mnu_itm; + this.root_wkr = root_wkr; this.invk = invk; this.msg_n = msg_n; this.msg_y = msg_y; + } + public void handleEvent(Event ev) { + if (mnu_itm.Selected_changing()) return; + GfoMsg msg = mnu_itm.Under_menu_item().getSelection() ? msg_y : msg_n; + try { + msg.Args_reset(); + root_wkr.Run_str_for(invk, msg); + } + catch (Exception e) {Swt_kit._.Ask_ok("", "", "error while invoking command: cmd=~{0} err=~{1}", msg.Key(), Err_.Message_gplx_brief(e));} + } +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_tab_itm.java b/150_gfui/xtn/gplx/gfui/Swt_tab_itm.java new file mode 100644 index 000000000..2c9aa27ea --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_tab_itm.java @@ -0,0 +1,55 @@ +/* +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.gfui; +import gplx.*; +import org.eclipse.swt.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; +public class Swt_tab_itm implements Gxw_tab_itm, Swt_control, FocusListener { + public CTabFolder Tab_mgr() {return tab_mgr;} private CTabFolder tab_mgr; + public Swt_tab_mgr Tab_mgr_swt() {return tab_mgr_swt;} private Swt_tab_mgr tab_mgr_swt; + public Gfui_tab_itm_data Tab_data() {return (Gfui_tab_itm_data)tab_itm.getData();} + public Swt_tab_itm(Swt_tab_mgr tab_mgr_swt, Swt_kit kit, CTabFolder tab_mgr, Gfui_tab_itm_data tab_data) { + this.tab_mgr_swt = tab_mgr_swt; this.kit = kit; this.tab_mgr = tab_mgr; + tab_itm = new CTabItem(tab_mgr, SWT.CLOSE); + tab_itm.setData(tab_data); + // core = new Swt_core_cmds(tab_itm); + } + public Swt_kit Kit() {return kit;} private Swt_kit kit; + @Override public Control Under_control() {return null;} + @Override public Composite Under_composite() {return null;} + @Override public Control Under_menu_control() {throw Err_.not_implemented_();} + @Override public String Tab_name() {return tab_itm.getText();} @Override public void Tab_name_(String v) {tab_itm.setText(v);} + @Override public String Tab_tip_text() {return tab_itm.getToolTipText();} @Override public void Tab_tip_text_(String v) {tab_itm.setToolTipText(v);} + public void Subs_add(GfuiElem sub) { + Swt_control swt_control = Swt_control_.cast_or_fail(sub); + tab_itm.setControl(swt_control.Under_control()); + } + public CTabItem Under_CTabItem() {return tab_itm;} private CTabItem tab_itm; + @Override public GxwCore_base Core() {return core;} GxwCore_base core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public String TextVal() {return "not implemented";} + @Override public void TextVal_set(String v) {} + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return GfoInvkAble_.Rv_unhandled;} + @Override public void focusGained(FocusEvent arg0) {} + @Override public void focusLost(FocusEvent arg0) {} +} +//#} \ No newline at end of file diff --git a/150_gfui/xtn/gplx/gfui/Swt_tab_mgr.java b/150_gfui/xtn/gplx/gfui/Swt_tab_mgr.java new file mode 100644 index 000000000..b7e9e58be --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_tab_mgr.java @@ -0,0 +1,269 @@ +/* +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.gfui; +import gplx.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; +public class Swt_tab_mgr implements Gxw_tab_mgr, Swt_control, FocusListener, GfoEvMgrOwner { + private GfuiInvkCmd cmd_async; // NOTE: async needed for some actions like responding to key_down and calling .setSelection; else app hangs; DATE:2014-04-30 + public Swt_tab_mgr(Swt_kit kit, Swt_control owner_control, KeyValHash ctorArgs) { + this.kit = kit; + tab_folder = new CTabFolder(owner_control.Under_composite(), SWT.BORDER); + tab_folder.setBorderVisible(false); + tab_folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + tab_folder.setSimple(true); + tab_folder.addListener(SWT.Selection, new Swt_tab_mgr_lnr_selection(this)); + tab_folder.addKeyListener(new Swt_lnr_key(this)); + + new Swt_tab_mgr_lnr_drag_drop(this, tab_folder); + tab_folder.addCTabFolder2Listener(new Swt_tab_mgr_lnr_close(this)); + core = new Swt_core_cmds(tab_folder); + cmd_async = kit.New_cmd_async(this); + } + public Swt_kit Kit() {return kit;} private Swt_kit kit; + public CTabFolder Under_ctabFolder() {return tab_folder;} + @Override public Control Under_control() {return tab_folder;} private CTabFolder tab_folder; + @Override public Composite Under_composite() {return tab_folder;} + @Override public Control Under_menu_control() {return tab_folder;} + public GfoEvMgr EvMgr() {return ev_mgr;} private GfoEvMgr ev_mgr; + public void EvMgr_(GfoEvMgr v) {ev_mgr = v;} + public ColorAdp Btns_selected_color() {return btns_selected_color;} private ColorAdp btns_selected_color; + public void Btns_selected_color_(ColorAdp v) { + btns_selected_color = v; + tab_folder.setSelectionBackground(kit.New_color(v)); + } + public ColorAdp Btns_unselected_color() {return btns_unselected_color;} + public void Btns_unselected_color_(ColorAdp v) { + btns_unselected_color = v; + tab_folder.setBackground(kit.New_color(v)); + } private ColorAdp btns_unselected_color; + @Override public boolean Btns_curved() {return tab_folder.getSimple();} @Override public void Btns_curved_(boolean v) {tab_folder.setSimple(!v);} + @Override public boolean Btns_place_on_top() {return tab_folder.getTabPosition() == SWT.TOP;} + @Override public void Btns_place_on_top_(boolean v) {tab_folder.setTabPosition(v ? SWT.TOP : SWT.BOTTOM); tab_folder.layout();} + @Override public int Btns_height() {return tab_folder.getTabHeight();} @Override public void Btns_height_(int v) {tab_folder.setTabHeight(v); tab_folder.layout();} + @Override public boolean Btns_close_visible() {return btns_close_visible;} private boolean btns_close_visible = true; + @Override public void Btns_close_visible_(boolean v) { + this.btns_close_visible = v; + CTabItem[] itms = tab_folder.getItems(); + int len = itms.length; + for (int i = 0; i < len; i++) + itms[i].setShowClose(v); + } + @Override public boolean Btns_unselected_close_visible() {return tab_folder.getUnselectedCloseVisible();} @Override public void Btns_unselected_close_visible_(boolean v) { + tab_folder.setUnselectedCloseVisible(v);} + @Override public Gxw_tab_itm Tabs_add(Gfui_tab_itm_data tab_data) { + Swt_tab_itm rv = new Swt_tab_itm(this, kit, tab_folder, tab_data); + rv.Under_CTabItem().setData(tab_data); + CTabItem ctab_itm = rv.Under_CTabItem(); + ctab_itm.setShowClose(btns_close_visible); + return rv; + } + @Override public void Tabs_close_by_idx(int i) { + CTabItem itm = tab_folder.getItems()[i]; + Gfui_tab_itm_data tab_data = Get_tab_data(itm); + CTabItem next_tab = Tabs_select_after_closing_itm(tab_data); // NOTE: must calc next_tab before calling Pub_tab_closed; latter will recalc idx + Pub_tab_closed(tab_data.Key()); // NOTE: dispose does not call event for .close; must manually raise event; + itm.dispose(); + this.Tabs_select_by_itm(next_tab); + } + @Override public void Tabs_select_by_idx(int i) { + if (i == Gfui_tab_itm_data.Idx_null) return; // 0 tabs; return; + msg_tabs_select_by_idx_swt.Clear(); + msg_tabs_select_by_idx_swt.Add("v", i); + cmd_async.Invk(GfsCtx._, 0, Invk_tabs_select_by_idx_swt, msg_tabs_select_by_idx_swt); + } private GfoMsg msg_tabs_select_by_idx_swt = GfoMsg_.new_cast_(Invk_tabs_select_by_idx_swt); + @Override public void Tabs_switch(int src, int trg) {Tabs_switch(tab_folder.getItem(src), tab_folder.getItem(trg));} + public boolean Tabs_switch(CTabItem src_tab_itm, CTabItem trg_tab_itm) { + Control temp_control = src_tab_itm.getControl(); + src_tab_itm.setControl(trg_tab_itm.getControl()); + trg_tab_itm.setControl(temp_control); + + String temp_str = src_tab_itm.getText(); + src_tab_itm.setText(trg_tab_itm.getText()); + trg_tab_itm.setText(temp_str); + + temp_str = src_tab_itm.getToolTipText(); + src_tab_itm.setToolTipText(trg_tab_itm.getToolTipText()); + trg_tab_itm.setToolTipText(temp_str); + + Gfui_tab_itm_data src_tab_data = Get_tab_data(src_tab_itm); + Gfui_tab_itm_data trg_tab_data = Get_tab_data(trg_tab_itm); + int src_tab_idx = src_tab_data.Idx(), trg_tab_idx = trg_tab_data.Idx(); + tab_folder.setSelection(trg_tab_itm); + GfoEvMgr_.PubVals(this, Gfui_tab_mgr.Evt_tab_switched, KeyVal_.new_("src", src_tab_data.Key()), KeyVal_.new_("trg", trg_tab_data.Key())); + return src_tab_idx < trg_tab_idx; + } + public void Tabs_select_by_itm(CTabItem itm) { + if (itm == null) return; // 0 tabs; return; + msg_tabs_select_by_itm_swt.Clear(); + msg_tabs_select_by_itm_swt.Add("v", itm); + cmd_async.Invk(GfsCtx._, 0, Invk_tabs_select_by_itm_swt, msg_tabs_select_by_itm_swt); + } private GfoMsg msg_tabs_select_by_itm_swt = GfoMsg_.new_cast_(Invk_tabs_select_by_itm_swt); + private void Tabs_select_by_idx_swt(int idx) { + tab_folder.setSelection(idx); + CTabItem itm = tab_folder.getItem(idx); + Pub_tab_selected(Get_tab_key(itm)); // NOTE: setSelection does not call event for SWT.Selection; must manually raise event; + } + private void Tabs_select_by_itm_swt(CTabItem itm) { + tab_folder.setSelection(itm); + Pub_tab_selected(Get_tab_key(itm)); // NOTE: setSelection does not call event for SWT.Selection; must manually raise event; + } + public CTabItem Tabs_select_after_closing_itm(Gfui_tab_itm_data tab_data) { + int next_idx = Gfui_tab_itm_data.Get_idx_after_closing(tab_data.Idx(), tab_folder.getItemCount()); + return next_idx == Gfui_tab_itm_data.Idx_null ? null : tab_folder.getItem(next_idx); + } + public void Pub_tab_selected(String key) { + GfoEvMgr_.PubObj(this, Gfui_tab_mgr.Evt_tab_selected, "key", key); + } + public void Pub_tab_closed(String key) { + GfoEvMgr_.PubObj(this, Gfui_tab_mgr.Evt_tab_closed, "key", key); + } + @Override public GxwCore_base Core() {return core;} GxwCore_base core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public String TextVal() {return "not implemented";} + @Override public void TextVal_set(String v) {} + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (String_.Eq(k, Invk_tabs_select_by_idx_swt)) Tabs_select_by_idx_swt(m.ReadInt("v")); + else if (String_.Eq(k, Invk_tabs_select_by_itm_swt)) Tabs_select_by_itm_swt((CTabItem)m.ReadObj("v", null)); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + @Override public void focusGained(FocusEvent arg0) {} + @Override public void focusLost(FocusEvent arg0) {} + private static String + Invk_tabs_select_by_idx_swt = "tabs_select_by_idx" + , Invk_tabs_select_by_itm_swt = "tabs_select_by_itm" + ; + public static Gfui_tab_itm_data Get_tab_data_by_obj(Object data) {return (Gfui_tab_itm_data)data;} + public static Gfui_tab_itm_data Get_tab_data(CTabItem itm) {return (Gfui_tab_itm_data)itm.getData();} + public static String Get_tab_key(CTabItem itm) {return ((Gfui_tab_itm_data)itm.getData()).Key();} +} +class Swt_tab_mgr_lnr_selection implements Listener { + public Swt_tab_mgr_lnr_selection(Swt_tab_mgr tab_folder) {this.tab_folder = tab_folder;} private Swt_tab_mgr tab_folder; + public void handleEvent(Event ev) { + tab_folder.Pub_tab_selected(Swt_tab_mgr.Get_tab_data_by_obj(ev.item.getData()).Key()); + } +} +class Swt_tab_mgr_lnr_close extends CTabFolder2Adapter { // handles close when tab x is clicked + public Swt_tab_mgr_lnr_close(Swt_tab_mgr tab_folder) {this.tab_folder = tab_folder;} private Swt_tab_mgr tab_folder; + @Override public void close(CTabFolderEvent ev) { + Gfui_tab_itm_data tab_data = Swt_tab_mgr.Get_tab_data_by_obj(ev.item.getData()); + tab_folder.Tabs_close_by_idx(tab_data.Idx()); + ev.doit = false; // mark ev handled, since Tabs_close_by_idx closes tab + } +} +class Swt_tab_mgr_lnr_drag_drop implements Listener { + private boolean dragging = false; + private boolean drag_stop = false; + private CTabItem drag_itm; + private final Swt_tab_mgr tab_mgr; private final CTabFolder tab_folder; private final Display display; + private Point prv_mouse; + private Point dead_zone = null; + public Swt_tab_mgr_lnr_drag_drop(Swt_tab_mgr tab_mgr, CTabFolder tab_folder) { + this.tab_mgr = tab_mgr; this.tab_folder = tab_folder; this.display = tab_folder.getDisplay(); + tab_folder.addListener(SWT.DragDetect, this); + tab_folder.addListener(SWT.MouseUp, this); + tab_folder.addListener(SWT.MouseMove, this); + tab_folder.addListener(SWT.MouseExit, this); + tab_folder.addListener(SWT.MouseEnter, this); + } + private void Drag_drop_bgn(CTabItem itm) { + dragging = true; + drag_stop = false; + drag_itm = itm; + dead_zone = null; + } + private void Drag_drop_end() { + tab_folder.setInsertMark(null, false); + dragging = false; + drag_stop = false; + drag_itm = null; + } + public void handleEvent(Event e) { + Point cur_mouse = e.type == SWT.DragDetect + ? tab_folder.toControl(display.getCursorLocation()) //see bug 43251 + : new Point(e.x, e.y) + ; + switch (e.type) { + case SWT.DragDetect: { + CTabItem itm = tab_folder.getItem(cur_mouse); + if (itm == null) return; + this.Drag_drop_bgn(itm); + break; + } + case SWT.MouseEnter: + if (drag_stop) { + dragging = e.button != 0; + drag_stop = false; + } + break; + case SWT.MouseExit: + if (dragging) + Drag_drop_end(); + break; + case SWT.MouseUp: { + if (!dragging) return; + Drag_drop_end(); + break; + } + case SWT.MouseMove: { + if (!dragging) return; + CTabItem curr_itm = tab_folder.getItem(cur_mouse); + if (curr_itm == null) { + tab_folder.setInsertMark(null, false); + return; + } + if (curr_itm == drag_itm) return; // curr_itm is same as drag_itm; ignore + int cur_mouse_x = cur_mouse.x; + int prv_mouse_x = prv_mouse == null ? 0 : prv_mouse.x; + prv_mouse = cur_mouse; // set prv_mouse now b/c of early return below; note that cur_mouse_x and prv_mouse_x are cached above + if ( dead_zone != null // dead_zone exists + && Int_.Between(cur_mouse_x, dead_zone.x, dead_zone.y)) { // mouse is in dead_zone + int drag_idx = Swt_tab_mgr.Get_tab_data(drag_itm).Idx(); + int curr_idx = Swt_tab_mgr.Get_tab_data(curr_itm).Idx(); + if (drag_idx > curr_idx && cur_mouse_x < prv_mouse_x) {} // drag_itm is right of curr_itm, but mouse is moving left (direction reversed); cancel + else if (drag_idx < curr_idx && cur_mouse_x > prv_mouse_x) {} // drag_itm is left of curr_itm, but mouse is moving right (direction reversed); cancel + else + return; // in dead zone, and still moving in original direction; return early + } + boolean fwd = tab_mgr.Tabs_switch(drag_itm, curr_itm); + drag_itm = curr_itm; + Rectangle drag_rect = drag_itm.getBounds(); + dead_zone = Calc_dead_zone(fwd, cur_mouse_x, drag_rect.x, drag_rect.width); + break; + } + } + } + public static Point Calc_dead_zone(boolean fwd, int mouse_x, int drag_l, int drag_w) { + if (fwd) { // drag_itm was moving fwd (moving right) + if (mouse_x < drag_l) return new Point(mouse_x, drag_l); // mouse_x < drag_l; create dead_zone until mouse_x reaches drag_l; occurs when moving drag is small_title and trg_itm is large_title + } + else { // drag_itm was moving bwd (moving left) + int drag_r = drag_l + drag_w; + if (mouse_x > drag_r) return new Point(drag_r, mouse_x); // mouse_x > drag_r; create dead_zone until mouse_x reaches drag_r + } + return null; + } +} +//#} \ No newline at end of file diff --git a/150_gfui/xtn/gplx/gfui/Swt_text.java b/150_gfui/xtn/gplx/gfui/Swt_text.java new file mode 100644 index 000000000..f734e1e55 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_text.java @@ -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 . +*/ +package gplx.gfui; import gplx.*; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +class Swt_text implements GxwTextFld, Swt_control { + private Text text_box; + public Swt_text(Swt_control owner_control, KeyValHash ctorArgs) { + int text_box_args = ctorArgs.Has(GfuiTextBox_.Ctor_Memo) + ? SWT.MULTI | SWT.WRAP | SWT.V_SCROLL + : SWT.NONE + ; + text_box = new Text(owner_control.Under_composite(), text_box_args); + core = new Swt_core_cmds(text_box); + text_box.addKeyListener(new Swt_lnr_key(this)); + text_box.addMouseListener(new Swt_lnr_mouse(this)); + } + @Override public Control Under_control() {return text_box;} + @Override public Composite Under_composite() {return null;} + @Override public Control Under_menu_control() {return text_box;} + @Override public int SelBgn() {return text_box.getCaretPosition();} @Override public void SelBgn_set(int v) {text_box.setSelection(v);} + @Override public int SelLen() {return text_box.getSelectionCount();} @Override public void SelLen_set(int v) {text_box.setSelection(this.SelBgn(), this.SelBgn() + v);} + @Override public String TextVal() {return text_box.getText();} @Override public void TextVal_set(String v) {text_box.setText(v);} + @Override public GxwCore_base Core() {return core;} GxwCore_base core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return null;} + public void Margins_set(int left, int top, int right, int bot) {} + @Override public boolean Border_on() {return false;} @Override public void Border_on_(boolean v) {} // SWT_TODO:borderWidth doesn't seem mutable + @Override public void CreateControlIfNeeded() {} + @Override public boolean OverrideTabKey() {return false;} @Override public void OverrideTabKey_(boolean v) {} +} diff --git a/150_gfui/xtn/gplx/gfui/Swt_text_w_border.java b/150_gfui/xtn/gplx/gfui/Swt_text_w_border.java new file mode 100644 index 000000000..8c23b82e7 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_text_w_border.java @@ -0,0 +1,89 @@ +/* +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.gfui; import gplx.*; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +public class Swt_text_w_border implements GxwTextFld, Swt_control { + private Composite text_host; + private Composite text_margin; + private Text text_elem; + public Swt_text_w_border(Swt_control owner_control, Color color, KeyValHash ctorArgs) { + Composite owner = owner_control.Under_composite(); + int text_elem_style = ctorArgs.Has(GfuiTextBox_.Ctor_Memo) ? SWT.MULTI | SWT.WRAP | SWT.V_SCROLL : SWT.FLAT; + New_box_text_w_border(owner.getDisplay(), owner.getShell(), text_elem_style, color); + core = new Swt_core_cmds_frames(text_host, new Swt_core_cmds_frames_itm[] + { new Swt_core_cmds_frames_itm_manual(text_margin, 1, 1, -2, -2) + , new Swt_core_cmds_frames_itm_center_v(text_elem, this) + }); + text_elem.addKeyListener(new Swt_lnr_key(this)); + text_elem.addMouseListener(new Swt_lnr_mouse(this)); + } + @Override public Control Under_control() {return text_host;} + @Override public Composite Under_composite() {return null;} + @Override public Control Under_menu_control() {return text_elem;} + public Text Under_text() {return text_elem;} + @Override public int SelBgn() {return text_elem.getCaretPosition();} @Override public void SelBgn_set(int v) {text_elem.setSelection(v);} + @Override public int SelLen() {return text_elem.getSelectionCount();} @Override public void SelLen_set(int v) {text_elem.setSelection(this.SelBgn(), this.SelBgn() + v);} + @Override public String TextVal() {return text_elem.getText();} @Override public void TextVal_set(String v) {text_elem.setText(v);} + @Override public GxwCore_base Core() {return core;} Swt_core_cmds_frames core; + @Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host; + @Override public void EnableDoubleBuffering() {} + @Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + return null; + } + public int Margins_l() {return margins_l;} int margins_l; + public int Margins_r() {return margins_r;} int margins_r; + public int Margins_t() {return margins_t;} int margins_t; + public int Margins_b() {return margins_b;} int margins_b; + public void Margins_set(int left, int top, int right, int bot) { + this.margins_l = left; this.margins_t = top; this.margins_r = right; this.margins_b = bot; + } + @Override public boolean Border_on() {return false;} @Override public void Border_on_(boolean v) {} // SWT_TODO:borderWidth doesn't seem mutable + @Override public void CreateControlIfNeeded() {} + @Override public boolean OverrideTabKey() {return false;} @Override public void OverrideTabKey_(boolean v) {} + void New_box_text_w_border(Display display, Shell shell, int style, Color color) { + text_host = new Composite(shell, SWT.FLAT); + text_margin = new Composite(text_host, SWT.FLAT); + text_elem = new Text(text_margin, style); + text_elem .addTraverseListener(Swt_lnr_traverse_ignore_ctrl._); // do not allow ctrl+tab to change focus when pressed in text box; allows ctrl+tab to be used by other bindings; DATE:2014-04-30 + text_host.setSize(20, 20); + text_host.setBackground(color); + text_margin.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); + text_margin.setSize(20, 20); + text_elem.setSize(20 - 2, 20 - 2); + text_elem.setLocation(1, 1); + } +} +class Swt_lnr_traverse_ignore_ctrl implements TraverseListener { + public void keyTraversed(TraverseEvent e) { + if (Swt_lnr_key.Has_ctrl(e.stateMask)) e.doit = false; + } + public static final Swt_lnr_traverse_ignore_ctrl _ = new Swt_lnr_traverse_ignore_ctrl(); +} \ No newline at end of file diff --git a/150_gfui/xtn/gplx/gfui/Swt_win.java b/150_gfui/xtn/gplx/gfui/Swt_win.java new file mode 100644 index 000000000..719a76875 --- /dev/null +++ b/150_gfui/xtn/gplx/gfui/Swt_win.java @@ -0,0 +1,114 @@ +/* +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.gfui; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import gplx.GfoInvkAbleCmd; +import gplx.GfoMsg; +import gplx.GfsCtx; +import gplx.Io_url_; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +class Swt_win implements GxwWin, Swt_control { + public Display UnderDisplay() {return display;} private Display display; + public Shell UnderShell() {return shell;} private Shell shell; + @Override public Control Under_control() {return shell;} + @Override public Composite Under_composite() {return shell;} + @Override public Control Under_menu_control() {return shell;} + public Swt_win(Shell owner) {ctor(new Shell(owner, SWT.RESIZE | SWT.DIALOG_TRIM), owner.getDisplay());} + public Swt_win(Display display) {ctor(new Shell(display), display); } + Swt_lnr_show showLnr; // use ptr to dispose later + void ctor(Shell shell, Display display) { + this.shell = shell; + this.display = display; + ctrlMgr = new Swt_core_cmds(shell); + showLnr = new Swt_lnr_show(this); + resizeLnr = new Swt_lnr_resize(this); + shell.addListener(SWT.Show, showLnr); + shell.addListener(SWT.Resize, resizeLnr); + } + Swt_lnr_resize resizeLnr; + public void ShowWin() {shell.setVisible(true);} + public void HideWin() {shell.setVisible(false);} + public boolean Maximized() {return shell.getMaximized();} public void Maximized_(boolean v) {shell.setMaximized(v);} + public boolean Minimized() {return shell.getMinimized();} public void Minimized_(boolean v) {shell.setMinimized(v);} + public void CloseWin() {shell.close();} + public boolean Pin() {return pin;} + public void Pin_set(boolean val) { + // shell.setAlwaysOnTop(val); + pin = val; + } boolean pin = false; + public IconAdp IconWin() {return icon;} IconAdp icon; + public void IconWin_set(IconAdp i) { + if (i == null || i.Url() == Io_url_.Null) return; + icon = i; + Image image = null; + try { + image = new Image(display, new FileInputStream(i.Url().Xto_api())); + } catch (FileNotFoundException e1) {e1.printStackTrace();} + shell.setImage(image); + } + public void OpenedCmd_set(GfoInvkAbleCmd v) {whenLoadedCmd = v;} GfoInvkAbleCmd whenLoadedCmd = GfoInvkAbleCmd.Null; + public void Opened() {whenLoadedCmd.Invk();} + public GxwCore_base Core() {return ctrlMgr;} GxwCore_base ctrlMgr; + public GxwCbkHost Host() {return host;} public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host = GxwCbkHost_.Null; + public String TextVal() { + return shell.getText();} + public void TextVal_set(String v) { + shell.setText(v); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {return this;} + public void SendKeyDown(IptKey key) {} + public void SendMouseMove(int x, int y) {} + public void SendMouseDown(IptMouseBtn btn) {} + //public void windowActivated(WindowEvent e) {} + //public void windowClosed(WindowEvent e) {} + //public void windowClosing(WindowEvent e) {host.DisposeCbk();} + //public void windowDeactivated(WindowEvent e) {} + //public void windowDeiconified(WindowEvent e) {host.SizeChangedCbk();} + //public void windowIconified(WindowEvent e) {host.SizeChangedCbk();} + //public void windowOpened(WindowEvent e) {whenLoadedCmd.Invk();} + //@Override public void processKeyEvent(KeyEvent e) {if (GxwCbkHost_.ExecKeyEvent(host, e)) super.processKeyEvent(e);} + //@Override public void processMouseEvent(MouseEvent e) {if (GxwCbkHost_.ExecMouseEvent(host, e)) super.processMouseEvent(e);} + //@Override public void processMouseWheelEvent(MouseWheelEvent e) {if (GxwCbkHost_.ExecMouseWheel(host, e)) super.processMouseWheelEvent(e);} + //@Override public void processMouseMotionEvent(MouseEvent e) {if (host.MouseMoveCbk(IptEvtDataMouse.new_(IptMouseBtn_.None, IptMouseWheel_.None, e.getX(), e.getY()))) super.processMouseMotionEvent(e);} + //@Override public void paint(Graphics g) { + // if (host.PaintCbk(PaintArgs.new_(GfxAdpBase.new_((Graphics2D)g), RectAdp_.Zero))) // ClipRect not used by any clients; implement when necessary + // super.paint(g); + //} + public void EnableDoubleBuffering() {} + public void TaskbarVisible_set(boolean val) {} public void TaskbarParkingWindowFix(GxwElem form) {} + void ctor_GxwForm() { + // this.setLayout(null); // use gfui layout + // this.ctrlMgr.BackColor_set(ColorAdp_.White); // default form backColor to white + // this.setUndecorated(true); // remove icon, titleBar, minimize, maximize, close, border + // this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // JAVA: cannot cancel alt+f4; set Close to noop, and manually control closing by calling this.CloseForm + // enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); + // this.addWindowListener(this); + // GxwBoxListener lnr = new GxwBoxListener(this); + // this.addComponentListener(lnr); + // this.addFocusListener(lnr); + } +} diff --git a/400_xowa/.classpath b/400_xowa/.classpath new file mode 100644 index 000000000..1a4b88bdd --- /dev/null +++ b/400_xowa/.classpath @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/400_xowa/.project b/400_xowa/.project new file mode 100644 index 000000000..cd7955f03 --- /dev/null +++ b/400_xowa/.project @@ -0,0 +1,17 @@ + + + 400_xowa + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/400_xowa/lib/luaj_xowa.jar b/400_xowa/lib/luaj_xowa.jar new file mode 100644 index 000000000..414a1cd71 Binary files /dev/null and b/400_xowa/lib/luaj_xowa.jar differ diff --git a/400_xowa/src/gplx/cache/Gfo_cache_mgr_base.java b/400_xowa/src/gplx/cache/Gfo_cache_mgr_base.java new file mode 100644 index 000000000..b6022302d --- /dev/null +++ b/400_xowa/src/gplx/cache/Gfo_cache_mgr_base.java @@ -0,0 +1,48 @@ +/* +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.cache; import gplx.*; +public class Gfo_cache_mgr_base { + private OrderedHash hash = OrderedHash_.new_bry_(); + public int Compress_max() {return compress_max;} public void Compress_max_(int v) {compress_max = v;} private int compress_max = 16; + public int Compress_to() {return compress_to;} public void Compress_to_(int v) {compress_to = v;} private int compress_to = 8; + protected Object Base_get_or_null(byte[] key) { + Object rv_obj = hash.Fetch(key); + return rv_obj == null ? null : ((Gfo_cache_itm)rv_obj).Val(); + } + protected void Base_add(byte[] key, Object val) { + if (hash.Count() >= compress_max) Compress(); + Gfo_cache_itm itm = new Gfo_cache_itm(key, val); + hash.Add(key, itm); + } + protected void Base_del(byte[] key) { + hash.Del(key); + } + public void Compress() { + hash.SortBy(Gfo_cache_itm_comparer.Touched_asc); + int del_len = hash.Count() - compress_to; + ListAdp del_list = ListAdp_.new_(); + for (int i = 0; i < del_len; i++) { + Gfo_cache_itm itm = (Gfo_cache_itm)hash.FetchAt(i); + del_list.Add(itm); + } + for (int i = 0; i < del_len; i++) { + Gfo_cache_itm itm = (Gfo_cache_itm)del_list.FetchAt(i); + hash.Del(itm.Key()); + } + } +} diff --git a/400_xowa/src/gplx/cache/Gfo_cache_mgr_bry.java b/400_xowa/src/gplx/cache/Gfo_cache_mgr_bry.java new file mode 100644 index 000000000..70984323c --- /dev/null +++ b/400_xowa/src/gplx/cache/Gfo_cache_mgr_bry.java @@ -0,0 +1,38 @@ +/* +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.cache; import gplx.*; +public class Gfo_cache_mgr_bry extends Gfo_cache_mgr_base { + public Object Get_or_null(byte[] key) {return Base_get_or_null(key);} + public void Add(byte[] key, Object val) {Base_add(key, val);} + public void Del(byte[] key) {Base_del(key);} +} +class Gfo_cache_itm { + public Gfo_cache_itm(Object key, Object val) {this.key = key; this.val = val; this.Touched_update();} + public Object Key() {return key;} private Object key; + public Object Val() {return val;} private Object val; + public long Touched() {return touched;} private long touched; + public Gfo_cache_itm Touched_update() {touched = Env_.TickCount(); return this;} +} +class Gfo_cache_itm_comparer implements gplx.lists.ComparerAble { + public int compare(Object lhsObj, Object rhsObj) { + Gfo_cache_itm lhs = (Gfo_cache_itm)lhsObj; + Gfo_cache_itm rhs = (Gfo_cache_itm)rhsObj; + return Long_.Compare(lhs.Touched(), rhs.Touched()); + } + public static final Gfo_cache_itm_comparer Touched_asc = new Gfo_cache_itm_comparer(); +} diff --git a/400_xowa/src/gplx/core/bytes/Bry_bldr.java b/400_xowa/src/gplx/core/bytes/Bry_bldr.java new file mode 100644 index 000000000..ec916e1c7 --- /dev/null +++ b/400_xowa/src/gplx/core/bytes/Bry_bldr.java @@ -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 . +*/ +package gplx.core.bytes; import gplx.*; import gplx.core.*; +public class Bry_bldr { + public byte[] Val() {return val;} private byte[] val; + public Bry_bldr New_256() {return New(256);} + public Bry_bldr New(int len) {val = new byte[len]; return this;} + public Bry_bldr Set_rng_ws(byte v) {return Set_many(v, Byte_ascii.Space, Byte_ascii.Tab, Byte_ascii.NewLine, Byte_ascii.CarriageReturn);} + public Bry_bldr Set_rng_xml_identifier(byte v) {return Set_rng_alpha_lc(v).Set_rng_alpha_uc(v).Set_rng_num(v).Set_many(v, Byte_ascii.Underline, Byte_ascii.Dash);} + public Bry_bldr Set_rng_alpha(byte v) {return Set_rng_alpha_lc(v).Set_rng_alpha_uc(v);} + public Bry_bldr Set_rng_alpha_lc(byte v) {return Set_rng(v, Byte_ascii.Ltr_a, Byte_ascii.Ltr_z);} + public Bry_bldr Set_rng_alpha_uc(byte v) {return Set_rng(v, Byte_ascii.Ltr_A, Byte_ascii.Ltr_Z);} + public Bry_bldr Set_rng_num(byte v) {return Set_rng(v, Byte_ascii.Num_0, Byte_ascii.Num_9);} + public Bry_bldr Set_rng(byte v, int bgn, int end) { + for (int i = bgn; i <= end; i++) + val[i] = v; + return this; + } + public Bry_bldr Set_many(byte v, int... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + val[ary[i]] = v; + return this; + } +} diff --git a/400_xowa/src/gplx/core/enums/Gfo_enum_grp.java b/400_xowa/src/gplx/core/enums/Gfo_enum_grp.java new file mode 100644 index 000000000..5215ee5fa --- /dev/null +++ b/400_xowa/src/gplx/core/enums/Gfo_enum_grp.java @@ -0,0 +1,54 @@ +/* +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.core.enums; import gplx.*; import gplx.core.*; +class Gfo_enum_grp { +// private OrderedHash itms = OrderedHash_.new_(); + public Gfo_enum_grp(UuidAdp uid, String key, int id, String name, int sort, String xtn) { + this.uid = uid; this.key = key; this.id = id; this.name = name; this.sort = sort; this.xtn = xtn; + } + public UuidAdp Uid() {return uid;} private UuidAdp uid; + public String Key() {return key;} private String key; + public int Id() {return id;} private int id; + public String Name() {return name;} private String name; + public int Sort() {return sort;} private int sort; + public String Xtn() {return xtn;} private String xtn; +} +class Gfo_enum_itm { + public Gfo_enum_itm(UuidAdp uid, String key, int id, String name, int sort, String xtn) { + this.uid = uid; this.key = key; this.id = id; this.name = name; this.sort = sort; this.xtn = xtn; + } + public UuidAdp Uid() {return uid;} private UuidAdp uid; + public String Key() {return key;} private String key; + public int Id() {return id;} private int id; + public String Name() {return name;} private String name; + public int Sort() {return sort;} private int sort; + public String Xtn() {return xtn;} private String xtn; +} +/* +enum_grps +grp_guid,grp_key,grp_int,grp_name,grp_sort,grp_xtn +0-1-2-3,xowa.wiki,0,wiki,, + +enum_itms +grp_int,itm_guid,itm_key,itm_int,itm_name,itm_sort,itm_xtn +1,0-1-2-3,0,en.wikipedia.org,0,enwiki,0,'' + +class Gfo_enum_mgr { +// public Gui +} +*/ diff --git a/400_xowa/src/gplx/core/ints/Int_ary_bldr.java b/400_xowa/src/gplx/core/ints/Int_ary_bldr.java new file mode 100644 index 000000000..7e39ef74c --- /dev/null +++ b/400_xowa/src/gplx/core/ints/Int_ary_bldr.java @@ -0,0 +1,23 @@ +/* +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.core.ints; import gplx.*; import gplx.core.*; +public class Int_ary_bldr { + public Int_ary_bldr(int len) {ary = new int[len];} + public Int_ary_bldr Set(int idx, int val) {ary[idx] = val; return this;} + public int[] Xto_int_ary() {return ary;} private int[] ary; +} diff --git a/400_xowa/src/gplx/fsdb/Binary_search_.java b/400_xowa/src/gplx/fsdb/Binary_search_.java new file mode 100644 index 000000000..4234c1ed2 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Binary_search_.java @@ -0,0 +1,52 @@ +/* +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.fsdb; import gplx.*; +public class Binary_search_ { + public static int Search(CompareAble[] ary, int ary_len, CompareAble val) { + if (ary_len == 1) return 0; + int interval = ary_len / 2; + int pos = interval - ListAdp_.Base1; + int pos_last = ary_len - 1; + int pos_prv = -1; + int loop_count = 0; + while (loop_count++ < 32) { // 32 bit integer + CompareAble lo = ary[pos]; + CompareAble hi = pos + 1 == ary_len ? null : ary[pos + 1]; + int adj = 0; + int lo_comp = val.compareTo(lo); + if (lo_comp == CompareAble_.Less) // val is < lo; search slots below + adj = -1; + else { + if (hi == null) return pos; // hi is null when at last slot in ary + int hi_comp = val.compareTo(hi); + if (hi_comp == CompareAble_.More) // val is > hi; search slots above + adj = 1; + else + return pos; // val is > lo and < hi; return slot + } + interval /= 2; + if (interval == 0) interval = 1; // do not allow 0 intervals; pos must always change; + pos += (interval * adj); + if (pos == 0 && pos_prv == 0) break; // NOTE: this will only happen when 1st member is not "" + if (pos < 0) pos = 0; + else if (pos > pos_last) pos = pos_last; + pos_prv = pos; + } + return Int_.MinValue; // should only occur if (a) ary's 0th slot is not ""; or (b) some unknown error + } +} diff --git a/400_xowa/src/gplx/fsdb/Binary_search__tst.java b/400_xowa/src/gplx/fsdb/Binary_search__tst.java new file mode 100644 index 000000000..e565cc570 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Binary_search__tst.java @@ -0,0 +1,47 @@ +/* +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.fsdb; import gplx.*; +import org.junit.*; +public class Binary_search__tst { + private Binary_search__fxt fxt = new Binary_search__fxt(); + @Test public void Basic() { + fxt.Init_ary("", "e", "j", "o", "t", "y"); + fxt.Test_binary_search("a", 0); + fxt.Test_binary_search("f", 1); + fxt.Test_binary_search("k", 2); + fxt.Test_binary_search("p", 3); + fxt.Test_binary_search("u", 4); + fxt.Test_binary_search("z", 5); + } + @Test public void One() { + fxt.Init_ary(""); + fxt.Test_binary_search("a", 0); + } +} +class Binary_search__fxt { + public void Init_ary(String... v) { + int ary_len = v.length; + ary = new String_obj_val[ary_len]; + for (int i = 0; i < ary_len; i++) + ary[i] = String_obj_val.new_(v[i]); + } private String_obj_val[] ary; + public void Test_binary_search(String val, int expd) { + int actl = Binary_search_.Search(ary, ary.length, String_obj_val.new_(val)); + Tfds.Eq(expd, actl, val); + } +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_bin_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_bin_tbl.java new file mode 100644 index 000000000..d4b6a6ffe --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_bin_tbl.java @@ -0,0 +1,131 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; import gplx.ios.*; +public class Fsdb_bin_tbl { + public static void Create_table(Db_provider p) {Sqlite_engine_.Tbl_create(p, Tbl_name, Tbl_sql);} + public static Db_stmt Insert_stmt(Db_provider p) {return Db_stmt_.new_insert_(p, Tbl_name, Fld_bin_owner_id, Fld_bin_owner_tid, Fld_bin_part_id, Fld_bin_data_url, Fld_bin_data);} + public static void Insert_rdr(Db_provider p, int id, byte tid, long bin_len, Io_stream_rdr bin_rdr) { + Db_stmt stmt = Insert_stmt(p); + try {Insert_rdr(stmt, id, tid, bin_len, bin_rdr);} + finally {stmt.Rls();} + } + public static long Insert_rdr(Db_stmt stmt, int id, byte tid, long bin_len, Io_stream_rdr bin_rdr) { + long rv = bin_len; + stmt.Clear() + .Val_int_(id) + .Val_byte_(tid) + .Val_int_(Null_part_id) + .Val_str_(Null_data_url) + ; + if (Sqlite_engine_.Supports_read_binary_stream) + stmt.Val_rdr_(bin_rdr, bin_len); + else { + byte[] bin_ary = Io_stream_rdr_.Load_all_as_bry(Bry_bfr.new_(), bin_rdr); + stmt.Val_bry_(bin_ary); + rv = bin_ary.length; + } + stmt.Exec_insert(); + return rv; + } + public static void Delete(Db_provider p, int id) { + Db_stmt stmt = Delete_stmt(p); + try {Delete(stmt, id);} + finally {stmt.Rls();} + } + private static Db_stmt Delete_stmt(Db_provider p) {return Db_stmt_.new_delete_(p, Tbl_name, Fld_bin_owner_id);} + private static void Delete(Db_stmt stmt, int id) { + stmt.Clear() + .Val_int_(id) + .Exec_delete(); + } + public static Io_stream_rdr Select_as_rdr(Db_provider p, int owner) { + Db_qry qry = Db_qry_.select_().From_(Tbl_name).Cols_(Fld_bin_data).Where_(Db_crt_.eq_(Fld_bin_owner_id, owner)); + DataRdr rdr = DataRdr_.Null; + try { + rdr = p.Exec_qry_as_rdr(qry); + if (rdr.MoveNextPeer()) { + if (Sqlite_engine_.Supports_read_binary_stream) + return rdr.ReadRdr(Fld_bin_data); + else + return gplx.ios.Io_stream_rdr_.mem_(Read_bin_data(rdr)); + } + else + return gplx.ios.Io_stream_rdr_.Null; + } + finally {rdr.Rls();} + } + public static boolean Select_to_url(Db_provider p, int owner, Io_url url, byte[] bin_bfr, int bin_flush_when) { + Db_qry qry = Db_qry_.select_().From_(Tbl_name).Cols_(Fld_bin_data).Where_(Db_crt_.eq_(Fld_bin_owner_id, owner)); + DataRdr rdr = DataRdr_.Null; + try { + rdr = p.Exec_qry_as_rdr(qry); + if (rdr.MoveNextPeer()) { + if (Sqlite_engine_.Supports_read_binary_stream) + return Select_to_fsys__stream(rdr, url, bin_bfr, bin_flush_when); + else { + byte[] bry = Read_bin_data(rdr); + Io_mgr._.SaveFilBry(url, bry); + return true; + } + } + else + return false; + } + finally {rdr.Rls();} + } + public static boolean Select_to_fsys__stream(DataRdr rdr, Io_url url, byte[] bin_bfr, int bin_flush_when) { + Io_stream_rdr db_stream = Io_stream_rdr_.Null; + IoStream fs_stream = IoStream_.Null; + try { + db_stream = rdr.ReadRdr(Fld_bin_data); if (db_stream == Io_stream_rdr_.Null) return false; + fs_stream = Io_mgr._.OpenStreamWrite(url); + int pos = 0, flush_nxt = bin_flush_when; + while (true) { + int read = db_stream.Read(bin_bfr, pos, bin_bfr.length); if (read == Io_stream_rdr_.Read_done) break; + fs_stream.Write(bin_bfr, 0, read); + if (pos > flush_nxt) { + fs_stream.Flush(); + flush_nxt += bin_flush_when; + } + } + fs_stream.Flush(); + return true; + } finally { + db_stream.Rls(); + fs_stream.Rls(); + } + } + private static byte[] Read_bin_data(DataRdr rdr) { + byte[] rv = rdr.ReadBry(Fld_bin_data); + return rv == null ? Bry_.Empty : rv; // NOTE: bug in v0.10.1 where .ogg would save as null; return Bry_.Empty instead, else java.io.ByteArrayInputStream would fail on null + } + public static final String Tbl_name = "fsdb_bin", Fld_bin_owner_id = "bin_owner_id", Fld_bin_owner_tid = "bin_owner_tid", Fld_bin_part_id = "bin_part_id", Fld_bin_data_url = "bin_data_url", Fld_bin_data = "bin_data"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_bin" + , "( bin_owner_id integer NOT NULL PRIMARY KEY" + , ", bin_owner_tid byte NOT NULL" + , ", bin_part_id integer NOT NULL" + , ", bin_data_url varchar(255) NOT NULL" + , ", bin_data mediumblob NOT NULL" + , ");" + ); + public static final byte Owner_tid_fil = 1, Owner_tid_thm = 2; + public static final int Null_db_bin_id = -1, Null_size = -1, Null_part_id = -1; + public static final String Null_data_url = ""; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_cfg_grp.java b/400_xowa/src/gplx/fsdb/Fsdb_cfg_grp.java new file mode 100644 index 000000000..a1f79fa6b --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_cfg_grp.java @@ -0,0 +1,63 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_cfg_grp { + private OrderedHash itms = OrderedHash_.new_(); + public Fsdb_cfg_grp(String grp) {this.grp = grp;} + public String Grp() {return grp;} private String grp; + public void Insert(String key, String val) { + if (itms.Has(key)) throw Err_.new_fmt_("cfg_grp.Insert failed; key={0}", key); + Fsdb_cfg_itm itm = new Fsdb_cfg_itm(grp, key, val); + itms.Add(key, itm); + } + public void Update(String key, String val) { + Fsdb_cfg_itm itm = (Fsdb_cfg_itm)itms.Fetch(key); + if (itm == null) throw Err_.new_fmt_("cfg_grp.Update failed; key={0}", key); + itm.Val_(val); + } + public void Upsert(String key, String val) { + Fsdb_cfg_itm itm = (Fsdb_cfg_itm)itms.Fetch(key); + if (itm == null) { + itm = new Fsdb_cfg_itm(grp, key, val); + itms.Add(key, itm); + } + else + itm.Val_(val); + } + public boolean Get_yn_or_y(String key) {return Get_yn_or(key, Bool_.Y);} + public boolean Get_yn_or_n(String key) {return Get_yn_or(key, Bool_.N);} + public boolean Get_yn_or(String key, boolean or) { + String rv = Get_str_or(key, null); + return rv == null ? or : Yn.parse_(rv); + } + public int Get_int_or(String key, int or) { + String rv = Get_str_or(key, null); + return rv == null ? or : Int_.parse_(rv); + } + public String Get_str_or(String key, String or) { + Fsdb_cfg_itm itm = (Fsdb_cfg_itm)itms.Fetch(key); + return itm == null ? or : itm.Val(); + } + public static final Fsdb_cfg_grp Null = new Fsdb_cfg_grp(); Fsdb_cfg_grp() {} +} +class Fsdb_cfg_itm { + public Fsdb_cfg_itm(String grp, String key, String val) {this.grp = grp; this.key = key; this.val = val;} + public String Grp() {return grp;} private String grp; + public String Key() {return key;} private String key; + public String Val() {return val;} public Fsdb_cfg_itm Val_(String v) {val = v; return this;} private String val; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_cfg_mgr.java b/400_xowa/src/gplx/fsdb/Fsdb_cfg_mgr.java new file mode 100644 index 000000000..c2a8b4c44 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_cfg_mgr.java @@ -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 . +*/ +package gplx.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_cfg_mgr { + private HashAdp grps = HashAdp_.new_(); + private Fsdb_cfg_tbl cfg_tbl; + public int Next_id() {return next_id++;} private int next_id = 1; + public boolean Schema_thm_page() {return schema_thm_page;} private boolean schema_thm_page = true; + public boolean Patch_next_id() {return patch_next_id;} private boolean patch_next_id = true; + public void Patch_next_id_exec(int last_id) { + if (last_id >= next_id) + next_id = last_id + 1; + cfg_tbl.Insert(Grp_core, Key_patch_next_id, "y"); + } + public void Txn_save() { + this.Update_next_id(); + } + public void Rls() {cfg_tbl.Rls();} + private void Update_next_id() {cfg_tbl.Update("core", "next_id", Int_.XtoStr(next_id));} + public Fsdb_cfg_mgr Update(String grp, String key, String new_val) { + String cur_val = cfg_tbl.Select_as_str_or(grp, key, null); + if (cur_val == null) + cfg_tbl.Insert(grp, key, new_val); + else + cfg_tbl.Update(grp, key, new_val); + return this; + } + public Fsdb_cfg_grp Grps_get_or_load(String grp_key) { + Fsdb_cfg_grp grp = (Fsdb_cfg_grp)grps.Fetch(grp_key); + if (grp == null) { + grp = cfg_tbl.Select_as_grp(grp_key); + grps.Add(grp_key, grp); + } + return grp; + } + public Fsdb_cfg_grp Grps_get_or_add(String grp_key) { // TEST: + Fsdb_cfg_grp grp = (Fsdb_cfg_grp)grps.Fetch(grp_key); + if (grp == null) { + grp = new Fsdb_cfg_grp(grp_key); + grps.Add(grp_key, grp); + } + return grp; + } + public static Fsdb_cfg_mgr load_(Fsdb_db_abc_mgr abc_mgr, Db_provider p) {return new Fsdb_cfg_mgr().Init_by_load(p);} + public static Fsdb_cfg_mgr make_(Fsdb_db_abc_mgr abc_mgr, Db_provider p) {return new Fsdb_cfg_mgr().Init_by_make(p);} + private Fsdb_cfg_mgr Init_by_load(Db_provider p) { + this.cfg_tbl = new Fsdb_cfg_tbl_sql().Ctor(p, false); + Fsdb_cfg_grp core_grp = Grps_get_or_load(Grp_core); + this.next_id = core_grp.Get_int_or(Key_next_id, -1); if (next_id == -1) throw Err_.new_("next_id not found in fsdb_cfg"); + this.schema_thm_page = core_grp.Get_yn_or_n(Key_schema_thm_page); + this.patch_next_id = core_grp.Get_yn_or_n(Key_patch_next_id); + return this; + } + private Fsdb_cfg_mgr Init_by_make(Db_provider p) { + this.cfg_tbl = new Fsdb_cfg_tbl_sql().Ctor(p, true); + this.cfg_tbl.Insert(Grp_core, Key_next_id , "1"); // start next_id at 1 + this.cfg_tbl.Insert(Grp_core, Key_schema_thm_page , "y"); // new dbs automatically have page and time in fsdb_xtn_tm + this.cfg_tbl.Insert(Grp_core, Key_patch_next_id , "y"); // new dbs automatically have correct next_id + return this; + } + public static final String Grp_core = "core"; + public static final String Key_next_id = "next_id", Key_schema_thm_page = "schema.thm.page", Key_patch_next_id = "patch.next_id"; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_cfg_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_cfg_tbl.java new file mode 100644 index 000000000..cd35e3ba5 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_cfg_tbl.java @@ -0,0 +1,143 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public interface Fsdb_cfg_tbl extends RlsAble { + Fsdb_cfg_tbl Ctor(Db_provider provider, boolean created); + void Insert(String grp, String key, String val); + void Update(String grp, String key, String val); + int Select_as_int_or(String grp, String key, int or); + int Select_as_int_or_fail(String grp, String key); + String Select_as_str_or(String grp, String key, String or); + Fsdb_cfg_grp Select_as_grp(String grp); +} +abstract class Fsdb_cfg_tbl_base { + public abstract int Select_as_int_or(String grp, String key, int or); + public int Select_as_int_or_fail(String grp, String key) { + int rv = Select_as_int_or(grp, key, Int_.MinValue); + if (rv == Int_.MinValue) throw Err_.new_fmt_("fsdb_cfg did not have itm: grp={0} key={1}", grp, key); + return rv; + } +} +class Fsdb_cfg_tbl_mem extends Fsdb_cfg_tbl_base implements Fsdb_cfg_tbl { + private HashAdp grps = HashAdp_.new_(); + public Fsdb_cfg_tbl Ctor(Db_provider provider, boolean created) {return this;} + public void Insert(String grp, String key, String val) { + Fsdb_cfg_grp grp_itm = Grps_get_or_make(grp); + grp_itm.Insert(key, val); + } + public void Update(String grp, String key, String val) { + Fsdb_cfg_grp grp_itm = Grps_get_or_make(grp); + grp_itm.Update(key, val); + } + @Override public int Select_as_int_or(String grp, String key, int or) { + Fsdb_cfg_grp grp_itm = Grps_get_or_null(grp); + return grp_itm == null ? or : grp_itm.Get_int_or(grp, or); + } + public String Select_as_str_or(String grp, String key, String or) { + Fsdb_cfg_grp grp_itm = Grps_get_or_null(grp); + return grp_itm == null ? or : grp_itm.Get_str_or(grp, or); + } + public Fsdb_cfg_grp Select_as_grp(String grp) {return Grps_get_or_null(grp);} + public void Rls() {} + private Fsdb_cfg_grp Grps_get_or_make(String grp) { + Fsdb_cfg_grp rv = (Fsdb_cfg_grp)grps.Fetch(grp); + if (rv == null) { + rv = new Fsdb_cfg_grp(grp); + grps.Add(grp, rv); + } + return rv; + } + public Fsdb_cfg_grp Grps_get_or_null(String grp) {return (Fsdb_cfg_grp)grps.Fetch(grp);} +} +class Fsdb_cfg_tbl_sql extends Fsdb_cfg_tbl_base implements Fsdb_cfg_tbl { + private Db_provider provider; + private Db_stmt stmt_insert, stmt_update, stmt_select; + public Fsdb_cfg_tbl Ctor(Db_provider provider, boolean created) { + this.provider = provider; + if (created) Create_table(); + return this; + } + private void Create_table() { + Sqlite_engine_.Tbl_create(provider, Tbl_name, Tbl_sql); + Sqlite_engine_.Idx_create(provider, Idx_main); + } + private Db_stmt Insert_stmt() {return Db_stmt_.new_insert_(provider, Tbl_name, Fld_cfg_grp, Fld_cfg_key, Fld_cfg_val);} + public void Insert(String grp, String key, String val) { + if (stmt_insert == null) stmt_insert = Insert_stmt(); + stmt_insert.Clear() + .Val_str_(grp) + .Val_str_(key) + .Val_str_(val) + .Exec_insert(); + } + private Db_stmt Update_stmt() {return Db_stmt_.new_update_(provider, Tbl_name, String_.Ary(Fld_cfg_grp, Fld_cfg_key), Fld_cfg_val);} + public void Update(String grp, String key, String val) { + if (stmt_update == null) stmt_update = Update_stmt(); + stmt_update.Clear() + .Val_str_(val) + .Val_str_(grp) + .Val_str_(key) + .Exec_update(); + } + private Db_stmt Select_stmt() { + Db_qry_select qry = Db_qry_.select_val_(Tbl_name, Fld_cfg_val, gplx.criterias.Criteria_.And_many(Db_crt_.eq_(Fld_cfg_grp, ""), Db_crt_.eq_(Fld_cfg_key, ""))); + return provider.Prepare(qry); + } + @Override public int Select_as_int_or(String grp, String key, int or) {return Int_.parse_or_(Select_as_str_or(grp, key, null), or);} + public String Select_as_str_or(String grp, String key, String or) { + if (stmt_select == null) stmt_select = Select_stmt(); + Object rv = (String)stmt_select.Clear() + .Val_str_(grp) + .Val_str_(key) + .Exec_select_val(); + return rv == null ? or : (String)rv; + } + public Fsdb_cfg_grp Select_as_grp(String grp) { + Fsdb_cfg_grp rv = null; + Db_qry_select qry = Db_qry_.select_cols_(Tbl_name, gplx.criterias.Criteria_.And_many(Db_crt_.eq_(Fld_cfg_grp, "")), Fld_cfg_key, Fld_cfg_val); + DataRdr rdr = DataRdr_.Null; + try { + rdr = provider.Prepare(qry).Clear().Val_str_(grp).Exec_select(); + while (rdr.MoveNextPeer()) { + if (rv == null) rv = new Fsdb_cfg_grp(grp); + String key = rdr.ReadStr(Fld_cfg_key); + String val = rdr.ReadStr(Fld_cfg_val); + rv.Upsert(key, val); + } + } + finally {rdr.Rls();} + return rv == null ? Fsdb_cfg_grp.Null : rv; + } + public void Rls() { + if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = null;} + if (stmt_update != null) {stmt_update.Rls(); stmt_update = null;} + if (stmt_select != null) {stmt_select.Rls(); stmt_select = null;} + } + private static final String Tbl_name = "fsdb_cfg", Fld_cfg_grp = "cfg_grp", Fld_cfg_key = "cfg_key", Fld_cfg_val = "cfg_val"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_cfg" + , "( cfg_grp varchar(255) NOT NULL" + , ", cfg_key varchar(255) NOT NULL" + , ", cfg_val varchar(1024) NOT NULL" + , ");" + ); + private static final Db_idx_itm + Idx_main = Db_idx_itm.sql_("CREATE INDEX IF NOT EXISTS fsdb_cfg__main ON fsdb_cfg (cfg_grp, cfg_key, cfg_val);") + ; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_cfg_tbl_.java b/400_xowa/src/gplx/fsdb/Fsdb_cfg_tbl_.java new file mode 100644 index 000000000..14f17c261 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_cfg_tbl_.java @@ -0,0 +1,22 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_cfg_tbl_ { + public static Fsdb_cfg_tbl new_sql_() {return new Fsdb_cfg_tbl_sql();} + public static Fsdb_cfg_tbl new_mem_() {return new Fsdb_cfg_tbl_mem();} +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_abc_mgr.java b/400_xowa/src/gplx/fsdb/Fsdb_db_abc_mgr.java new file mode 100644 index 000000000..1c2889515 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_abc_mgr.java @@ -0,0 +1,112 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_db_abc_mgr implements RlsAble { + private Db_provider boot_provider; + public int Next_id() {return cfg_mgr.Next_id();} + public Fsdb_mnt_mgr Mnt_mgr() {return mnt_mgr;} private Fsdb_mnt_mgr mnt_mgr; + public Fsdb_db_abc_mgr(Fsdb_mnt_mgr mnt_mgr) {this.mnt_mgr = mnt_mgr;} + public Fsdb_db_atr_mgr Atr_mgr() {return atr_mgr;} private Fsdb_db_atr_mgr atr_mgr; + public Fsdb_db_bin_mgr Bin_mgr() {return bin_mgr;} private Fsdb_db_bin_mgr bin_mgr; + public Fsdb_cfg_mgr Cfg_mgr() {return cfg_mgr;} private Fsdb_cfg_mgr cfg_mgr; + public Fsdb_db_abc_mgr Init(Io_url dir) { + Io_url url = dir.GenSubFil("fsdb.abc.sqlite3"); + if (Io_mgr._.ExistsFil(url)) + Init_load(dir, url); + else + Init_make(dir, url); + return this; + } + public void Fil_insert(Fsdb_fil_itm rv , byte[] dir, byte[] fil, int ext_id, DateAdp modified, String hash, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + int bin_db_id = bin_mgr.Get_id_for_insert(bin_len); + rv.Db_bin_id_(bin_db_id); + int fil_id = atr_mgr.Fil_insert(rv, dir, fil, ext_id, modified, hash, bin_db_id, bin_len, bin_rdr); + bin_len = bin_mgr.Insert(bin_db_id, fil_id, Fsdb_bin_tbl.Owner_tid_fil, bin_len, bin_rdr); + bin_mgr.Increment(bin_len); + } + public void Thm_insert(Fsdb_xtn_thm_itm rv, byte[] dir, byte[] fil, int ext_id, int w, int h, double thumbtime, int page, DateAdp modified, String hash, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + int bin_db_id = bin_mgr.Get_id_for_insert(bin_len); + rv.Db_bin_id_(bin_db_id); + int thm_id = atr_mgr.Thm_insert(rv, dir, fil, ext_id, w, h, thumbtime, page, modified, hash, bin_db_id, bin_len, bin_rdr); + bin_len = bin_mgr.Insert(bin_db_id, thm_id, Fsdb_bin_tbl.Owner_tid_thm, bin_len, bin_rdr); + bin_mgr.Increment(bin_len); + } + public void Img_insert(Fsdb_xtn_img_itm rv, byte[] dir, byte[] fil, int ext_id, DateAdp modified, String hash, long bin_len, gplx.ios.Io_stream_rdr bin_rdr, int img_w, int img_h) { + int bin_db_id = bin_mgr.Get_id_for_insert(bin_len); + rv.Db_bin_id_(bin_db_id); + int fil_id = atr_mgr.Img_insert(rv, String_.new_utf8_(dir), String_.new_utf8_(fil), ext_id, img_w, img_h, modified, hash, bin_db_id, bin_len, bin_rdr); + bin_len = bin_mgr.Insert(bin_db_id, fil_id, Fsdb_bin_tbl.Owner_tid_fil, bin_len, bin_rdr); + bin_mgr.Increment(bin_len); + } + public boolean Thm_select_bin(byte[] dir, byte[] fil, Fsdb_xtn_thm_itm thm) { + Fsdb_fil_itm fil_itm = atr_mgr.Fil_select(dir, fil); + return atr_mgr.Thm_select(fil_itm.Id(), thm); + } + public Fsdb_fil_itm Fil_select_bin(byte[] dir, byte[] fil, boolean is_thumb, int width, double thumbtime) { + return atr_mgr.Fil_select(dir, fil); + } + public void Txn_open() { + boot_provider.Txn_mgr().Txn_bgn_if_none(); + atr_mgr.Txn_open(); + bin_mgr.Txn_open(); + } + public void Txn_save() { + atr_mgr.Txn_save(boot_provider); + bin_mgr.Txn_save(); + cfg_mgr.Txn_save(); + } + public void Rls() { + atr_mgr.Rls(); + bin_mgr.Rls(); + cfg_mgr.Rls(); + boot_provider.Rls(); + } + private void Init_load(Io_url dir, Io_url boot_url) { + Db_connect connect = Db_connect_sqlite.load_(boot_url); + boot_provider = Db_provider_.new_(connect); + atr_mgr = Fsdb_db_atr_mgr.load_(this, boot_provider, dir); + bin_mgr = Fsdb_db_bin_mgr.load_(boot_provider, dir); + cfg_mgr = Fsdb_cfg_mgr.load_(this, boot_provider); + if (!cfg_mgr.Patch_next_id()) Fsdb_db_abc_mgr_.Patch_next_id(this, dir); + } + private void Init_make(Io_url dir, Io_url boot_url) { + Db_connect connect = Db_connect_sqlite.make_(boot_url); + boot_provider = Db_provider_.new_(connect); + Sqlite_engine_.Pragma_page_size_4096(boot_provider); + atr_mgr = Fsdb_db_atr_mgr.make_(this, boot_provider, dir); + bin_mgr = Fsdb_db_bin_mgr.make_(boot_provider, dir); + cfg_mgr = Fsdb_cfg_mgr.make_(this, boot_provider); + this.Txn_save(); // immediately save new entries in atr,cfg + } +} +class Fsdb_db_abc_mgr_ { + public static void Patch_next_id(Fsdb_db_abc_mgr abc_mgr, Io_url dir) { + if (!String_.Eq(dir.NameOnly(), "fsdb.user")) return; + Fsdb_db_atr_mgr atr_mgr = abc_mgr.Atr_mgr(); + Fsdb_cfg_mgr cfg_mgr = abc_mgr.Cfg_mgr(); + int last_id = -1; + if (atr_mgr.Len() > 0) { + Fsdb_db_atr_fil atr_fil = atr_mgr.Get_at(0); + int max_fil_id = Db_provider_.Select_fld0_as_int_or(atr_fil.Provider(), "SELECT Max(fil_id) AS MaxId FROM fsdb_fil;", -1); + int max_thm_id = Db_provider_.Select_fld0_as_int_or(atr_fil.Provider(), "SELECT Max(thm_id) AS MaxId FROM fsdb_xtn_thm;", -1); + last_id = max_fil_id > max_thm_id ? max_fil_id : max_thm_id; + } + cfg_mgr.Patch_next_id_exec(last_id); + } +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_atr_fil.java b/400_xowa/src/gplx/fsdb/Fsdb_db_atr_fil.java new file mode 100644 index 000000000..ef5c8c4fe --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_atr_fil.java @@ -0,0 +1,150 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; import gplx.cache.*; +public class Fsdb_db_atr_fil implements RlsAble { + private Gfo_cache_mgr_bry dir_cache = new Gfo_cache_mgr_bry(); + private Fsdb_dir_tbl tbl_dir; private Fsdb_fil_tbl tbl_fil; private Fsdb_xtn_thm_tbl tbl_thm; private Fsdb_xtn_img_tbl tbl_img; private Bool_obj_ref img_needs_create = Bool_obj_ref.n_(); + public Fsdb_db_atr_fil(Fsdb_db_abc_mgr abc_mgr, Io_url url, boolean create) { + this.abc_mgr = abc_mgr; + Db_connect connect = create ? Db_connect_sqlite.make_(url) : Db_connect_sqlite.load_(url); + provider = Db_provider_.new_(connect); + Sqlite_engine_.Pragma_page_size_4096(provider); + tbl_dir = new Fsdb_dir_tbl(provider, create); + tbl_fil = new Fsdb_fil_tbl(provider, create); + tbl_thm = new Fsdb_xtn_thm_tbl(this, provider, create); + tbl_img = new Fsdb_xtn_img_tbl(provider, create); + } + public Fsdb_db_abc_mgr Abc_mgr() {return abc_mgr;} private Fsdb_db_abc_mgr abc_mgr; + public Db_provider Provider() {return provider;} private Db_provider provider; + public int Id() {return id;} private int id; + public Io_url Url() {return url;} private Io_url url; + public String Path_bgn() {return path_bgn;} private String path_bgn; + public byte Cmd_mode() {return cmd_mode;} public Fsdb_db_atr_fil Cmd_mode_(byte v) {cmd_mode = v; return this;} private byte cmd_mode; + public void Rls() { + tbl_dir.Rls(); + tbl_fil.Rls(); + tbl_img.Rls(); + tbl_thm.Rls(); + provider.Txn_mgr().Txn_end_all(); + provider.Rls(); + } + public void Txn_open() { + provider.Txn_mgr().Txn_bgn_if_none(); + } + public void Txn_save() { + provider.Txn_mgr().Txn_end_all(); + } + public Fsdb_fil_itm Fil_select(byte[] dir, byte[] fil) { + Int_obj_ref dir_id_obj = (Int_obj_ref)dir_cache.Get_or_null(dir); + int dir_id = -1; + if (dir_id_obj == null) { + Fsdb_dir_itm dir_itm = tbl_dir.Select_itm(String_.new_utf8_(dir)); + dir_id = dir_itm == Fsdb_dir_itm.Null ? -1 : dir_itm.Id(); + dir_cache.Add(dir, Int_obj_ref.new_(dir_id)); + } + else + dir_id = dir_id_obj.Val(); + if (dir_id == Int_.Neg1) return Fsdb_fil_itm.Null; + return tbl_fil.Select_itm_by_name(dir_id, String_.new_utf8_(fil)); + } + public boolean Thm_select(int owner_id, Fsdb_xtn_thm_itm thm) { + return tbl_thm.Select_itm_by_fil_width(owner_id, thm); + } + public int Fil_insert(Fsdb_fil_itm rv, String dir, String fil, int ext_id, DateAdp modified, String hash, int bin_db_id, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + int dir_id = Dir_id__get_or_insert(dir); + int fil_id = Fil_id__get_or_insert(Fsdb_xtn_tid_.Tid_none, dir_id, fil, ext_id, modified, hash, bin_db_id, bin_len); + rv.Id_(fil_id).Owner_(dir_id); + return fil_id; + } + public int Img_insert(Fsdb_xtn_img_itm rv, String dir, String fil, int ext_id, int img_w, int img_h, DateAdp modified, String hash, int bin_db_id, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + int dir_id = Dir_id__get_or_insert(dir); + img_needs_create.Val_(false); + int fil_id = Fil_id__get_or_insert(Fsdb_xtn_tid_.Tid_img, dir_id, fil, ext_id, modified, hash, bin_db_id, bin_len); + if (img_needs_create.Val()) // NOTE: fsdb_fil row can already exist; EX: thm gets inserted -> fsdb_fil row gets created with -1 bin_db_id; orig gets inserted -> fsdb_fil row already exists + tbl_img.Insert(fil_id, img_w, img_h); + rv.Id_(fil_id); + return fil_id; + } + public int Thm_insert(Fsdb_xtn_thm_itm rv, String dir, String fil, int ext_id, int thm_w, int thm_h, double thumbtime, int page, DateAdp modified, String hash, int bin_db_id, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + int dir_id = Dir_id__get_or_insert(dir); + int fil_id = Fil_id__get_or_insert(Fsdb_xtn_tid_.Tid_thm, dir_id, fil, ext_id, modified, hash, Fsdb_bin_tbl.Null_db_bin_id, Fsdb_bin_tbl.Null_size); // NOTE: bin_db_id must be set to NULL + int thm_id = abc_mgr.Next_id(); + tbl_thm.Insert(thm_id, fil_id, thm_w, thm_h, thumbtime, page, bin_db_id, bin_len, modified, hash); + rv.Id_(thm_id).Owner_id_(fil_id).Dir_id_(dir_id); + return thm_id; + } + public static Fsdb_db_atr_fil load_(Fsdb_db_abc_mgr abc_mgr, DataRdr rdr, Io_url dir) { + Io_url url = dir.GenSubFil(rdr.ReadStr(Fsdb_db_atr_tbl.Fld_url)); + Fsdb_db_atr_fil rv = new Fsdb_db_atr_fil(abc_mgr, url, false); + rv.id = rdr.ReadInt(Fsdb_db_atr_tbl.Fld_uid); + rv.url = url; + rv.path_bgn = rdr.ReadStr(Fsdb_db_atr_tbl.Fld_path_bgn); + rv.cmd_mode = Db_cmd_mode.Ignore; + return rv; + } + public static Fsdb_db_atr_fil make_(Fsdb_db_abc_mgr abc_mgr, int id, Io_url url, String path_bgn) { + Fsdb_db_atr_fil rv = new Fsdb_db_atr_fil(abc_mgr, url, true); + rv.id = id; + rv.url = url; + rv.path_bgn = path_bgn; + rv.cmd_mode = Db_cmd_mode.Create; + return rv; + } + private int Dir_id__get_or_insert(String dir_str) { + byte[] dir_bry = Bry_.new_utf8_(dir_str); + Object rv_obj = dir_cache.Get_or_null(dir_bry); + int rv = -1; + if (rv_obj != null) { // item found + rv = ((Int_obj_ref)rv_obj).Val(); + if (rv == -1) // dir was previously -1; occurs when doing select on empty db (no dir, so -1 added) and then doing insert (-1 now needs to be dropped) + dir_cache.Del(dir_bry); + } + if (rv == -1) { + Fsdb_dir_itm itm = tbl_dir.Select_itm(dir_str); + if (itm == Fsdb_dir_itm.Null) { + rv = abc_mgr.Next_id(); + tbl_dir.Insert(rv, dir_str, 0); // 0: always assume root owner + } + else { + rv = itm.Id(); + } + dir_cache.Add(dir_bry, Int_obj_ref.new_(rv)); + } + return rv; + } + private int Fil_id__get_or_insert(int xtn_tid, int dir_id, String fil, int ext_id, DateAdp modified, String hash, int bin_db_id, long bin_len) { + Fsdb_fil_itm fil_itm = tbl_fil.Select_itm_by_name(dir_id, fil); + int fil_id = fil_itm.Id(); + if (fil_id == Fsdb_fil_itm.Null_id) { // new item + fil_id = abc_mgr.Next_id(); + tbl_fil.Insert(fil_id, dir_id, fil, xtn_tid, ext_id, bin_len, modified, hash, bin_db_id); + img_needs_create.Val_(true); + } + else { // existing item + if ( fil_itm.Db_bin_id() == Fsdb_bin_tbl.Null_db_bin_id // prv row was previously inserted by thumb + && xtn_tid != Fsdb_xtn_tid_.Tid_thm // cur row is not thumb + ) { + tbl_fil.Update(fil_id, dir_id, fil, xtn_tid, ext_id, bin_len, modified, hash, bin_db_id); // update props; note that thumb inserts null props, whereas file will insert real props (EX: bin_db_id) + if (xtn_tid == Fsdb_xtn_tid_.Tid_img) + img_needs_create.Val_(true); + } + } + return fil_id; + } +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_atr_mgr.java b/400_xowa/src/gplx/fsdb/Fsdb_db_atr_mgr.java new file mode 100644 index 000000000..aadd01d7b --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_atr_mgr.java @@ -0,0 +1,76 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; import gplx.cache.*; +public class Fsdb_db_atr_mgr implements RlsAble { + private Fsdb_db_atr_fil[] itms; private Fsdb_db_atr_fil itms_0; + public int Len() {return itms.length;} + public Fsdb_db_atr_fil Get_at(int i) {return i == Id_0 ? itms_0 : itms[i];} + public Fsdb_fil_itm Fil_select(byte[] dir, byte[] fil) {return itms_0.Fil_select(dir, fil);} + public boolean Thm_select(int owner_id, Fsdb_xtn_thm_itm thm) {return itms_0.Thm_select(owner_id, thm);} + public int Fil_insert(Fsdb_fil_itm rv , byte[] dir, byte[] fil, int ext_id, DateAdp modified, String hash, int bin_db_id, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + return itms_0.Fil_insert(rv, String_.new_utf8_(dir), String_.new_utf8_(fil), ext_id, modified, hash, bin_db_id, bin_len, bin_rdr); + } + public int Img_insert(Fsdb_xtn_img_itm rv, String dir, String fil, int ext_id, int img_w, int img_h, DateAdp modified, String hash, int bin_db_id, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + return itms_0.Img_insert(rv, dir, fil, ext_id, img_w, img_h, modified, hash, bin_db_id, bin_len, bin_rdr); + } + public int Thm_insert(Fsdb_xtn_thm_itm rv, byte[] dir, byte[] fil, int ext_id, int width, int height, double thumbtime, int page, DateAdp modified, String hash, int bin_db_id, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + return itms_0.Thm_insert(rv, String_.new_utf8_(dir), String_.new_utf8_(fil), ext_id, width, height, thumbtime, page, modified, hash, bin_db_id, bin_len, bin_rdr); + } + public void Txn_open() { + int len = itms.length; + for (int i = 0; i < len; i++) { + Fsdb_db_atr_fil itm = itms[i]; + itm.Txn_open(); + } + } + public void Txn_save(Db_provider provider) { + Fsdb_db_atr_tbl.Commit_all(provider, itms); + int len = itms.length; + for (int i = 0; i < len; i++) { + Fsdb_db_atr_fil itm = itms[i]; + itm.Txn_save(); + } + } + public void Rls() { + int len = itms.length; + for (int i = 0; i < len; i++) { + Fsdb_db_atr_fil itm = itms[i]; + itm.Rls(); + } + } + public static Fsdb_db_atr_mgr load_(Fsdb_db_abc_mgr abc_mgr, Db_provider p, Io_url dir) { + Fsdb_db_atr_mgr rv = new Fsdb_db_atr_mgr(); + rv.itms = Fsdb_db_atr_tbl.Select_all(abc_mgr, p, dir); + rv.itms_0 = rv.itms[0]; + return rv; + } + public static Fsdb_db_atr_mgr make_(Fsdb_db_abc_mgr abc_mgr, Db_provider p, Io_url dir) { + Fsdb_db_atr_tbl.Create_table(p); + Fsdb_db_atr_mgr rv = new Fsdb_db_atr_mgr(); + Fsdb_db_atr_fil itm = Fsdb_db_atr_fil.make_(abc_mgr, Id_0, url_(dir, Id_0), Path_bgn_0); + rv.itms_0 = itm; + rv.itms = new Fsdb_db_atr_fil[] {itm}; + return rv; + } + private static Io_url url_(Io_url dir, int id) { + return dir.GenSubFil_ary("fsdb.atr.", Int_.XtoStr_PadBgn(id, 2), ".sqlite3"); + } + public static final int Id_0 = 0; + public static final String Path_bgn_0 = ""; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_atr_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_db_atr_tbl.java new file mode 100644 index 000000000..177c8523d --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_atr_tbl.java @@ -0,0 +1,64 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_db_atr_tbl { + public static void Create_table(Db_provider p) {Sqlite_engine_.Tbl_create(p, Tbl_name, Tbl_sql);} + public static Fsdb_db_atr_fil[] Select_all(Fsdb_db_abc_mgr abc_mgr, Db_provider p, Io_url dir) { + ListAdp rv = ListAdp_.new_(); + Db_qry qry = Db_qry_select.new_().From_(Tbl_name).Cols_all_().OrderBy_asc_(Fld_uid); + DataRdr rdr = DataRdr_.Null; + try { + rdr = p.Exec_qry_as_rdr(qry); + while (rdr.MoveNextPeer()) { + Fsdb_db_atr_fil itm = Fsdb_db_atr_fil.load_(abc_mgr, rdr, dir); + rv.Add(itm); + } + } finally {rdr.Rls();} + return (Fsdb_db_atr_fil[])rv.XtoAry(Fsdb_db_atr_fil.class); + } + public static void Commit_all(Db_provider provider, Fsdb_db_atr_fil[] ary) { + stmt_bldr.Init(provider); + try { + int len = ary.length; + for (int i = 0; i < len; i++) + Commit_itm(ary[i]); + stmt_bldr.Commit(); + } finally {stmt_bldr.Rls();} + } + private static void Commit_itm(Fsdb_db_atr_fil itm) { + Db_stmt stmt = stmt_bldr.Get(itm.Cmd_mode()); + switch (itm.Cmd_mode()) { + case Db_cmd_mode.Create: stmt.Clear().Val_int_(itm.Id()) .Val_str_(itm.Url().NameAndExt()).Val_str_(itm.Path_bgn()).Exec_insert(); break; + case Db_cmd_mode.Update: stmt.Clear() .Val_str_(itm.Url().NameAndExt()).Val_str_(itm.Path_bgn()).Val_int_(itm.Id()).Exec_update(); break; + case Db_cmd_mode.Delete: stmt.Clear().Val_int_(itm.Id()).Exec_delete(); break; + case Db_cmd_mode.Ignore: break; + default: throw Err_.unhandled(itm.Cmd_mode()); + } + itm.Cmd_mode_(Db_cmd_mode.Ignore); + } + public static final String Tbl_name = "fsdb_db_atr", Fld_uid = "uid", Fld_url = "url", Fld_path_bgn = "path_bgn"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_db_atr" + , "( uid integer NOT NULL PRIMARY KEY" + , ", url varchar(255) NOT NULL" + , ", path_bgn varchar(255) NOT NULL" + , ");" + ); + private static Db_stmt_bldr stmt_bldr = new Db_stmt_bldr(Tbl_name, String_.Ary(Fld_uid), Fld_url, Fld_path_bgn); +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_bin_fil.java b/400_xowa/src/gplx/fsdb/Fsdb_db_bin_fil.java new file mode 100644 index 000000000..597726e51 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_bin_fil.java @@ -0,0 +1,85 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; import gplx.ios.*; +public class Fsdb_db_bin_fil implements RlsAble { + public int Id() {return id;} private int id; + public Io_url Url() {return url;} private Io_url url; + public long Bin_max() {return bin_max;} private long bin_max; + public void Bin_max_(long v) { + bin_max = v; + if (cmd_mode == Db_cmd_mode.Ignore) cmd_mode = Db_cmd_mode.Update; + } + public long Bin_len() {return bin_len;} private long bin_len; + public void Bin_len_(long v) { + bin_len = v; + if (cmd_mode == Db_cmd_mode.Ignore) cmd_mode = Db_cmd_mode.Update; + } + public byte Cmd_mode() {return cmd_mode;} public Fsdb_db_bin_fil Cmd_mode_(byte v) {cmd_mode = v; return this;} private byte cmd_mode; + public Db_provider Provider() { + if (provider == null) { + if (cmd_mode == Db_cmd_mode.Create) { + provider = Db_provider_.new_(Db_connect_sqlite.make_(url)); + Sqlite_engine_.Pragma_page_size_4096(provider); + Fsdb_bin_tbl.Create_table(provider); + } + else + provider = Db_provider_.new_(Db_connect_sqlite.load_(url)); + } + return provider; + } private Db_provider provider; + public void Rls() {if (provider != null) provider.Rls();} + public long Insert(int bin_id, byte owner_tid, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + Db_stmt stmt = Db_stmt_.Null; + try { + stmt = Fsdb_bin_tbl.Insert_stmt(this.Provider()); + return Fsdb_bin_tbl.Insert_rdr(stmt, bin_id, owner_tid, bin_len, bin_rdr); + } + finally {stmt.Rls();} + } + public boolean Get_to_url(int id, Io_url url, byte[] bin_bfr, int bin_flush_when) { + return Fsdb_bin_tbl.Select_to_url(this.Provider(), id, url, bin_bfr, bin_flush_when); + } + public Io_stream_rdr Get_as_rdr(int id) { + return Fsdb_bin_tbl.Select_as_rdr(this.Provider(), id); + } + public static Fsdb_db_bin_fil load_(DataRdr rdr, Io_url dir) { + return new_ + ( rdr.ReadInt(Fsdb_db_bin_tbl.Fld_uid) + , dir.GenSubFil(rdr.ReadStr(Fsdb_db_bin_tbl.Fld_url)) + , rdr.ReadLong(Fsdb_db_bin_tbl.Fld_bin_len) + , rdr.ReadLong(Fsdb_db_bin_tbl.Fld_bin_max) + , Db_cmd_mode.Ignore + ); + } + public static Fsdb_db_bin_fil make_(int id, Io_url url, long bin_len, long bin_max) { + Fsdb_db_bin_fil rv = new_(id, url, bin_len, bin_max, Db_cmd_mode.Create); + rv.Provider(); // force table create + return rv; + } + private static Fsdb_db_bin_fil new_(int id, Io_url url, long bin_len, long bin_max, byte cmd_mode) { + Fsdb_db_bin_fil rv = new Fsdb_db_bin_fil(); + rv.id = id; + rv.url = url; + rv.bin_len = bin_len; + rv.bin_max = bin_max; + rv.cmd_mode = cmd_mode; + return rv; + } + public static final Fsdb_db_bin_fil[] Ary_empty = new Fsdb_db_bin_fil[0]; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_bin_mgr.java b/400_xowa/src/gplx/fsdb/Fsdb_db_bin_mgr.java new file mode 100644 index 000000000..c2775d110 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_bin_mgr.java @@ -0,0 +1,99 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_db_bin_mgr implements RlsAble { + private Io_url dir; + private Fsdb_db_bin_fil[] itms = Fsdb_db_bin_fil.Ary_empty; private int itms_len = 0; + private Fsdb_db_bin_fil itms_n; + private Db_provider provider; + private Fsdb_db_bin_mgr(Io_url dir) {this.dir = dir;} + public int Len() {return itms.length;} + public long Db_bin_max() {return db_bin_max;} + public int Insert_to_bin() {return insert_to_bin;} public Fsdb_db_bin_mgr Insert_to_bin_(int v) {insert_to_bin = v; return this;} private int insert_to_bin = Fsdb_mnt_mgr.Insert_to_bin_null; + public Fsdb_db_bin_mgr Db_bin_max_(long v) { + db_bin_max = v; + for (int i = 0; i < itms_len; i++) + itms[i].Bin_max_(v); + return this; + } private long db_bin_max = Io_mgr.Len_mb * Long_.X_by_int(188); + public Fsdb_db_bin_fil Get_at(int i) {return itms[i];} + private Fsdb_db_bin_fil Get_cur() {return itms_len == 0 ? null : itms[itms_len - 1];} + public void Txn_open() { + Get_cur().Provider().Txn_mgr().Txn_bgn_if_none(); + } + public void Txn_save() { + Fsdb_db_bin_tbl.Commit_all(provider, itms); + Get_cur().Provider().Txn_mgr().Txn_end_all(); + } + public void Rls() { + int len = itms.length; + for (int i = 0; i < len; i++) { + Fsdb_db_bin_fil itm = itms[i]; + itm.Rls(); + } + } + public int Get_id_for_insert(long bin_len) { + if (insert_to_bin != Fsdb_mnt_mgr.Insert_to_bin_null) return insert_to_bin; // insert_to_bin specified; return it + if (itms_n.Bin_len() > itms_n.Bin_max()) + Itms_add(0); + return itms_n.Id(); + } + public void Increment(long bin_len) { + long new_bin_len = itms_n.Bin_len() + bin_len; + itms_n.Bin_len_(new_bin_len); + } + public long Insert(int db_id, int bin_id, byte owner_tid, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + Fsdb_db_bin_fil bin_fil = itms[db_id]; + return bin_fil.Insert(bin_id, owner_tid, bin_len, bin_rdr); + } + public static Fsdb_db_bin_mgr load_(Db_provider p, Io_url dir) { + Fsdb_db_bin_mgr rv = new Fsdb_db_bin_mgr(dir); + rv.provider = p; + rv.itms = Fsdb_db_bin_tbl.Select_all(p, dir); + rv.itms_len = rv.itms.length; + rv.itms_n = rv.itms[rv.itms_len - 1]; + return rv; + } + public static Fsdb_db_bin_mgr make_(Db_provider p, Io_url dir) { + Fsdb_db_bin_tbl.Create_table(p); + Fsdb_db_bin_mgr rv = new Fsdb_db_bin_mgr(dir); + rv.provider = p; + rv.Itms_add(0); + return rv; + } + private void Itms_add(long bin_len) { + Fsdb_db_bin_fil cur = Get_cur(); + if (cur != null) { + cur.Provider().Txn_mgr().Txn_end_all(); + cur.Provider().Rls(); + } + int new_itms_len = itms_len + 1; + Fsdb_db_bin_fil[] new_itms = new Fsdb_db_bin_fil[new_itms_len]; + for (int i = 0; i < itms_len; i++) + new_itms[i] = itms[i]; + itms_n = Fsdb_db_bin_fil.make_(itms_len, url_(dir, itms_len), bin_len, db_bin_max); + itms = new_itms; + itms_len = new_itms_len; + itms[itms_len - 1] = itms_n; + this.Txn_open(); + } + private static Io_url url_(Io_url dir, int id) { + return dir.GenSubFil_ary("fsdb.bin.", Int_.XtoStr_PadBgn(id, 4), ".sqlite3"); + } +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_db_bin_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_db_bin_tbl.java new file mode 100644 index 000000000..862ca34da --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_db_bin_tbl.java @@ -0,0 +1,65 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_db_bin_tbl { + public static void Create_table(Db_provider p) {Sqlite_engine_.Tbl_create(p, Tbl_name, Tbl_sql);} + public static void Commit_all(Db_provider provider, Fsdb_db_bin_fil[] ary) { + stmt_bldr.Init(provider); + try { + int len = ary.length; + for (int i = 0; i < len; i++) + Commit_itm(ary[i]); + stmt_bldr.Commit(); + } finally {stmt_bldr.Rls();} + } + private static void Commit_itm(Fsdb_db_bin_fil itm) { + Db_stmt stmt = stmt_bldr.Get(itm.Cmd_mode()); + switch (itm.Cmd_mode()) { + case Db_cmd_mode.Create: stmt.Clear().Val_int_(itm.Id()) .Val_str_(itm.Url().NameAndExt()).Val_long_(itm.Bin_len()).Val_long_(itm.Bin_max()).Exec_insert(); break; + case Db_cmd_mode.Update: stmt.Clear() .Val_str_(itm.Url().NameAndExt()).Val_long_(itm.Bin_len()).Val_long_(itm.Bin_max()).Val_int_(itm.Id()).Exec_update(); break; + case Db_cmd_mode.Delete: stmt.Clear().Val_int_(itm.Id()).Exec_delete(); break; + case Db_cmd_mode.Ignore: break; + default: throw Err_.unhandled(itm.Cmd_mode()); + } + itm.Cmd_mode_(Db_cmd_mode.Ignore); + } + public static Fsdb_db_bin_fil[] Select_all(Db_provider p, Io_url dir) { + ListAdp rv = ListAdp_.new_(); + Db_qry qry = Db_qry_select.new_().From_(Tbl_name).Cols_all_().OrderBy_asc_(Fld_uid); + DataRdr rdr = DataRdr_.Null; + try { + rdr = p.Exec_qry_as_rdr(qry); + while (rdr.MoveNextPeer()) { + Fsdb_db_bin_fil itm = Fsdb_db_bin_fil.load_(rdr, dir); + rv.Add(itm); + } + } finally {rdr.Rls();} + return (Fsdb_db_bin_fil[])rv.XtoAry(Fsdb_db_bin_fil.class); + } + public static final String Tbl_name = "fsdb_db_bin", Fld_uid = "uid", Fld_url = "url", Fld_bin_len = "bin_len", Fld_bin_max = "bin_max"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_db_bin" + , "( uid integer NOT NULL PRIMARY KEY" + , ", url varchar(255) NOT NULL" + , ", bin_len bigint NOT NULL" + , ", bin_max bigint NOT NULL" + , ");" + ); + private static Db_stmt_bldr stmt_bldr = new Db_stmt_bldr(Tbl_name, String_.Ary(Fld_uid), Fld_url, Fld_bin_len, Fld_bin_max); +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_dir_itm.java b/400_xowa/src/gplx/fsdb/Fsdb_dir_itm.java new file mode 100644 index 000000000..329c19afc --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_dir_itm.java @@ -0,0 +1,25 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_dir_itm { + public Fsdb_dir_itm(int id, int owner, String name) {this.id = id; this.owner = owner; this.name = name;} + public int Id() {return id;} public Fsdb_dir_itm Id_(int v) {id = v; return this;} private int id; + public int Owner() {return owner;} public Fsdb_dir_itm Owner_(int v) {owner = v; return this;} private int owner; + public String Name() {return name;} public Fsdb_dir_itm Name_(String v) {name = v; return this;} private String name; + public static final Fsdb_dir_itm Null = new Fsdb_dir_itm(0, 0, ""); +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_dir_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_dir_tbl.java new file mode 100644 index 000000000..f334b5d43 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_dir_tbl.java @@ -0,0 +1,87 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_dir_tbl { + private Db_provider provider; + private Db_stmt stmt_insert, stmt_update, stmt_select_by_name; + public Fsdb_dir_tbl(Db_provider provider, boolean created) { + this.provider = provider; + if (created) Create_table(); + } + public void Rls() { + if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = null;} + if (stmt_update != null) {stmt_update.Rls(); stmt_update = null;} + if (stmt_select_by_name != null) {stmt_select_by_name.Rls(); stmt_select_by_name = null;} + } + public void Create_table() { + Sqlite_engine_.Tbl_create(provider, Tbl_name, Tbl_sql); + Sqlite_engine_.Idx_create(provider, Idx_name); + } + private Db_stmt Insert_stmt() {return Db_stmt_.new_insert_(provider, Tbl_name, Fld_dir_id, Fld_dir_owner_id, Fld_dir_name);} + public void Insert(int id, String name, int owner_id) { + if (stmt_insert == null) stmt_insert = Insert_stmt(); + try { + stmt_insert.Clear() + .Val_int_(id) + .Val_int_(owner_id) + .Val_str_(name) + .Exec_insert(); + } catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail + } + private Db_stmt Update_stmt() {return Db_stmt_.new_update_(provider, Tbl_name, String_.Ary(Fld_dir_id), Fld_dir_owner_id, Fld_dir_name);} + public void Update(int id, String name, int owner_id) { + if (stmt_update == null) stmt_update = Update_stmt(); + try { + stmt_update.Clear() + .Val_int_(id) + .Val_str_(name) + .Val_int_(owner_id) + .Exec_update(); + } catch (Exception exc) {stmt_update = null; throw Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail + } + private Db_stmt Select_itm_stmt() { + Db_qry qry = Db_qry_.select_().From_(Tbl_name).Cols_(Fld_dir_id, Fld_dir_owner_id).Where_(Db_crt_.eq_(Fld_dir_name, Bry_.Empty)); + return provider.Prepare(qry); + } + public Fsdb_dir_itm Select_itm(String dir_name) { + if (stmt_select_by_name == null) stmt_select_by_name = Select_itm_stmt(); + DataRdr rdr = DataRdr_.Null; + try { + rdr = stmt_select_by_name.Clear() + .Val_str_(dir_name) + .Exec_select(); + while (rdr.MoveNextPeer()) { + return new Fsdb_dir_itm(rdr.ReadInt(Fld_dir_id), rdr.ReadInt(Fld_dir_owner_id), dir_name); + } + return Fsdb_dir_itm.Null; + } + finally {rdr.Rls();} + } + public static final String Tbl_name = "fsdb_dir", Fld_dir_id = "dir_id", Fld_dir_owner_id = "dir_owner_id", Fld_dir_name = "dir_name"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_dir" + , "( dir_id integer NOT NULL PRIMARY KEY" + , ", dir_owner_id integer NOT NULL" + , ", dir_name varchar(255) NOT NULL" + , ");" + ); + public static final Db_idx_itm + Idx_name = Db_idx_itm.sql_("CREATE INDEX IF NOT EXISTS fsdb_dir__name ON fsdb_dir (dir_name, dir_owner_id, dir_id);") + ; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_fil_itm.java b/400_xowa/src/gplx/fsdb/Fsdb_fil_itm.java new file mode 100644 index 000000000..f520717d0 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_fil_itm.java @@ -0,0 +1,29 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_fil_itm { + public int Id() {return id;} public Fsdb_fil_itm Id_(int v) {id = v; return this;} private int id; + public int Owner() {return owner;} public Fsdb_fil_itm Owner_(int v) {owner = v; return this;} private int owner; + public int Ext_id() {return ext_id;} public Fsdb_fil_itm Ext_id_(int v) {ext_id = v; return this;} private int ext_id; + public String Name() {return name;} public Fsdb_fil_itm Name_(String v) {name = v; return this;} private String name; + public int Db_bin_id() {return bin_db_id;} public Fsdb_fil_itm Db_bin_id_(int v) {bin_db_id = v; return this;} private int bin_db_id; + public int Mnt_id() {return mnt_id;} public Fsdb_fil_itm Mnt_id_(int v) {mnt_id = v; return this;} private int mnt_id; + public Fsdb_fil_itm Init(int id, int owner, int ext_id, String name, int bin_db_id) {this.id = id; this.owner = owner; this.ext_id = ext_id; this.name = name; this.bin_db_id = bin_db_id; return this;} + public static final int Null_id = 0; + public static final Fsdb_fil_itm Null = new Fsdb_fil_itm().Init(Null_id, 0, 0, "", Fsdb_bin_tbl.Null_db_bin_id); +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_fil_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_fil_tbl.java new file mode 100644 index 000000000..5aaf0bf63 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_fil_tbl.java @@ -0,0 +1,134 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_fil_tbl { + private Db_provider provider; + private Db_stmt stmt_insert, stmt_update, stmt_select_by_name, stmt_select_by_id; + public Fsdb_fil_tbl(Db_provider provider, boolean created) { + this.provider = provider; + if (created) Create_table(); + } + private void Create_table() { + Sqlite_engine_.Tbl_create(provider, Tbl_name, Tbl_sql); + Sqlite_engine_.Idx_create(provider, Idx_owner); + } + public void Rls() { + if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = null;} + if (stmt_update != null) {stmt_update.Rls(); stmt_update = null;} + if (stmt_select_by_name != null) {stmt_select_by_name.Rls(); stmt_select_by_name = null;} + if (stmt_select_by_id != null) {stmt_select_by_id.Rls(); stmt_select_by_id = null;} + } + private Db_stmt Insert_stmt() {return Db_stmt_.new_insert_(provider, Tbl_name, Fld_fil_id, Fld_fil_owner_id, Fld_fil_name, Fld_fil_xtn_id, Fld_fil_ext_id, Fld_fil_bin_db_id, Fld_fil_size, Fld_fil_modified, Fld_fil_hash);} + public void Insert(int id, int owner_id, String name, int xtn_id, int ext_id, long size, DateAdp modified, String hash, int bin_db_id) { + if (stmt_insert == null) stmt_insert = Insert_stmt(); + try { + stmt_insert.Clear() + .Val_int_(id) + .Val_int_(owner_id) + .Val_str_(name) + .Val_int_(xtn_id) + .Val_int_(ext_id) + .Val_int_(bin_db_id) + .Val_long_(size) + .Val_str_(Sqlite_engine_.X_date_to_str(modified)) + .Val_str_(hash) + .Exec_insert(); + } catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail + } + private Db_stmt Update_stmt() {return Db_stmt_.new_update_(provider, Tbl_name, String_.Ary(Fld_fil_id), Fld_fil_owner_id, Fld_fil_name, Fld_fil_xtn_id, Fld_fil_ext_id, Fld_fil_bin_db_id, Fld_fil_size, Fld_fil_modified, Fld_fil_hash);} + public void Update(int id, int owner_id, String name, int xtn_id, int ext_id, long size, DateAdp modified, String hash, int bin_db_id) { + if (stmt_update == null) stmt_update = Update_stmt(); + try { + stmt_update.Clear() + .Val_int_(owner_id) + .Val_str_(name) + .Val_int_(xtn_id) + .Val_int_(ext_id) + .Val_int_(bin_db_id) + .Val_long_(size) + .Val_str_(Sqlite_engine_.X_date_to_str(modified)) + .Val_str_(hash) + .Val_int_(id) + .Exec_update(); + } catch (Exception exc) {stmt_update = null; throw Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail + } + private Db_stmt Select_by_name_stmt() { + Db_qry qry = Sqlite_engine_.Supports_indexed_by + ? (Db_qry)Db_qry_sql.rdr_("SELECT * FROM fsdb_fil INDEXED BY fsdb_fil__owner WHERE fil_owner_id = ? AND fil_name = ?;") + : Db_qry_.select_().From_(Tbl_name).Cols_all_().Where_(gplx.criterias.Criteria_.And_many(Db_crt_.eq_(Fld_fil_owner_id, Int_.MinValue), Db_crt_.eq_(Fld_fil_name, ""))) + ; + return provider.Prepare(qry); + } + public Fsdb_fil_itm Select_itm_by_name(int dir_id, String fil_name) { + if (stmt_select_by_name == null) stmt_select_by_name = Select_by_name_stmt(); + DataRdr rdr = DataRdr_.Null; + try { + rdr = stmt_select_by_name.Clear() + .Val_int_(dir_id) + .Val_str_(fil_name) + .Exec_select(); + if (rdr.MoveNextPeer()) + return load_(rdr); + else + return Fsdb_fil_itm.Null; + } + finally {rdr.Rls();} + } + private Db_stmt Select_by_id_stmt() { + Db_qry qry = Db_qry_.select_().From_(Tbl_name).Cols_all_().Where_(Db_crt_.eq_(Fld_fil_id, 0)); + return provider.Prepare(qry); + } + public Fsdb_fil_itm Select_itm_by_id(int fil_id) { + if (stmt_select_by_id == null) stmt_select_by_id = Select_by_id_stmt(); + DataRdr rdr = DataRdr_.Null; + try { + rdr = stmt_select_by_name.Clear() + .Val_int_(fil_id) + .Exec_select(); + if (rdr.MoveNextPeer()) + return load_(rdr); + else + return Fsdb_fil_itm.Null; + } + finally {rdr.Rls();} + } + private Fsdb_fil_itm load_(DataRdr rdr) { + return new Fsdb_fil_itm().Init(rdr.ReadInt(Fld_fil_id), rdr.ReadInt(Fld_fil_owner_id), rdr.ReadInt(Fld_fil_ext_id), rdr.ReadStr(Fld_fil_name), rdr.ReadInt(Fld_fil_bin_db_id)); + } + public static final String Tbl_name = "fsdb_fil", Fld_fil_id = "fil_id", Fld_fil_owner_id = "fil_owner_id", Fld_fil_name = "fil_name", Fld_fil_xtn_id = "fil_xtn_id", Fld_fil_ext_id = "fil_ext_id" + , Fld_fil_size = "fil_size", Fld_fil_modified = "fil_modified", Fld_fil_hash = "fil_hash", Fld_fil_bin_db_id = "fil_bin_db_id" + ; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_fil" + , "( fil_id integer NOT NULL PRIMARY KEY" + , ", fil_owner_id integer NOT NULL" + , ", fil_xtn_id integer NOT NULL" + , ", fil_ext_id integer NOT NULL" + , ", fil_bin_db_id integer NOT NULL" // group ints at beginning of table + , ", fil_name varchar(255) NOT NULL" + , ", fil_size bigint NOT NULL" + , ", fil_modified varchar(14) NOT NULL" // stored as yyyyMMddHHmmss + , ", fil_hash varchar(40) NOT NULL" + , ");" + ); + public static final Db_idx_itm +// Idx_name = Db_idx_itm.sql_ ("CREATE INDEX IF NOT EXISTS fsdb_fil__name ON fsdb_fil (fil_name, fil_owner_id, fil_id, fil_ext_id);") + Idx_owner = Db_idx_itm.sql_ ("CREATE INDEX IF NOT EXISTS fsdb_fil__owner ON fsdb_fil (fil_owner_id, fil_name, fil_id);") + ; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_mnt_itm.java b/400_xowa/src/gplx/fsdb/Fsdb_mnt_itm.java new file mode 100644 index 000000000..b8d8a65e5 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_mnt_itm.java @@ -0,0 +1,26 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_mnt_itm { + public Fsdb_mnt_itm(int id, String name, String url) {this.id = id; this.name = name; this.url = url;} + public int Id() {return id;} public Fsdb_mnt_itm Id_(int v) {id = v; return this;} private int id; + public String Name() {return name;} public Fsdb_mnt_itm Name_(String v) {name = v; return this;} private String name; + public String Url() {return url;} public Fsdb_mnt_itm Url_(String v) {url = v; return this;} private String url; + public static final Fsdb_mnt_itm Null = new Fsdb_mnt_itm(0, "", ""); + public static final Fsdb_mnt_itm[] Ary_empty = new Fsdb_mnt_itm[0]; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_mnt_mgr.java b/400_xowa/src/gplx/fsdb/Fsdb_mnt_mgr.java new file mode 100644 index 000000000..baa8b870c --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_mnt_mgr.java @@ -0,0 +1,119 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; import gplx.xowa.files.fsdb.*; +public class Fsdb_mnt_mgr implements GfoInvkAble { + private Db_provider provider; + private Fsdb_cfg_tbl tbl_cfg; + private Fsdb_db_abc_mgr[] ary; int ary_len = 0; + public Gfo_usr_dlg Usr_dlg() {return usr_dlg;} public Fsdb_mnt_mgr Usr_dlg_(Gfo_usr_dlg v) {usr_dlg = v; return this;} private Gfo_usr_dlg usr_dlg = Gfo_usr_dlg_.Null; + public void Init(Io_url cur_dir) { + Fsdb_mnt_itm[] mnts = Db_load_or_make(cur_dir); + ary_len = mnts.length; + ary = new Fsdb_db_abc_mgr[ary_len]; + for (int i = 0; i < ary_len; i++) { + Fsdb_mnt_itm itm = mnts[i]; + Io_url abc_url = cur_dir.GenSubFil_nest(itm.Url(), "fsdb.abc.sqlite3"); + ary[i] = new Fsdb_db_abc_mgr(this).Init(abc_url.OwnerDir()); + } + insert_to_mnt = tbl_cfg.Select_as_int_or_fail("core", "mnt.insert_idx"); + } + public int Insert_to_mnt() {return insert_to_mnt;} public Fsdb_mnt_mgr Insert_to_mnt_(int v) {insert_to_mnt = v; return this;} private int insert_to_mnt = Mnt_idx_user; + public int Abc_mgr_len() {return ary == null ? 0 : ary.length;} + public Fsdb_db_abc_mgr Abc_mgr_at(int i) {return ary[i];} + private Fsdb_mnt_itm[] Db_load_or_make(Io_url cur_dir) { + Bool_obj_ref created = Bool_obj_ref.n_(); + provider = Sqlite_engine_.Provider_load_or_make_(cur_dir.GenSubFil("wiki.mnt.sqlite3"), created); + tbl_cfg = new Fsdb_cfg_tbl_sql().Ctor(provider, created.Val()); + if (created.Val()) { + Fsdb_mnt_tbl.Create_table(provider); + Fsdb_mnt_tbl.Insert(provider, Mnt_idx_main, "fsdb.main", "fsdb.main"); + Fsdb_mnt_tbl.Insert(provider, Mnt_idx_user, "fsdb.user", "fsdb.user"); + tbl_cfg.Insert("core", "mnt.insert_idx", Int_.XtoStr(Mnt_idx_user)); + } + return Fsdb_mnt_tbl.Select_all(provider); + } + public Fsdb_db_bin_fil Bin_db_get(int mnt_id, int bin_db_id) { + return ary[mnt_id].Bin_mgr().Get_at(bin_db_id); + } + public Fsdb_fil_itm Fil_select_bin(byte[] dir, byte[] fil, boolean is_thumb, int width, double thumbtime) { + for (int i = 0; i < ary_len; i++) { + Fsdb_fil_itm rv = ary[i].Fil_select_bin(dir, fil, is_thumb, width, thumbtime); + if (rv != Fsdb_fil_itm.Null && rv.Db_bin_id() != Fsdb_bin_tbl.Null_db_bin_id) { // NOTE: mnt_0 can have thumb, but mnt_1 can have itm; check for itm with Db_bin_id; DATE:2013-11-16 + rv.Mnt_id_(i); + return rv; + } + } + return Fsdb_fil_itm.Null; + } + public boolean Thm_select_bin(byte[] dir, byte[] fil, Fsdb_xtn_thm_itm thm) { + for (int i = 0; i < ary_len; i++) { + boolean rv = ary[i].Thm_select_bin(dir, fil, thm); + if (rv) { + thm.Mnt_id_(i); + return rv; + } + } + return false; + } + public void Fil_insert(Fsdb_fil_itm rv , byte[] dir, byte[] fil, int ext_id, DateAdp modified, String hash, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + ary[insert_to_mnt].Fil_insert(rv, dir, fil, ext_id, modified, hash, bin_len, bin_rdr); + } + public void Thm_insert(Fsdb_xtn_thm_itm rv, byte[] dir, byte[] fil, int ext_id, int w, int h, double thumbtime, int page, DateAdp modified, String hash, long bin_len, gplx.ios.Io_stream_rdr bin_rdr) { + ary[insert_to_mnt].Thm_insert(rv, dir, fil, ext_id, w, h, thumbtime, page, modified, hash, bin_len, bin_rdr); + } + public void Img_insert(Fsdb_xtn_img_itm rv, byte[] dir, byte[] fil, int ext_id, DateAdp modified, String hash, long bin_len, gplx.ios.Io_stream_rdr bin_rdr, int img_w, int img_h) { + ary[insert_to_mnt].Img_insert(rv, dir, fil, ext_id, modified, hash, bin_len, bin_rdr, img_w, img_h); + } + public void Bin_db_max_(long v) { + for (int i = 0; i < ary_len; i++) + ary[i].Bin_mgr().Db_bin_max_(v); + } + public void Insert_to_bin_(int v) { + for (int i = 0; i < ary_len; i++) + ary[i].Bin_mgr().Insert_to_bin_(v); + } + public void Txn_open() { + for (int i = 0; i < ary_len; i++) + ary[i].Txn_open(); + } + public void Txn_save() { + for (int i = 0; i < ary_len; i++) + ary[i].Txn_save(); + } + public void Rls() { + for (int i = 0; i < ary_len; i++) + ary[i].Rls(); + tbl_cfg.Rls(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_bin_db_max_in_mb_)) this.Bin_db_max_(m.ReadLong("v") * Io_mgr.Len_mb); + else if (ctx.Match(k, Invk_insert_to_mnt_)) insert_to_mnt = m.ReadInt("v"); + else if (ctx.Match(k, Invk_insert_to_bin_)) this.Insert_to_bin_(m.ReadInt("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } private static final String Invk_bin_db_max_in_mb_ = "bin_db_max_in_mb_", Invk_insert_to_mnt_ = "insert_to_mnt_", Invk_insert_to_bin_ = "insert_to_bin_"; + public static final int Mnt_idx_main = 0, Mnt_idx_user = 1, Insert_to_bin_null = -1; + public static void Patch(Fsdb_mnt_mgr mnt_mgr) { + mnt_mgr.Abc_mgr_at(Fsdb_mnt_mgr.Mnt_idx_main).Cfg_mgr() + .Update(Xof_fsdb_mgr_cfg.Grp_xowa, Xof_fsdb_mgr_cfg.Key_gallery_fix_defaults, "y") + .Update(Xof_fsdb_mgr_cfg.Grp_xowa, Xof_fsdb_mgr_cfg.Key_gallery_packed, "y") + .Update(Xof_fsdb_mgr_cfg.Grp_xowa, Xof_fsdb_mgr_cfg.Key_upright_patch, "y") + ; + } +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_mnt_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_mnt_tbl.java new file mode 100644 index 000000000..652d07bd2 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_mnt_tbl.java @@ -0,0 +1,67 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_mnt_tbl { + public static void Create_table(Db_provider p) { + Sqlite_engine_.Tbl_create(p, Tbl_name, Tbl_sql); + } + public static void Insert(Db_provider p, int id, String name, String url) { + Db_stmt stmt = Insert_stmt(p); + try {Insert(stmt, id, name, url);} + finally {stmt.Rls();} + } + private static Db_stmt Insert_stmt(Db_provider p) {return Db_stmt_.new_insert_(p, Tbl_name, Fld_mnt_id, Fld_mnt_name, Fld_mnt_url);} + private static void Insert(Db_stmt stmt, int id, String name, String url) { + stmt.Clear() + .Val_int_(id) + .Val_str_(name) + .Val_str_(url) + .Exec_insert(); + } + public static Db_stmt Update_stmt(Db_provider p) {return Db_stmt_.new_update_(p, Tbl_name, String_.Ary(Fld_mnt_id), Fld_mnt_name, Fld_mnt_url);} + public static void Update(Db_stmt stmt, int id, String name, String url) { + stmt.Clear() + .Val_str_(name) + .Val_str_(url) + .Val_int_(id) + .Exec_update(); + } + public static Fsdb_mnt_itm[] Select_all(Db_provider p) { + Db_qry qry = Db_qry_.select_().From_(Tbl_name); + DataRdr rdr = DataRdr_.Null; + ListAdp list = ListAdp_.new_(); + try { + rdr = p.Exec_qry_as_rdr(qry); + while (rdr.MoveNextPeer()) { + Fsdb_mnt_itm itm = new Fsdb_mnt_itm(rdr.ReadInt(Fld_mnt_id), rdr.ReadStr(Fld_mnt_name), rdr.ReadStr(Fld_mnt_url)); + list.Add(itm); + } + } + finally {rdr.Rls();} + return (Fsdb_mnt_itm[])list.XtoAryAndClear(Fsdb_mnt_itm.class); + } + public static final String Tbl_name = "fsdb_mnt", Fld_mnt_id = "mnt_id", Fld_mnt_name = "mnt_name", Fld_mnt_url = "mnt_url"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_mnt" + , "( mnt_id integer NOT NULL PRIMARY KEY" + , ", mnt_name varchar(255) NOT NULL" + , ", mnt_url varchar(255) NOT NULL" + , ");" + ); +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_xtn_img_itm.java b/400_xowa/src/gplx/fsdb/Fsdb_xtn_img_itm.java new file mode 100644 index 000000000..7f235b043 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_xtn_img_itm.java @@ -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 . +*/ +package gplx.fsdb; import gplx.*; +public class Fsdb_xtn_img_itm { + public int Id() {return id;} public void Id_(int v) {this.id = v;} private int id; + public int W() {return w;} public void W_(int v) {this.w = v;} private int w; + public int H() {return h;} public void H_(int v) {this.h = v;} private int h; + public int Db_bin_id() {return bin_db_id;} public Fsdb_xtn_img_itm Db_bin_id_(int v) {bin_db_id = v; return this;} private int bin_db_id; + public Fsdb_xtn_img_itm Init_by_load(int id, int w, int h) {this.id = id; this.w = w; this.h = h; return this;} + public static final Fsdb_xtn_img_itm Null = new Fsdb_xtn_img_itm(); + public static final int Bits_default = 8; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_xtn_img_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_xtn_img_tbl.java new file mode 100644 index 000000000..a5d9618f5 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_xtn_img_tbl.java @@ -0,0 +1,68 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_xtn_img_tbl { + private Db_provider provider; + private Db_stmt stmt_insert, stmt_select_by_id; + public Fsdb_xtn_img_tbl(Db_provider provider, boolean created) { + this.provider = provider; + if (created) Create_table(); + } + public void Rls() { + if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = null;} + if (stmt_select_by_id != null) {stmt_select_by_id.Rls(); stmt_select_by_id = null;} + } + public void Create_table() { + Sqlite_engine_.Tbl_create(provider, Tbl_name, Tbl_sql); + } + public Db_stmt Insert_stmt() {return Db_stmt_.new_insert_(provider, Tbl_name, Fld_img_id, Fld_img_w, Fld_img_h);} + public void Insert(int id, int w, int h) { + if (stmt_insert == null) stmt_insert = Insert_stmt(); + try { + stmt_insert.Clear() + .Val_int_(id) + .Val_int_(w) + .Val_int_(h) + .Exec_insert(); + } catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail + } + public Db_stmt Select_itm_by_id_stmt() {return Db_stmt_.new_select_(provider, Tbl_name, String_.Ary(Fld_img_id), Fld_img_w, Fld_img_h); } + public Fsdb_xtn_img_itm Select_itm_by_id(int id) { + if (stmt_select_by_id == null) stmt_select_by_id = this.Select_itm_by_id_stmt(); + DataRdr rdr = DataRdr_.Null; + try { + rdr = stmt_select_by_id.Clear() + .Val_int_(id) + .Exec_select(); + if (rdr.MoveNextPeer()) + return new Fsdb_xtn_img_itm().Init_by_load(id, rdr.ReadInt(Fld_img_w), rdr.ReadInt(Fld_img_h)); + else + return Fsdb_xtn_img_itm.Null; + } + finally {rdr.Rls();} + } + public static final String Tbl_name = "fsdb_xtn_img", Fld_img_id = "img_id", Fld_img_w = "img_w", Fld_img_h = "img_h"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_xtn_img" + , "( img_id integer NOT NULL PRIMARY KEY" + , ", img_w integer NOT NULL" + , ", img_h integer NOT NULL" + , ");" + ); +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_xtn_thm_itm.java b/400_xowa/src/gplx/fsdb/Fsdb_xtn_thm_itm.java new file mode 100644 index 000000000..bf15e423d --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_xtn_thm_itm.java @@ -0,0 +1,56 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_xtn_thm_itm { + protected Fsdb_xtn_thm_itm() {} + public int Id() {return id;} public Fsdb_xtn_thm_itm Id_(int v) {id = v; return this;} private int id; + public Fsdb_fil_itm Owner() {return owner;} public Fsdb_xtn_thm_itm Owner_(Fsdb_fil_itm v) {owner = v; return this;} private Fsdb_fil_itm owner = Fsdb_fil_itm.Null; + public int Owner_id() {return owner_id;} public Fsdb_xtn_thm_itm Owner_id_(int v) {owner_id = v; return this;} private int owner_id; + public int Width() {return width;} public Fsdb_xtn_thm_itm Width_(int v) {width = v; return this;} private int width; + public double Thumbtime() {return thumbtime;} public Fsdb_xtn_thm_itm Thumbtime_(double v) {thumbtime = v; return this;} private double thumbtime; + public int Page() {return page;} public Fsdb_xtn_thm_itm Page_(int v) {page = v; return this;} private int page; + public int Height() {return height;} public Fsdb_xtn_thm_itm Height_(int v) {height = v; return this;} private int height; + public long Size() {return size;} public Fsdb_xtn_thm_itm Size_(long v) {size = v; return this;} private long size; + public String Modified() {return modified;} public Fsdb_xtn_thm_itm Modified_(String v) {modified = v; return this;} private String modified; + public String Hash() {return hash;} public Fsdb_xtn_thm_itm Hash_(String v) {hash = v; return this;} private String hash; + public int Dir_id() {return dir_id;} public Fsdb_xtn_thm_itm Dir_id_(int v) {dir_id = v; return this;} private int dir_id; + public int Db_bin_id() {return bin_db_id;} public Fsdb_xtn_thm_itm Db_bin_id_(int v) {bin_db_id = v; return this;} private int bin_db_id; + public int Mnt_id() {return mnt_id;} public Fsdb_xtn_thm_itm Mnt_id_(int v) {mnt_id = v; return this;} private int mnt_id; + public void Init_by_load(DataRdr rdr, boolean schema_thm_page) { + this.id = rdr.ReadInt(Fsdb_xtn_thm_tbl.Fld_thm_id); + this.owner_id = rdr.ReadInt(Fsdb_xtn_thm_tbl.Fld_thm_owner_id); + this.width = rdr.ReadInt(Fsdb_xtn_thm_tbl.Fld_thm_w); + this.height = rdr.ReadInt(Fsdb_xtn_thm_tbl.Fld_thm_h); + this.size = rdr.ReadLong(Fsdb_xtn_thm_tbl.Fld_thm_size); + this.modified = rdr.ReadStr(Fsdb_xtn_thm_tbl.Fld_thm_modified); + this.hash = rdr.ReadStr(Fsdb_xtn_thm_tbl.Fld_thm_hash); + this.bin_db_id = rdr.ReadInt(Fsdb_xtn_thm_tbl.Fld_thm_bin_db_id); + if (schema_thm_page) { + this.thumbtime = gplx.xowa.files.Xof_doc_thumb.Db_load_double(rdr, Fsdb_xtn_thm_tbl.Fld_thm_time); + this.page = gplx.xowa.files.Xof_doc_page.Db_load_int(rdr, Fsdb_xtn_thm_tbl.Fld_thm_page); + } + else { + this.thumbtime = gplx.xowa.files.Xof_doc_thumb.Db_load_int(rdr, Fsdb_xtn_thm_tbl.Fld_thm_thumbtime); + this.page = gplx.xowa.files.Xof_doc_page.Null; + } + } + public static Fsdb_xtn_thm_itm new_() {return new Fsdb_xtn_thm_itm();} // NOTE: Owner is null by default + public static Fsdb_xtn_thm_itm load_() {return new Fsdb_xtn_thm_itm().Owner_(new Fsdb_fil_itm());} // NOTE: Owner is new'd b/c load will use owner.Ext_id + public static final Fsdb_xtn_thm_itm Null = new Fsdb_xtn_thm_itm(); + public static final Fsdb_xtn_thm_itm[] Ary_empty = new Fsdb_xtn_thm_itm[0]; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_xtn_thm_tbl.java b/400_xowa/src/gplx/fsdb/Fsdb_xtn_thm_tbl.java new file mode 100644 index 000000000..b5647a853 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_xtn_thm_tbl.java @@ -0,0 +1,128 @@ +/* +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.fsdb; import gplx.*; +import gplx.dbs.*; +public class Fsdb_xtn_thm_tbl { + private Fsdb_db_atr_fil atr_fil; private Db_provider provider; + private Db_stmt stmt_insert, stmt_select_by_fil_w; + public Fsdb_xtn_thm_tbl(Fsdb_db_atr_fil atr_fil, Db_provider provider, boolean created) { + this.atr_fil = atr_fil; this.provider = provider; + if (created) Create_table(); + } + private void Create_table() { + Sqlite_engine_.Tbl_create(provider, Tbl_name, Tbl_sql); + Sqlite_engine_.Idx_create(provider, Idx_name); + } + public void Rls() { + if (stmt_insert != null) {stmt_insert.Rls(); stmt_insert = null;} + if (stmt_select_by_fil_w != null) {stmt_select_by_fil_w.Rls(); stmt_select_by_fil_w = null;} + } + private boolean Schema_thm_page() { + if (schema_thm_page_init) { + schema_thm_page = atr_fil.Abc_mgr().Cfg_mgr().Schema_thm_page(); + schema_thm_page_init = false; + } + return schema_thm_page; + } private boolean schema_thm_page, schema_thm_page_init = true; + private Db_stmt Make_stmt_insert() { + return this.Schema_thm_page() + ? Db_stmt_.new_insert_(provider, Tbl_name, Fld_thm_id, Fld_thm_owner_id, Fld_thm_w, Fld_thm_h, Fld_thm_bin_db_id, Fld_thm_size, Fld_thm_modified, Fld_thm_hash, Fld_thm_time, Fld_thm_page) + : Db_stmt_.new_insert_(provider, Tbl_name, Fld_thm_id, Fld_thm_owner_id, Fld_thm_w, Fld_thm_h, Fld_thm_bin_db_id, Fld_thm_size, Fld_thm_modified, Fld_thm_hash, Fld_thm_thumbtime) + ; + } + public void Insert(int id, int thm_owner_id, int width, int height, double thumbtime, int page, int bin_db_id, long size, DateAdp modified, String hash) { + if (stmt_insert == null) stmt_insert = Make_stmt_insert(); + try { + stmt_insert.Clear() + .Val_int_(id) + .Val_int_(thm_owner_id) + .Val_int_(width) + .Val_int_(height) + .Val_int_(bin_db_id) + .Val_long_(size) + .Val_str_(Sqlite_engine_.X_date_to_str(modified)) + .Val_str_(hash); + if (this.Schema_thm_page()) { + stmt_insert.Val_double_ (gplx.xowa.files.Xof_doc_thumb.Db_save_double(thumbtime)); + stmt_insert.Val_int_ (gplx.xowa.files.Xof_doc_page.Db_save_int(page)); + } + else + stmt_insert.Val_int_(gplx.xowa.files.Xof_doc_thumb.Db_save_int(thumbtime)); + stmt_insert.Exec_insert(); + } catch (Exception exc) {stmt_insert = null; throw Err_.err_(exc, "stmt failed");} // must reset stmt, else next call will fail + } + private Db_stmt Select_by_fil_w_stmt() { + Db_qry_select qry = Db_qry_.select_().From_(Tbl_name).Cols_all_(); + gplx.criterias.Criteria crt + = this.Schema_thm_page() + ? gplx.criterias.Criteria_.And_many(Db_crt_.eq_(Fld_thm_owner_id, Int_.MinValue), Db_crt_.eq_(Fld_thm_w, Int_.MinValue), Db_crt_.eq_(Fld_thm_time, Int_.MinValue), Db_crt_.eq_(Fld_thm_page, Int_.MinValue)) + : gplx.criterias.Criteria_.And_many(Db_crt_.eq_(Fld_thm_owner_id, Int_.MinValue), Db_crt_.eq_(Fld_thm_w, Int_.MinValue), Db_crt_.eq_(Fld_thm_thumbtime, Int_.MinValue)) + ; + qry.Where_(crt); + return provider.Prepare(qry); + } + public boolean Select_itm_by_fil_width(int owner_id, Fsdb_xtn_thm_itm thm) { + if (stmt_select_by_fil_w == null) stmt_select_by_fil_w = Select_by_fil_w_stmt(); + DataRdr rdr = DataRdr_.Null; + try { + stmt_select_by_fil_w.Clear() + .Val_int_(owner_id) + .Val_int_(thm.Width()) + ; + if (this.Schema_thm_page()) { + stmt_select_by_fil_w.Val_double_(gplx.xowa.files.Xof_doc_thumb.Db_save_double(thm.Thumbtime())); + stmt_select_by_fil_w.Val_int_(gplx.xowa.files.Xof_doc_page.Db_save_int(thm.Page())); + } + else { + stmt_select_by_fil_w.Val_int_(gplx.xowa.files.Xof_doc_thumb.Db_save_int(thm.Thumbtime())); + } + rdr = stmt_select_by_fil_w.Exec_select(); + if (rdr.MoveNextPeer()) { + thm.Init_by_load(rdr, this.Schema_thm_page()); + return true; + } + else + return false; + } + finally {rdr.Rls();} + } + public static final String Tbl_name = "fsdb_xtn_thm" + , Fld_thm_id = "thm_id", Fld_thm_owner_id = "thm_owner_id", Fld_thm_w = "thm_w", Fld_thm_h = "thm_h" + , Fld_thm_thumbtime = "thm_thumbtime", Fld_thm_time = "thm_time", Fld_thm_page = "thm_page" + , Fld_thm_bin_db_id = "thm_bin_db_id", Fld_thm_size = "thm_size", Fld_thm_modified = "thm_modified", Fld_thm_hash = "thm_hash"; + private static final String Tbl_sql = String_.Concat_lines_nl + ( "CREATE TABLE IF NOT EXISTS fsdb_xtn_thm" + , "( thm_id integer NOT NULL PRIMARY KEY" + , ", thm_owner_id integer NOT NULL" + , ", thm_w integer NOT NULL" + , ", thm_h integer NOT NULL" + //, ", thm_thumbtime integer NOT NULL" // removed; DATE:2014-01-23 + , ", thm_time double NOT NULL" // replacement for thm_time + , ", thm_page integer NOT NULL" + , ", thm_bin_db_id integer NOT NULL" + , ", thm_size bigint NOT NULL" + , ", thm_modified varchar(14) NOT NULL" // stored as yyyyMMddHHmmss + , ", thm_hash varchar(40) NOT NULL" + , ");" + ); + public static final Db_idx_itm + Idx_name = Db_idx_itm.sql_("CREATE INDEX IF NOT EXISTS fsdb_xtn_thm__owner ON fsdb_xtn_thm (thm_owner_id, thm_id, thm_w, thm_time, thm_page);") + ; + public static final DateAdp Modified_null = null; + public static final String Hash_null = ""; +} diff --git a/400_xowa/src/gplx/fsdb/Fsdb_xtn_tid_.java b/400_xowa/src/gplx/fsdb/Fsdb_xtn_tid_.java new file mode 100644 index 000000000..dc9cf7bd3 --- /dev/null +++ b/400_xowa/src/gplx/fsdb/Fsdb_xtn_tid_.java @@ -0,0 +1,21 @@ +/* +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.fsdb; import gplx.*; +public class Fsdb_xtn_tid_ { + public static final int Tid_none = 0, Tid_thm = 1, Tid_img = 2; +} diff --git a/400_xowa/src/gplx/gfs/Gfs_lxr.java b/400_xowa/src/gplx/gfs/Gfs_lxr.java new file mode 100644 index 000000000..95c543af1 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_lxr.java @@ -0,0 +1,214 @@ +/* +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.gfs; import gplx.*; +interface Gfs_lxr { + byte Lxr_tid(); + int Process(Gfs_parser_ctx ctx, int bgn, int end); +} +class Gfs_lxr_whitespace implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_whitespace;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + byte[] src = ctx.Src(); int src_len = ctx.Src_len(); + int rv = Gfs_lxr_.Rv_eos, cur_pos; + for (cur_pos = end; cur_pos < src_len; cur_pos++) { + byte b = src[cur_pos]; + Object o = ctx.Trie().Match(b, src, cur_pos, src_len); + if (o == null) { + rv = Gfs_lxr_.Rv_null; + ctx.Process_null(cur_pos); + break; + } + else { + Gfs_lxr lxr = (Gfs_lxr)o; + if (lxr.Lxr_tid() == Gfs_lxr_.Tid_whitespace) {} + else { + rv = Gfs_lxr_.Rv_lxr; + ctx.Process_lxr(cur_pos, lxr); + break; + } + } + } + return rv; + } + public static final Gfs_lxr_whitespace _ = new Gfs_lxr_whitespace(); Gfs_lxr_whitespace() {} +} +class Gfs_lxr_comment_flat implements Gfs_lxr { + public Gfs_lxr_comment_flat(byte[] bgn_bry, byte[] end_bry) { + this.bgn_bry = bgn_bry; this.bgn_bry_len = bgn_bry.length; + this.end_bry = end_bry; this.end_bry_len = end_bry.length; + } byte[] bgn_bry, end_bry; int bgn_bry_len, end_bry_len; + public byte Lxr_tid() {return Gfs_lxr_.Tid_comment;} + public int Process(Gfs_parser_ctx ctx, int lxr_bgn, int lxr_end) { + byte[] src = ctx.Src(); int src_len = ctx.Src_len(); + int end_pos = Bry_finder.Find_fwd(src, end_bry, lxr_end, src_len); + // if (end_pos == Bry_.NotFound) throw Err_.new_fmt_("comment is not closed: {0}", String_.new_utf8_(end_bry)); + return (end_pos == Bry_.NotFound) + ? src_len // allow eos to terminate flat comment; needed for "tidy-always-adds-nl-in-textarea" fix; NOTE: DATE:2014-06-21 + : end_pos + end_bry_len; // position after end_bry + } +} +class Gfs_lxr_identifier implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_identifier;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + byte[] src = ctx.Src(); int src_len = ctx.Src_len(); + int pos, rv = Gfs_lxr_.Rv_eos; + for (pos = end; pos < src_len; pos++) { + byte b = src[pos]; + Object o = ctx.Trie().Match(b, src, pos, src_len); + if (o == null) { // invalid char; stop; + rv = Gfs_lxr_.Rv_null; + ctx.Process_null(pos); + break; + } + else { + Gfs_lxr lxr = (Gfs_lxr)o; + if (lxr.Lxr_tid() == Gfs_lxr_.Tid_identifier) {} // still an identifier; continue + else { // new lxr (EX: "." in "abc."); (a) hold word of "abc"; mark "." as new lxr; + ctx.Hold_word(bgn, pos); + rv = Gfs_lxr_.Rv_lxr; + ctx.Process_lxr(pos, lxr); + break; + } + } + } + if (rv == Gfs_lxr_.Rv_eos) ctx.Process_eos(); // eos + return rv; + } + public static final Gfs_lxr_identifier _ = new Gfs_lxr_identifier(); Gfs_lxr_identifier() {} +} +class Gfs_lxr_semic implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_semic;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_identifier: ctx.Make_nde(bgn, end); ctx.Cur_nde_from_stack(); break; // a; + case Gfs_lxr_.Tid_quote: + case Gfs_lxr_.Tid_paren_end: ctx.Cur_nde_from_stack(); break; // a(); + case Gfs_lxr_.Tid_semic: break; // a;; ignore; + default: ctx.Err_mgr().Fail_invalid_lxr(ctx, bgn, this.Lxr_tid(), Byte_ascii.Semic); break; + } + return end; + } + public static final Gfs_lxr_semic _ = new Gfs_lxr_semic(); Gfs_lxr_semic() {} +} +class Gfs_lxr_dot implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_dot;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_identifier: ctx.Make_nde(bgn, end); break; // a. + case Gfs_lxr_.Tid_paren_end: break; // a(). + default: ctx.Err_mgr().Fail_invalid_lxr(ctx, bgn, this.Lxr_tid(), Byte_ascii.Dot); break; + } + return end; + } + public static final Gfs_lxr_dot _ = new Gfs_lxr_dot(); Gfs_lxr_dot() {} +} +class Gfs_lxr_paren_bgn implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_paren_bgn;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_identifier: ctx.Make_nde(bgn, end); break; // a(; + default: ctx.Err_mgr().Fail_invalid_lxr(ctx, bgn, this.Lxr_tid(), Byte_ascii.Paren_bgn); break; + } + return end; + } + public static final Gfs_lxr_paren_bgn _ = new Gfs_lxr_paren_bgn(); Gfs_lxr_paren_bgn() {} +} +class Gfs_lxr_paren_end implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_paren_end;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_paren_bgn: + case Gfs_lxr_.Tid_quote: break; // "))", "abc)", "'abc')" + case Gfs_lxr_.Tid_identifier: ctx.Make_atr_by_idf(); break; // 123) + default: ctx.Err_mgr().Fail_invalid_lxr(ctx, bgn, this.Lxr_tid(), Byte_ascii.Paren_end); break; + } + return end; + } + public static final Gfs_lxr_paren_end _ = new Gfs_lxr_paren_end(); Gfs_lxr_paren_end() {} +} +class Gfs_lxr_quote implements Gfs_lxr { + public Gfs_lxr_quote(byte[] bgn_bry, byte[] end_bry) { + this.bgn_bry_len = bgn_bry.length; + this.end_bry = end_bry; this.end_bry_len = end_bry.length; + } private byte[] end_bry; private int bgn_bry_len, end_bry_len; + public byte Lxr_tid() {return Gfs_lxr_.Tid_quote;} + public int Process(Gfs_parser_ctx ctx, int lxr_bgn, int lxr_end) { + byte[] src = ctx.Src(); int src_len = ctx.Src_len(); + int end_pos = Bry_finder.Find_fwd(src, end_bry, lxr_end, src_len); + if (end_pos == Bry_.NotFound) throw Err_.new_fmt_("quote is not closed: {0}", String_.new_utf8_(end_bry)); + Bry_bfr bfr = ctx.Tmp_bfr().Clear(); + int prv_pos = lxr_end; + int nxt_pos = end_pos + end_bry_len; + if (Bry_.Match(src, nxt_pos, nxt_pos + end_bry_len, end_bry)) { // end_bry is doubled; EX: end_bry = ' and raw = a'' + while (true) { + bfr.Add_mid(src, prv_pos, end_pos); // add everything up to end_bry + bfr.Add(end_bry); // add end_bry + prv_pos = nxt_pos + end_bry_len; // set prv_pos to after doubled end_bry + end_pos = Bry_finder.Find_fwd(src, end_bry, prv_pos, src_len); + if (end_pos == Bry_.NotFound) throw Err_.new_fmt_("quote is not closed: {0}", String_.new_utf8_(end_bry)); + nxt_pos = end_pos + end_bry_len; + if (!Bry_.Match(src, nxt_pos, nxt_pos + end_bry_len, end_bry)) { + bfr.Add_mid(src, prv_pos, end_pos); + break; + } + } + ctx.Make_atr_by_bry(lxr_bgn + bgn_bry_len, end_pos, bfr.XtoAryAndClear()); + } + else + ctx.Make_atr(lxr_bgn + bgn_bry_len, end_pos); + return end_pos + end_bry_len; // position after quote + } +} +class Gfs_lxr_curly_bgn implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_curly_bgn;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_identifier: ctx.Make_nde(bgn, end); ctx.Stack_add(); break; // a{; + case Gfs_lxr_.Tid_paren_end: ctx.Stack_add(); break; // a(){; NOTE: node exists but needs to be pushed onto stack + default: ctx.Err_mgr().Fail_invalid_lxr(ctx, bgn, this.Lxr_tid(), Byte_ascii.Curly_bgn); break; + } + return end; + } + public static final Gfs_lxr_curly_bgn _ = new Gfs_lxr_curly_bgn(); Gfs_lxr_curly_bgn() {} +} +class Gfs_lxr_curly_end implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_curly_end;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + ctx.Stack_pop(bgn); + return end; + } + public static final Gfs_lxr_curly_end _ = new Gfs_lxr_curly_end(); Gfs_lxr_curly_end() {} +} +class Gfs_lxr_equal implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_eq;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + ctx.Make_nde(bgn, end).Op_tid_(Gfs_nde.Op_tid_assign); + return end; + } + public static final Gfs_lxr_equal _ = new Gfs_lxr_equal(); Gfs_lxr_equal() {} +} +class Gfs_lxr_comma implements Gfs_lxr { + public byte Lxr_tid() {return Gfs_lxr_.Tid_comma;} + public int Process(Gfs_parser_ctx ctx, int bgn, int end) { + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_identifier: ctx.Make_atr_by_idf(); break; // 123, + } + return end; + } + public static final Gfs_lxr_comma _ = new Gfs_lxr_comma(); Gfs_lxr_comma() {} +} diff --git a/400_xowa/src/gplx/gfs/Gfs_lxr_.java b/400_xowa/src/gplx/gfs/Gfs_lxr_.java new file mode 100644 index 000000000..ec463a001 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_lxr_.java @@ -0,0 +1,39 @@ +/* +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.gfs; import gplx.*; +class Gfs_lxr_ { + public static final int Rv_init = -1, Rv_null = -2, Rv_eos = -3, Rv_lxr = -4; + public static final byte Tid_identifier = 1, Tid_dot = 2, Tid_semic = 3, Tid_paren_bgn = 4, Tid_paren_end = 5, Tid_curly_bgn = 6, Tid_curly_end = 7, Tid_quote = 8, Tid_comma = 9, Tid_whitespace = 10, Tid_comment = 11, Tid_eq = 12; + public static String Tid__name(byte tid) { + switch (tid) { + case Tid_identifier: return "identifier"; + case Tid_dot: return "dot"; + case Tid_semic: return "semic"; + case Tid_paren_bgn: return "paren_bgn"; + case Tid_paren_end: return "paren_end"; + case Tid_curly_bgn: return "curly_bgn"; + case Tid_curly_end: return "curly_end"; + case Tid_quote: return "quote"; + case Tid_comma: return "comma"; + case Tid_whitespace: return "whitespace"; + case Tid_comment: return "comment"; + case Tid_eq: return "eq"; + default: throw Err_.unhandled(tid); + } + } +} diff --git a/400_xowa/src/gplx/gfs/Gfs_msg_bldr.java b/400_xowa/src/gplx/gfs/Gfs_msg_bldr.java new file mode 100644 index 000000000..ae0c3788c --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_msg_bldr.java @@ -0,0 +1,49 @@ +/* +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.gfs; import gplx.*; +public class Gfs_msg_bldr implements GfoMsgParser { + Gfs_parser parser = new Gfs_parser(); + public GfoMsg ParseToMsg(String s) {return Bld(s);} + public GfoMsg Bld(String src) {return Bld(Bry_.new_utf8_(src));} + public GfoMsg Bld(byte[] src) { + Gfs_nde nde = parser.Parse(src); + return Bld_msg(src, nde); + } + GfoMsg Bld_msg(byte[] src, Gfs_nde nde) { + boolean op_is_assign = (nde.Op_tid() == Gfs_nde.Op_tid_assign); + String name = String_.new_utf8_(nde.Name_bry(src)); + if (op_is_assign) name += Tkn_mutator; + GfoMsg rv = GfoMsg_.new_parse_(name); + int len = nde.Atrs_len(); + for (int i = 0; i < len; i++) { + Gfs_nde atr = nde.Atrs_get_at(i); + rv.Add("", String_.new_utf8_(atr.Name_bry(src))); + } + len = nde.Subs_len(); + for (int i = 0; i < len; i++) { + Gfs_nde sub = nde.Subs_get_at(i); + if (op_is_assign) // NOTE: for now (a) assignss cannot be nested; EX: "a.b = c;" is okay but "a.b = c.d;" is not + rv.Add("", Bld_msg(src, sub).Key()); + else + rv.Subs_add(Bld_msg(src, sub)); + } + return rv; + } + public static final Gfs_msg_bldr _ = new Gfs_msg_bldr(); Gfs_msg_bldr() {} + public static final String Tkn_mutator = "_"; +} diff --git a/400_xowa/src/gplx/gfs/Gfs_msg_bldr_tst.java b/400_xowa/src/gplx/gfs/Gfs_msg_bldr_tst.java new file mode 100644 index 000000000..9ec4ff8d6 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_msg_bldr_tst.java @@ -0,0 +1,76 @@ +/* +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.gfs; import gplx.*; +import org.junit.*; +public class Gfs_msg_bldr_tst { + @Before public void init() {fxt.Clear();} Gfs_msg_bldr_fxt fxt = new Gfs_msg_bldr_fxt(); + @Test public void Basic() { + fxt.Test_build("a;", fxt.msg_("a")); + } + @Test public void Dot() { + fxt.Test_build("a.b.c;" + , fxt.msg_("a").Subs_ + ( fxt.msg_("b").Subs_ + ( fxt.msg_("c") + ))); + } + @Test public void Args() { + fxt.Test_build("a('b', 'c');", fxt.msg_("a", fxt.kv_("", "b"), fxt.kv_("", "c"))); + } + @Test public void Args_num() { + fxt.Test_build("a(1);", fxt.msg_("a", fxt.kv_("", "1"))); + } + @Test public void Assign() { + fxt.Test_build("a = 'b';", fxt.msg_("a_", fxt.kv_("", "b"))); + } + @Test public void Assign_num() { + fxt.Test_build("a = 1;", fxt.msg_("a_", fxt.kv_("", "1"))); + } +} +class Gfs_msg_bldr_fxt { + public void Clear() {} String_bldr sb = String_bldr_.new_(); Gfs_msg_bldr msg_bldr = Gfs_msg_bldr._; + public KeyVal kv_(String key, String val) {return KeyVal_.new_(key, val);} + public GfoMsg msg_(String key, KeyVal... args) { + GfoMsg rv = GfoMsg_.new_parse_(key); + int len = args.length; + for (int i = 0; i < len; i++) { + KeyVal kv = args[i]; + rv.Add(kv.Key(), kv.Val()); + } + return rv; + } + public void Test_build(String raw, GfoMsg... expd) { + GfoMsg root = msg_bldr.Bld(raw); + Tfds.Eq_str_lines(Xto_str(expd), Xto_str(Xto_ary(root))); + } + GfoMsg[] Xto_ary(GfoMsg msg) { + int len = msg.Subs_count(); + GfoMsg[] rv = new GfoMsg[len]; + for (int i = 0; i < len; i++) + rv[i] = msg.Subs_getAt(i); + return rv; + } + String Xto_str(GfoMsg[] ary) { + int len = ary.length; + for (int i = 0; i < len; i++) { + if (i != 0) sb.Add_char_crlf(); + sb.Add(ary[i].XtoStr()); + } + return sb.XtoStrAndClear(); + } +} diff --git a/400_xowa/src/gplx/gfs/Gfs_nde.java b/400_xowa/src/gplx/gfs/Gfs_nde.java new file mode 100644 index 000000000..9ec6df282 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_nde.java @@ -0,0 +1,85 @@ +/* +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.gfs; import gplx.*; +public class Gfs_nde { + public byte[] Name_bry(byte[] src) {return name == null ? Bry_.Mid(src, name_bgn, name_end) : name;} + public byte[] Name() {return name;} public Gfs_nde Name_(byte[] v) {name = v; return this;} private byte[] name; + public int Name_bgn() {return name_bgn;} private int name_bgn = -1; + public int Name_end() {return name_end;} private int name_end = -1; + public Gfs_nde Name_rng_(int name_bgn, int name_end) {this.name_bgn = name_bgn; this.name_end = name_end; return this;} + public byte Op_tid() {return op_tid;} public Gfs_nde Op_tid_(byte v) {op_tid = v; return this;} private byte op_tid; + public void Subs_clear() { + for (int i = 0; i < subs_len; i++) + subs[i] = null; + subs_len = 0; + } + public int Subs_len() {return subs_len;} private int subs_len; + public Gfs_nde Subs_add_many(Gfs_nde... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Subs_add(ary[i]); + return this; + } + public Gfs_nde Subs_add(Gfs_nde nde) { + int new_len = subs_len + 1; + if (new_len > subs_max) { // ary too small >>> expand + subs_max = new_len * 2; + Gfs_nde[] new_subs = new Gfs_nde[subs_max]; + Array_.CopyTo(subs, 0, new_subs, 0, subs_len); + subs = new_subs; + } + subs[subs_len] = nde; + subs_len = new_len; + return this; + } Gfs_nde[] subs = Gfs_nde.Ary_empty; int subs_max; int[] subs_pos_ary = Int_.Ary_empty; + public Gfs_nde Subs_get_at(int i) {return subs[i];} + public Gfs_nde[] Subs_to_ary() { + Gfs_nde[] rv = new Gfs_nde[subs_len]; + for (int i = 0; i < subs_len; i++) + rv[i] = subs[i]; + return rv; + } + public int Atrs_len() {return args_len;} private int args_len; + public Gfs_nde Atrs_get_at(int i) {return args[i];} + public Gfs_nde Atrs_add_many(Gfs_nde... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Atrs_add(ary[i]); + return this; + } + public Gfs_nde Atrs_add(Gfs_nde nde) { + int new_len = args_len + 1; + if (new_len > args_max) { // ary too small >>> expand + args_max = new_len * 2; + Gfs_nde[] new_args = new Gfs_nde[args_max]; + Array_.CopyTo(args, 0, new_args, 0, args_len); + args = new_args; + } + args[args_len] = nde; + args_len = new_len; + return this; + } Gfs_nde[] args = Gfs_nde.Ary_empty; int args_max; int[] args_pos_ary = Int_.Ary_empty; + public Gfs_nde[] Atrs_to_ary() { + Gfs_nde[] rv = new Gfs_nde[args_len]; + for (int i = 0; i < args_len; i++) + rv[i] = args[i]; + return rv; + } + public static final Gfs_nde[] Ary_empty = new Gfs_nde[0]; + public static final byte Op_tid_null = 0, Op_tid_assign = 1; +} diff --git a/400_xowa/src/gplx/gfs/Gfs_parser.java b/400_xowa/src/gplx/gfs/Gfs_parser.java new file mode 100644 index 000000000..2d2a87175 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_parser.java @@ -0,0 +1,103 @@ +/* +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.gfs; import gplx.*; +public class Gfs_parser { + ByteTrieMgr_fast trie = Gfs_parser_.trie_(); + Gfs_parser_ctx ctx = new Gfs_parser_ctx(); + public Gfs_nde Parse(byte[] src) { + ctx.Root().Subs_clear(); + int src_len = src.length; if (src_len == 0) return ctx.Root(); + ctx.Init(trie, src, src_len); + int pos = 0; + while (pos < src_len) { + byte b = src[pos]; + Object o = trie.Match(b, src, pos, src_len); + if (o == null) + ctx.Err_mgr().Fail_unknown_char(ctx, pos, b); + else { + Gfs_lxr lxr = (Gfs_lxr)o; + while (lxr != null) { + int rslt = lxr.Process(ctx, pos, trie.Match_pos()); + switch (lxr.Lxr_tid()) { + case Gfs_lxr_.Tid_whitespace: break; + case Gfs_lxr_.Tid_comment: break; + default: ctx.Prv_lxr_(lxr.Lxr_tid()); break; + } + switch (rslt) { + case Gfs_lxr_.Rv_lxr: + pos = ctx.Nxt_pos(); + lxr = ctx.Nxt_lxr(); + break; + case Gfs_lxr_.Rv_eos: + pos = src_len; + lxr = null; + break; + default: + pos = rslt; + lxr = null; + break; + } + } + } + } + switch (ctx.Prv_lxr()) { + case Gfs_lxr_.Tid_curly_end: + case Gfs_lxr_.Tid_semic: break; + default: ctx.Err_mgr().Fail_eos(ctx); break; + } + return ctx.Root(); + } +} +class Gfs_parser_ { + public static ByteTrieMgr_fast trie_() { + ByteTrieMgr_fast rv = ByteTrieMgr_fast.ci_ascii_(); // NOTE:ci.ascii:gfs;letters/symbols only; + Gfs_lxr_identifier word_lxr = Gfs_lxr_identifier._; + trie_add_rng(rv, word_lxr, Byte_ascii.Ltr_a, Byte_ascii.Ltr_z); + trie_add_rng(rv, word_lxr, Byte_ascii.Ltr_A, Byte_ascii.Ltr_Z); + trie_add_rng(rv, word_lxr, Byte_ascii.Num_0, Byte_ascii.Num_9); + rv.Add(Byte_ascii.Underline, word_lxr); + trie_add_many(rv, Gfs_lxr_whitespace._, Byte_ascii.Space, Byte_ascii.NewLine, Byte_ascii.CarriageReturn, Byte_ascii.Tab); + trie_add_quote(rv, new byte[] {Byte_ascii.Apos}); + trie_add_quote(rv, new byte[] {Byte_ascii.Quote}); + trie_add_quote(rv, Bry_.new_ascii_("<:[\"\n"), Bry_.new_ascii_("\n\"]:>")); + trie_add_quote(rv, Bry_.new_ascii_("<:['\n"), Bry_.new_ascii_("\n']:>")); + trie_add_comment(rv, new byte[] {Byte_ascii.Slash, Byte_ascii.Slash}, new byte[] {Byte_ascii.NewLine}); + trie_add_comment(rv, new byte[] {Byte_ascii.Slash, Byte_ascii.Asterisk}, new byte[] {Byte_ascii.Asterisk, Byte_ascii.Slash}); + rv.Add(Byte_ascii.Semic, Gfs_lxr_semic._); + rv.Add(Byte_ascii.Paren_bgn, Gfs_lxr_paren_bgn._); + rv.Add(Byte_ascii.Paren_end, Gfs_lxr_paren_end._); + rv.Add(Byte_ascii.Curly_bgn, Gfs_lxr_curly_bgn._); + rv.Add(Byte_ascii.Curly_end, Gfs_lxr_curly_end._); + rv.Add(Byte_ascii.Dot, Gfs_lxr_dot._); + rv.Add(Byte_ascii.Comma, Gfs_lxr_comma._); + rv.Add(Byte_ascii.Eq, Gfs_lxr_equal._); + return rv; + } + private static void trie_add_rng(ByteTrieMgr_fast trie, Gfs_lxr lxr, byte bgn, byte end) { + for (byte b = bgn; b <= end; b++) + trie.Add(b, lxr); + } + private static void trie_add_many(ByteTrieMgr_fast trie, Gfs_lxr lxr, byte... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + trie.Add(ary[i], lxr); + } + private static void trie_add_quote(ByteTrieMgr_fast trie, byte[] bgn) {trie_add_quote(trie, bgn, bgn);} + private static void trie_add_quote(ByteTrieMgr_fast trie, byte[] bgn, byte[] end) {trie.Add(bgn, new Gfs_lxr_quote(bgn, end));} + private static void trie_add_comment(ByteTrieMgr_fast trie, byte[] bgn, byte[] end) {trie.Add(bgn, new Gfs_lxr_comment_flat(bgn, end));} +} diff --git a/400_xowa/src/gplx/gfs/Gfs_parser_ctx.java b/400_xowa/src/gplx/gfs/Gfs_parser_ctx.java new file mode 100644 index 000000000..542b24af0 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_parser_ctx.java @@ -0,0 +1,125 @@ +/* +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.gfs; import gplx.*; +class Gfs_parser_ctx { + public ByteTrieMgr_fast Trie() {return trie;} ByteTrieMgr_fast trie; + public Gfs_nde Root() {return root;} Gfs_nde root = new Gfs_nde(); + public byte[] Src() {return src;} private byte[] src; + public int Src_len() {return src_len;} private int src_len; + public byte Prv_lxr() {return prv_lxr;} public Gfs_parser_ctx Prv_lxr_(byte v) {prv_lxr = v; return this;} private byte prv_lxr; + public Gfs_nde Cur_nde() {return cur_nde;} Gfs_nde cur_nde; + public int Nxt_pos() {return nxt_pos;} private int nxt_pos; + public Gfs_lxr Nxt_lxr() {return nxt_lxr;} Gfs_lxr nxt_lxr; + public Bry_bfr Tmp_bfr() {return tmp_bfr;} private Bry_bfr tmp_bfr = Bry_bfr.new_(); + public void Process_eos() {} + public void Process_lxr(int nxt_pos, Gfs_lxr nxt_lxr) {this.nxt_pos = nxt_pos; this.nxt_lxr = nxt_lxr;} + public void Process_null(int cur_pos) {this.nxt_pos = cur_pos; this.nxt_lxr = null;} + public void Init(ByteTrieMgr_fast trie, byte[] src, int src_len) { + this.trie = trie; this.src = src; this.src_len = src_len; + cur_nde = root; + Stack_add(); + } + public void Hold_word(int bgn, int end) { + cur_idf_bgn = bgn; + cur_idf_end = end; + } int cur_idf_bgn = -1, cur_idf_end = -1; + private void Held_word_clear() {cur_idf_bgn = -1; cur_idf_end = -1;} + + public Gfs_nde Make_nde(int tkn_bgn, int tkn_end) { // "abc."; "abc("; "abc;"; "abc{" + Gfs_nde nde = new Gfs_nde().Name_rng_(cur_idf_bgn, cur_idf_end); + this.Held_word_clear(); + cur_nde.Subs_add(nde); + cur_nde = nde; + return nde; + } + public void Make_atr_by_idf() {Make_atr(cur_idf_bgn, cur_idf_end); Held_word_clear();} + public void Make_atr_by_bry(int bgn, int end, byte[] bry) {Make_atr(bgn, end).Name_(bry);} + public Gfs_nde Make_atr(int bgn, int end) { + Gfs_nde nde = new Gfs_nde().Name_rng_(bgn, end); + cur_nde.Atrs_add(nde); + return nde; + } + public void Cur_nde_from_stack() {cur_nde = (Gfs_nde)nodes.FetchAtLast();} + public void Stack_add() {nodes.Add(cur_nde);} ListAdp nodes = ListAdp_.new_(); + public void Stack_pop(int pos) { + if (nodes.Count() < 2) err_mgr.Fail_nde_stack_empty(this, pos); // NOTE: need at least 2 items; 1 to pop and 1 to set as current + ListAdp_.DelAt_last(nodes); + Cur_nde_from_stack(); + } + public Gfs_err_mgr Err_mgr() {return err_mgr;} Gfs_err_mgr err_mgr = new Gfs_err_mgr(); +} +class Gfs_err_mgr { + public void Fail_eos(Gfs_parser_ctx ctx) {Fail(ctx, Fail_msg_eos, ctx.Src_len());} + public void Fail_unknown_char(Gfs_parser_ctx ctx, int pos, byte c) {Fail(ctx, Fail_msg_unknown_char, pos, KeyVal_.new_("char", Char_.XtoStr((char)c)));} + public void Fail_nde_stack_empty(Gfs_parser_ctx ctx, int pos) {Fail(ctx, Fail_msg_nde_stack_empty, pos);} + public void Fail_invalid_lxr(Gfs_parser_ctx ctx, int pos, byte cur_lxr, byte c) { + Fail(ctx, Fail_msg_invalid_lxr, pos, KeyVal_.new_("char", Char_.XtoStr((char)c)), KeyVal_.new_("cur_lxr", Gfs_lxr_.Tid__name(cur_lxr)), KeyVal_.new_("prv_lxr", Gfs_lxr_.Tid__name(ctx.Prv_lxr()))); + } + private void Fail(Gfs_parser_ctx ctx, String msg, int pos, KeyVal... args) { + byte[] src = ctx.Src(); int src_len = ctx.Src_len(); + Fail_args_standard(src, src_len, pos); + int len = args.length; + for (int i = 0; i < len; i++) { + KeyVal arg = args[i]; + tmp_fail_args.Add(arg.Key(), arg.Val_to_str_or_empty()); + } + throw Err_.new_(Fail_msg(msg, tmp_fail_args)); + } + private void Fail_args_standard(byte[] src, int src_len, int pos) { + tmp_fail_args.Add("excerpt_bgn", Fail_excerpt_bgn(src, src_len, pos)); + tmp_fail_args.Add("excerpt_end", Fail_excerpt_end(src, src_len, pos)); + tmp_fail_args.Add("pos" , pos); + } + public static final String Fail_msg_invalid_lxr = "invalid character", Fail_msg_unknown_char = "unknown char", Fail_msg_eos = "end of stream", Fail_msg_nde_stack_empty = "node stack empty"; + String Fail_msg(String type, KeyValList fail_args) { + tmp_fail_bfr.Add_str(type).Add_byte(Byte_ascii.Colon); + int len = fail_args.Count(); + for (int i = 0; i < len; i++) { + tmp_fail_bfr.Add_byte(Byte_ascii.Space); + KeyVal kv = fail_args.GetAt(i); + tmp_fail_bfr.Add_str(kv.Key()); + tmp_fail_bfr.Add_byte(Byte_ascii.Eq).Add_byte(Byte_ascii.Apos); + tmp_fail_bfr.Add_str(kv.Val_to_str_or_empty()).Add_byte(Byte_ascii.Apos); + } + return tmp_fail_bfr.XtoStrAndClear(); + } + Bry_bfr tmp_fail_bfr = Bry_bfr.reset_(255); + KeyValList tmp_fail_args = new KeyValList(); + private static int excerpt_len = 50; + String Fail_excerpt_bgn(byte[] src, int src_len, int pos) { + int bgn = pos - excerpt_len; if (bgn < 0) bgn = 0; + Fail_excerpt_rng(tmp_fail_bfr, src, bgn, pos); + return tmp_fail_bfr.XtoStrAndClear(); + } + String Fail_excerpt_end(byte[] src, int src_len, int pos) { + int end = pos + excerpt_len; if (end > src_len) end = src_len; + Fail_excerpt_rng(tmp_fail_bfr, src, pos, end); + return tmp_fail_bfr.XtoStrAndClear(); + } + private static void Fail_excerpt_rng(Bry_bfr bfr, byte[] src, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Tab: bfr.Add(Esc_tab); break; + case Byte_ascii.NewLine: bfr.Add(Esc_nl); break; + case Byte_ascii.CarriageReturn: bfr.Add(Esc_cr); break; + default: bfr.Add_byte(b); break; + } + } + } static final byte[] Esc_nl = Bry_.new_ascii_("\\n"), Esc_cr = Bry_.new_ascii_("\\r"), Esc_tab = Bry_.new_ascii_("\\t"); +} diff --git a/400_xowa/src/gplx/gfs/Gfs_parser_tst.java b/400_xowa/src/gplx/gfs/Gfs_parser_tst.java new file mode 100644 index 000000000..27ff6362a --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_parser_tst.java @@ -0,0 +1,196 @@ +/* +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.gfs; import gplx.*; +import org.junit.*; +public class Gfs_parser_tst { + @Before public void init() {fxt.Clear();} Gfs_parser_fxt fxt = new Gfs_parser_fxt(); + @Test public void Semicolon() { + fxt .Test_parse("a;", fxt.nde_("a")); + fxt .Test_parse("a;b;c;", fxt.nde_("a"), fxt.nde_("b"), fxt.nde_("c")); + fxt .Test_parse("a_0;", fxt.nde_("a_0")); + } + @Test public void Dot() { + fxt .Test_parse("a.b;", fxt.nde_("a").Subs_add(fxt.nde_("b"))); + fxt .Test_parse("a.b;c.d;", fxt.nde_("a").Subs_add(fxt.nde_("b")), fxt.nde_("c").Subs_add(fxt.nde_("d"))); + } + @Test public void Parens() { + fxt .Test_parse("a();b();", fxt.nde_("a"), fxt.nde_("b")); + fxt .Test_parse("a().b();c().d();", fxt.nde_("a").Subs_add(fxt.nde_("b")), fxt.nde_("c").Subs_add(fxt.nde_("d"))); + } + @Test public void Num() { + fxt .Test_parse("a(1,2);", fxt.nde_("a").Atrs_add_many(fxt.val_("1"), fxt.val_("2"))); + } + @Test public void Quote() { + fxt .Test_parse("a('b');", fxt.nde_("a").Atrs_add(fxt.val_("b"))); + } + @Test public void Quote_escaped() { + fxt .Test_parse("a('b''c''d');", fxt.nde_("a").Atrs_add(fxt.val_("b'c'd"))); + } + @Test public void Quote_escaped_2() { + fxt .Test_parse("a('a''''b');", fxt.nde_("a").Atrs_add(fxt.val_("a''b"))); + } + @Test public void Quote_mixed() { + fxt .Test_parse("a('b\"c');", fxt.nde_("a").Atrs_add(fxt.val_("b\"c"))); + } + @Test public void Comma() { + fxt .Test_parse("a('b','c','d');", fxt.nde_("a").Atrs_add_many(fxt.val_("b"), fxt.val_("c"), fxt.val_("d"))); + } + @Test public void Ws() { + fxt .Test_parse(" a ( 'b' , 'c' ) ; ", fxt.nde_("a").Atrs_add_many(fxt.val_("b"), fxt.val_("c"))); + } + @Test public void Comment_slash_slash() { + fxt .Test_parse("//z\na;//y\n", fxt.nde_("a")); + } + @Test public void Comment_slash_star() { + fxt .Test_parse("/*z*/a;/*y*/", fxt.nde_("a")); + } + @Test public void Curly() { + fxt .Test_parse("a{b;}", fxt.nde_("a").Subs_add(fxt.nde_("b"))); + } + @Test public void Curly_nest() { + fxt .Test_parse("a{b{c{d;}}}" + , fxt.nde_("a").Subs_add + ( fxt.nde_("b").Subs_add + ( fxt.nde_("c").Subs_add + ( fxt.nde_("d") + )))); + } + @Test public void Curly_nest_peers() { + fxt .Test_parse(String_.Concat_lines_nl + ( "a{" + , " a0{" + , " a00{" + , " a000;" + , " }" + , " a01;" + , " }" + , " a1;" + , "}" + ) + , fxt.nde_("a").Subs_add_many + ( fxt.nde_("a0").Subs_add_many + ( fxt.nde_("a00").Subs_add + ( fxt.nde_("a000") + ) + , fxt.nde_("a01") + ) + , fxt.nde_("a1") + )); + } + @Test public void Curly_dot() { + fxt .Test_parse("a{a0.a00;a1.a10;}" + , fxt.nde_("a").Subs_add_many + ( fxt.nde_("a0").Subs_add_many(fxt.nde_("a00")) + , fxt.nde_("a1").Subs_add_many(fxt.nde_("a10")) + )); + } + @Test public void Eq() { + fxt .Test_parse("a='b';", fxt.nde_("a").Atrs_add(fxt.val_("b"))); + fxt .Test_parse("a.b.c='d';" + , fxt.nde_("a").Subs_add + ( fxt.nde_("b").Subs_add_many + ( fxt.nde_("c").Atrs_add(fxt.val_("d")) + ))); + fxt .Test_parse("a.b{c='d'; e='f'}" + , fxt.nde_("a").Subs_add + ( fxt.nde_("b").Subs_add_many + ( fxt.nde_("c").Atrs_add(fxt.val_("d")) + , fxt.nde_("e").Atrs_add(fxt.val_("f")) + ))); + } + @Test public void Curly_nest_peers2() { + fxt .Test_parse(String_.Concat_lines_nl + ( "a() {" + , " k1 = 'v1';" + , "}" + ) + , fxt.nde_("a").Subs_add_many + ( fxt.nde_("k1").Atrs_add(fxt.val_("v1")) + ) + ); + } + @Test public void Fail() { + fxt .Test_parse_fail("a(.);", Gfs_err_mgr.Fail_msg_invalid_lxr); // (.) + fxt .Test_parse_fail("a..b;", Gfs_err_mgr.Fail_msg_invalid_lxr); // .. + fxt .Test_parse_fail("a.;", Gfs_err_mgr.Fail_msg_invalid_lxr); // .; + fxt .Test_parse_fail("a", Gfs_err_mgr.Fail_msg_eos); // eos + fxt .Test_parse_fail("a;~;", Gfs_err_mgr.Fail_msg_unknown_char); // ~ + } +} +class Gfs_parser_fxt { + public void Clear() {} + public Gfs_nde nde_(String v) {return new Gfs_nde().Name_(Bry_.new_ascii_(v));} + public Gfs_nde val_(String v) {return new Gfs_nde().Name_(Bry_.new_ascii_(v));} + public void Test_parse(String src_str, Gfs_nde... expd) { + byte[] src_bry = Bry_.new_utf8_(src_str); + Gfs_nde root = parser.Parse(src_bry); + Tfds.Eq_str_lines(To_str(null, expd), To_str(src_bry, root.Subs_to_ary())); + } private Bry_bfr tmp_bfr = Bry_bfr.new_(), path_bfr = Bry_bfr.new_(); Gfs_parser parser = new Gfs_parser(); + public void Test_parse_fail(String src_str, String expd_err) { + byte[] src_bry = Bry_.new_utf8_(src_str); + try {parser.Parse(src_bry);} + catch (Exception e) { + String actl_err = Err_.Message_gplx_brief(e); + actl_err = String_.GetStrBefore(actl_err, ":"); + boolean match = String_.HasAtBgn(actl_err, expd_err); + if (!match) Tfds.Fail("expecting '" + expd_err + "' got '" + actl_err + "'"); + return; + } + Tfds.Fail("expected to fail with " + expd_err); + } + public String To_str(byte[] src, Gfs_nde[] expd) { + int subs_len = expd.length; + for (int i = 0; i < subs_len; i++) { + path_bfr.Clear().Add_int_variable(i); + To_str(tmp_bfr, path_bfr, src, expd[i]); + } + return tmp_bfr.XtoStrAndClear(); + } + public void To_str(Bry_bfr bfr, Bry_bfr path, byte[] src, Gfs_nde nde) { + To_str_atr(bfr, path, src, Atr_name, nde.Name(), nde.Name_bgn(), nde.Name_end()); + int atrs_len = nde.Atrs_len(); + for (int i = 0; i < atrs_len; i++) { + Gfs_nde atr = nde.Atrs_get_at(i); + int path_len_old = path.Len(); + path.Add_byte(Byte_ascii.Dot).Add_byte((byte)(Byte_ascii.Ltr_a + i)); + int path_len_new = path.Len(); + To_str(bfr, path, src, atr); + path.Del_by(path_len_new - path_len_old); + } + int subs_len = nde.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Gfs_nde sub = nde.Subs_get_at(i); + int path_len_old = path.Len(); + path.Add_byte(Byte_ascii.Dot).Add_int_variable(i); + int path_len_new = path.Len(); + To_str(bfr, path, src, sub); + path.Del_by(path_len_new - path_len_old); + } + } + private void To_str_atr(Bry_bfr bfr, Bry_bfr path_bfr, byte[] src, byte[] name, byte[] val, int val_bgn, int val_end) { + if (val == null && val_bgn == -1 && val_end == -1) return; + bfr.Add_bfr(path_bfr).Add_byte(Byte_ascii.Colon); + bfr.Add(name); + if (val == null) + bfr.Add_mid(src, val_bgn, val_end); + else + bfr.Add(val); + bfr.Add_byte_nl(); + } + private static final byte[] Atr_name = Bry_.new_ascii_("name="); +} diff --git a/400_xowa/src/gplx/gfs/Gfs_wtr.java b/400_xowa/src/gplx/gfs/Gfs_wtr.java new file mode 100644 index 000000000..8402415f7 --- /dev/null +++ b/400_xowa/src/gplx/gfs/Gfs_wtr.java @@ -0,0 +1,46 @@ +/* +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.gfs; import gplx.*; +public class Gfs_wtr { + public byte Quote_char() {return quote_char;} public Gfs_wtr Quote_char_(byte v) {quote_char = v; return this;} private byte quote_char = Byte_ascii.Apos; + public Bry_bfr Bfr() {return bfr;} private Bry_bfr bfr = Bry_bfr.reset_(255); + public void Add_grp_bgn(byte[] key) { + bfr.Add(key); // key + bfr.Add_byte(Byte_ascii.Curly_bgn); // { + } + public void Add_grp_end(byte[] key) { + bfr.Add_byte(Byte_ascii.Curly_end); // } + } + public void Add_set_eq(byte[] key, byte[] val) { + bfr.Add(key); // key + bfr.Add_byte_eq(); // = + bfr.Add_byte(quote_char); // ' + Write_val(val); + bfr.Add_byte(quote_char); // ' + bfr.Add_byte(Byte_ascii.Semic); // ; + } + private void Write_val(byte[] bry) { + int bry_len = bry.length; + for (int i = 0; i < bry_len; i++) { + byte b = bry[i]; + if (b == quote_char) // byte is quote + bfr.Add_byte(b); // double up + bfr.Add_byte(b); + } + } +} diff --git a/400_xowa/src/gplx/gfui/Gfui_bnd_parser.java b/400_xowa/src/gplx/gfui/Gfui_bnd_parser.java new file mode 100644 index 000000000..ea425cba7 --- /dev/null +++ b/400_xowa/src/gplx/gfui/Gfui_bnd_parser.java @@ -0,0 +1,286 @@ +/* +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.gfui; import gplx.*; +public class Gfui_bnd_parser { + private Bry_bfr tmp_bfr = Bry_bfr.reset_(32); + private Hash_adp_bry + gfui_regy = Hash_adp_bry.ci_ascii_() + , norm_regy = Hash_adp_bry.ci_ascii_() + ; + private static final Gfui_bnd_tkn + Itm_sym_plus = new_sym_(Gfui_bnd_tkn.Tid_sym_plus , new byte[] {Byte_ascii.Plus}) + , Itm_sym_pipe = new_sym_(Gfui_bnd_tkn.Tid_sym_pipe , new byte[] {Byte_ascii.Pipe}) + , Itm_sym_comma = new_sym_(Gfui_bnd_tkn.Tid_sym_comma , new byte[] {Byte_ascii.Comma}) +// , Itm_sym_ws = new_sym_(Gfui_bnd_tkn.Tid_sym_ws , Bry_.Empty) + , Itm_sym_eos = new_sym_(Gfui_bnd_tkn.Tid_sym_eos , Bry_.Empty) + ; + private static final Gfui_bnd_tkn[] Mod_ary = new Gfui_bnd_tkn[] + { null + , new_mod_(Gfui_bnd_tkn.Tid_mod_c , "mod.c" , "Ctrl") + , new_mod_(Gfui_bnd_tkn.Tid_mod_a , "mod.a" , "Alt") + , new_mod_(Gfui_bnd_tkn.Tid_mod_ca , "mod.ca" , "Ctrl + Alt") + , new_mod_(Gfui_bnd_tkn.Tid_mod_s , "mod.s" , "Shift") + , new_mod_(Gfui_bnd_tkn.Tid_mod_cs , "mod.cs" , "Ctrl + Shift") + , new_mod_(Gfui_bnd_tkn.Tid_mod_as , "mod.as" , "Alt + Shift") + , new_mod_(Gfui_bnd_tkn.Tid_mod_cas , "mod.cas" , "Ctrl + Alt + Shift") + }; + private byte[] src; private int src_len; + private ListAdp tkns = ListAdp_.new_(); private int mod_val = Mod_val_null; + public String X_to_norm(String src_str) {return Convert(Bool_.Y, src_str);} + public String X_to_gfui(String src_str) {return Convert(Bool_.N, src_str);} + private String Convert(boolean src_is_gfui, String src_str) { + this.src = Bry_.new_utf8_(src_str); this.src_len = src.length; + tkns.Clear(); mod_val = Mod_val_null; + int pos = 0; int itm_bgn = -1, itm_end = -1; + while (pos <= src_len) { // loop over bytes and break up tkns by symbols + byte b = pos == src_len ? Byte_ascii.NewLine: src[pos]; // treat eos as "\n" for purpose of separating tokens + Gfui_bnd_tkn sym_tkn = null; + switch (b) { + case Byte_ascii.Plus: // simultaneous; EX: Ctrl + S + sym_tkn = Itm_sym_plus; + break; + case Byte_ascii.Pipe: // alternate; EX: Ctrl + S | Ctrl + Alt + s + sym_tkn = Itm_sym_pipe; + break; + case Byte_ascii.Comma: // chorded; EX: Ctrl + S, Ctrl + T + sym_tkn = Itm_sym_comma; + break; + case Byte_ascii.NewLine: // eos: process anything in bfr + sym_tkn = Itm_sym_eos; + break; + case Byte_ascii.Space: + if (itm_bgn != -1) // if word started, " " ends word; EX: "Ctrl + A"; " +" ends "Ctrl" + itm_end = pos; + ++pos; + continue; + default: // letter / number; continue + if (itm_bgn == -1) // no word started; start it + itm_bgn = pos; + ++pos; + continue; + } + if (itm_end == -1) // end not set by space; char before symbol is end + itm_end = pos; + Process_sym(src_is_gfui, sym_tkn, itm_bgn, itm_end); + if (sym_tkn.Tid() == Gfui_bnd_tkn.Tid_sym_eos) + break; + else + ++pos; + itm_bgn = itm_end = -1; + } + int tkns_len = tkns.Count(); + for (int i = 0; i < tkns_len; i++) { + Gfui_bnd_tkn tkn = (Gfui_bnd_tkn)tkns.FetchAt(i); + tkn.Write(tmp_bfr, !src_is_gfui); + } + return tmp_bfr.XtoStrAndClear(); + } + private void Process_sym(boolean src_is_gfui, Gfui_bnd_tkn sym_tkn, int itm_bgn, int itm_end) { + Hash_adp_bry regy = src_is_gfui ? gfui_regy : norm_regy; + Gfui_bnd_tkn tkn = (Gfui_bnd_tkn)regy.Get_by_mid(src, itm_bgn, itm_end); + if (tkn == null) throw Err_.new_fmt_("unknown key: key={0}", String_.new_utf8_(src, itm_bgn, itm_end)); + int mod_adj = Mod_val_null; + switch (tkn.Tid()) { + case Gfui_bnd_tkn.Tid_mod_c: mod_adj = Gfui_bnd_tkn.Tid_mod_c; break; + case Gfui_bnd_tkn.Tid_mod_a: mod_adj = Gfui_bnd_tkn.Tid_mod_a; break; + case Gfui_bnd_tkn.Tid_mod_s: mod_adj = Gfui_bnd_tkn.Tid_mod_s; break; + case Gfui_bnd_tkn.Tid_mod_cs: mod_adj = Gfui_bnd_tkn.Tid_mod_cs; break; + case Gfui_bnd_tkn.Tid_mod_as: mod_adj = Gfui_bnd_tkn.Tid_mod_as; break; + case Gfui_bnd_tkn.Tid_mod_ca: mod_adj = Gfui_bnd_tkn.Tid_mod_ca; break; + case Gfui_bnd_tkn.Tid_mod_cas: mod_adj = Gfui_bnd_tkn.Tid_mod_cas; break; + case Gfui_bnd_tkn.Tid_key: break; + default: throw Err_.unhandled(tkn.Tid()); + } + switch (sym_tkn.Tid()) { + case Gfui_bnd_tkn.Tid_sym_plus: // EX: Ctrl + A + if (mod_adj != Mod_val_null) { // if mod, just update mod_val and exit + mod_val = Enm_.FlipInt(true, mod_val, mod_adj); + return; + } + break; + } + if (mod_val != Mod_val_null) { // modifier exists; add tkn + tkns.Add(Mod_ary[mod_val]); + tkns.Add(Itm_sym_plus); + mod_val = Mod_val_null; + } + tkns.Add(tkn); // add word + if (sym_tkn.Tid() != Gfui_bnd_tkn.Tid_sym_eos) + tkns.Add(sym_tkn); + } + private Gfui_bnd_parser Init_en() { + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_c); + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_a); + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_s); + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_ca); + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_cs); + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_as); + Init_itm_mod(Gfui_bnd_tkn.Tid_mod_cas); + Init_itm(Gfui_bnd_tkn.Tid_mod_c, "key.ctrl", "Ctrl"); + Init_itm(Gfui_bnd_tkn.Tid_mod_a, "key.alt", "Atl"); + Init_itm(Gfui_bnd_tkn.Tid_mod_s, "key.shift", "Shift"); + Init_itm("key.a", "A"); + Init_itm("key.b", "B"); + Init_itm("key.c", "C"); + Init_itm("key.d", "D"); + Init_itm("key.e", "E"); + Init_itm("key.f", "F"); + Init_itm("key.g", "G"); + Init_itm("key.h", "H"); + Init_itm("key.i", "I"); + Init_itm("key.j", "J"); + Init_itm("key.k", "K"); + Init_itm("key.l", "L"); + Init_itm("key.m", "M"); + Init_itm("key.n", "N"); + Init_itm("key.o", "O"); + Init_itm("key.p", "P"); + Init_itm("key.q", "Q"); + Init_itm("key.r", "R"); + Init_itm("key.s", "S"); + Init_itm("key.t", "T"); + Init_itm("key.u", "U"); + Init_itm("key.v", "V"); + Init_itm("key.w", "W"); + Init_itm("key.x", "X"); + Init_itm("key.y", "Y"); + Init_itm("key.z", "Z"); + Init_itm("key.d0", "0"); + Init_itm("key.d1", "1"); + Init_itm("key.d2", "2"); + Init_itm("key.d3", "3"); + Init_itm("key.d4", "4"); + Init_itm("key.d5", "5"); + Init_itm("key.d6", "6"); + Init_itm("key.d7", "7"); + Init_itm("key.d8", "8"); + Init_itm("key.d9", "9"); + Init_itm("key.f1", "F1"); + Init_itm("key.f2", "F2"); + Init_itm("key.f3", "F3"); + Init_itm("key.f4", "F4"); + Init_itm("key.f5", "F5"); + Init_itm("key.f6", "F6"); + Init_itm("key.f7", "F7"); + Init_itm("key.f8", "F8"); + Init_itm("key.f9", "F9"); + Init_itm("key.f10", "F10"); + Init_itm("key.f11", "F11"); + Init_itm("key.f12", "F12"); + Init_itm("key.none", "None"); + Init_itm("key.back", "Backspace"); + Init_itm("key.tab", "Tab"); + Init_itm("key.clear", "Clear"); + Init_itm("key.enter", "Enter"); + Init_itm("key.shiftKey", "ShiftKey"); + Init_itm("key.ctrlKey", "CtrlKey"); + Init_itm("key.altKey", "AltKey"); + Init_itm("key.pause", "Pause"); + Init_itm("key.capsLock", "CapsLock"); + Init_itm("key.escape", "Escape"); + Init_itm("key.space", "Space"); + Init_itm("key.pageUp", "PageUp"); + Init_itm("key.pageDown", "PageDown"); + Init_itm("key.end", "End"); + Init_itm("key.home", "Home"); + Init_itm("key.left", "Left"); + Init_itm("key.up", "Up"); + Init_itm("key.right", "Right"); + Init_itm("key.down", "Down"); + Init_itm("key.printScreen", "PrintScreen"); + Init_itm("key.insert", "Insert"); + Init_itm("key.delete", "Delete"); + Init_itm("key.numLock", "NumLock"); + Init_itm("key.scrollLock", "ScrollLock"); + Init_itm("key.semicolon", "Semicolon"); + Init_itm("key.equal", "Equal"); + Init_itm("key.comma", "Comma"); + Init_itm("key.minus", "Minus"); + Init_itm("key.period", "Period"); + Init_itm("key.slash", "Slash"); + Init_itm("key.tick", "Tick"); + Init_itm("key.openBracket", "OpenBracket"); + Init_itm("key.backslash", "Backslash"); + Init_itm("key.closeBracket", "CloseBracket"); + Init_itm("key.quote", "Quote"); + Init_itm("mouse.middle", "Middle Click"); + Init_itm("mouse.left", "Left Click"); + Init_itm("mouse.right", "Right Click"); + return this; + } + private void Init_itm(String gfui, String norm) {Init_itm(Gfui_bnd_tkn.Tid_key, gfui, norm);} + private void Init_itm_mod(int tid) { + Gfui_bnd_tkn itm = Mod_ary[tid]; + gfui_regy.Add(itm.Bry_gfui(), itm); + norm_regy.Add(itm.Bry_norm(), itm); + } + private void Init_itm(byte tid, String gfui, String norm) { + byte[] gfui_bry = Bry_.new_utf8_(gfui); + byte[] norm_bry = Bry_.new_utf8_(norm); + Gfui_bnd_tkn itm = new Gfui_bnd_tkn(tid, gfui_bry, norm_bry); + gfui_regy.Add(gfui_bry, itm); + norm_regy.Add_if_new(norm_bry, itm); + } + private static final int Mod_val_null = 0; + public static Gfui_bnd_parser new_en_() {return new Gfui_bnd_parser().Init_en();} Gfui_bnd_parser() {} + private static Gfui_bnd_tkn new_sym_(byte tid, byte[] bry) {return new Gfui_bnd_tkn(tid, bry, bry);} + private static Gfui_bnd_tkn new_mod_(byte tid, String gfui, String norm) {return new Gfui_bnd_tkn(tid, Bry_.new_ascii_(gfui), Bry_.new_ascii_(norm));} +} +class Gfui_bnd_tkn { + public Gfui_bnd_tkn(byte tid, byte[] gfui, byte[] norm) {this.tid = tid; this.bry_gfui = gfui; this.bry_norm = norm;} + public byte Tid() {return tid;} private byte tid; + public byte[] Bry_gfui() {return bry_gfui;} private byte[] bry_gfui; + public byte[] Bry_norm() {return bry_norm;} private byte[] bry_norm; + public void Write(Bry_bfr bfr, boolean src_is_gfui) { + byte[] bry = src_is_gfui ? bry_gfui : bry_norm; + switch (tid) { + case Tid_mod_c: case Tid_mod_a: case Tid_mod_s: + case Tid_mod_ca: case Tid_mod_cs: case Tid_mod_as: case Tid_mod_cas: + bfr.Add(bry); + break; + case Tid_sym_plus: + if (!src_is_gfui) + bfr.Add_byte_space(); + bfr.Add(bry); + if (!src_is_gfui) + bfr.Add_byte_space(); + break; + case Tid_sym_pipe: + if (!src_is_gfui) + bfr.Add_byte_space(); + bfr.Add(bry); + if (!src_is_gfui) + bfr.Add_byte_space(); + break; + case Tid_sym_comma: + bfr.Add(bry); + if (!src_is_gfui) + bfr.Add_byte_space(); + break; + case Tid_key: + bfr.Add(bry); + break; + } + } + public static final byte + Tid_mod_c = 1 , Tid_mod_a = 2 , Tid_mod_s = 4 + , Tid_mod_ca = 3 , Tid_mod_cs = 5 , Tid_mod_as = 6, Tid_mod_cas = 7 + , Tid_sym_plus = 8 , Tid_sym_pipe = 9 , Tid_sym_comma = 10, Tid_sym_eos = 11 + , Tid_key = 12 + ; +} diff --git a/400_xowa/src/gplx/gfui/Gfui_bnd_parser_tst.java b/400_xowa/src/gplx/gfui/Gfui_bnd_parser_tst.java new file mode 100644 index 000000000..0446184fb --- /dev/null +++ b/400_xowa/src/gplx/gfui/Gfui_bnd_parser_tst.java @@ -0,0 +1,56 @@ +/* +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.gfui; import gplx.*; +import org.junit.*; +public class Gfui_bnd_parser_tst { + @Before public void init() {fxt.Clear();} private Gfui_bnd_parser_fxt fxt = new Gfui_bnd_parser_fxt(); + @Test public void Norm_one() { + fxt.Test_x_to_norm("mod.c" , "Ctrl"); + fxt.Test_x_to_norm("key.ctrl" , "Ctrl"); + fxt.Test_x_to_norm("key.a" , "A"); + fxt.Test_x_to_norm("key.left" , "Left"); + } + @Test public void Norm_add() { + fxt.Test_x_to_norm("mod.c+key.a" , "Ctrl + A"); + fxt.Test_x_to_norm("mod.ca+key.a" , "Ctrl + Alt + A"); + fxt.Test_x_to_norm("mod.cas+key.a" , "Ctrl + Alt + Shift + A"); + } + @Test public void Norm_chord() { + fxt.Test_x_to_norm("key.a,key.b" , "A, B"); + } + @Test public void Norm_add_and_chord() { + fxt.Test_x_to_norm("mod.c+key.a,mod.a+key.b" , "Ctrl + A, Alt + B"); + } + @Test public void Gfui_add() { + fxt.Test_x_to_gfui("Ctrl + A" , "mod.c+key.a"); + fxt.Test_x_to_gfui("Ctrl + Shift + A" , "mod.cs+key.a"); + fxt.Test_x_to_gfui("Ctrl + Alt + Shift + A" , "mod.cas+key.a"); + } +} +class Gfui_bnd_parser_fxt { + private Gfui_bnd_parser parser; + public void Clear() { + parser = Gfui_bnd_parser.new_en_(); + } + public void Test_x_to_norm(String key, String expd) { + Tfds.Eq(expd, parser.X_to_norm(key)); + } + public void Test_x_to_gfui(String key, String expd) { + Tfds.Eq(expd, parser.X_to_gfui(key)); + } +} diff --git a/400_xowa/src/gplx/html/Html_atrs.java b/400_xowa/src/gplx/html/Html_atrs.java new file mode 100644 index 000000000..bb3e75ca0 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_atrs.java @@ -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 . +*/ +package gplx.html; import gplx.*; +public class Html_atrs { + public static final String + Src_str = "src" + ; + public static final byte[] + Id_bry = Bry_.new_ascii_("id") + , Cls_bry = Bry_.new_ascii_("class") + , Style_bry = Bry_.new_ascii_("style") + ; +} diff --git a/400_xowa/src/gplx/html/Html_consts.java b/400_xowa/src/gplx/html/Html_consts.java new file mode 100644 index 000000000..4274f1626 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_consts.java @@ -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 . +*/ +package gplx.html; import gplx.*; +public class Html_consts { + public static final String Nl_str = " "; + public static final String + Comm_bgn_str = "" + , Img_str = "img" + ; + public static final byte[] + Lt = Bry_.new_ascii_("<"), Gt = Bry_.new_ascii_(">") + , Amp = Bry_.new_ascii_("&"), Quote = Bry_.new_ascii_("""), Apos = Bry_.new_ascii_("'") + , Eq = Bry_.new_ascii_("=") + , Nl_bry = Bry_.new_ascii_(Nl_str), Space_bry = Bry_.new_ascii_(" ") + , Comm_bgn = Bry_.new_ascii_(Comm_bgn_str), Comm_end = Bry_.new_ascii_(Comm_end_str) + , Hr_bry = Bry_.new_ascii_("
") + , Br_bry = Bry_.new_ascii_("
") + , Td_bgn_bry = Bry_.new_ascii_("") + , Td_end_bry = Bry_.new_ascii_("") + , Ul_tag_bry = Bry_.new_ascii_("ul") + ; + public static final int + Comm_bgn_len = Comm_bgn.length + , Comm_end_len = Comm_end.length + ; +} diff --git a/400_xowa/src/gplx/html/Html_nde.java b/400_xowa/src/gplx/html/Html_nde.java new file mode 100644 index 000000000..d729db6bf --- /dev/null +++ b/400_xowa/src/gplx/html/Html_nde.java @@ -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 . +*/ +package gplx.html; import gplx.*; +public class Html_nde { + public Html_nde(byte[] src, boolean tag_tid_is_inline, int tag_lhs_bgn, int tag_lhs_end, int tag_rhs_bgn, int tag_rhs_end, int name_bgn, int name_end, int[] cur_atrs, int atrs_idx) { + this.src = src; + this.tag_tid_is_inline = tag_tid_is_inline; + this.tag_lhs_bgn = tag_lhs_bgn; this.tag_lhs_end = tag_lhs_end; this.tag_rhs_bgn = tag_rhs_bgn; this.tag_rhs_end = tag_rhs_end; this.name_bgn = name_bgn; this.name_end = name_end; + if (atrs_idx > 0) { + atrs = new int[atrs_idx]; + for (int i = 0; i < atrs_idx; i++) + atrs[i] = cur_atrs[i]; + atrs_len = atrs_idx / 5; + } + } + public byte[] Src() {return src;} private byte[] src; + public int[] Atrs() {return atrs;} private int[] atrs = Int_.Ary_empty; + public int Atrs_len() {return atrs_len;} private int atrs_len; + public boolean Tag_tid_is_inline() {return tag_tid_is_inline;} private boolean tag_tid_is_inline; + public int Tag_lhs_bgn() {return tag_lhs_bgn;} public Html_nde Tag_lhs_bgn_(int v) {tag_lhs_bgn = v; return this;} private int tag_lhs_bgn; + public int Tag_lhs_end() {return tag_lhs_end;} public Html_nde Tag_lhs_end_(int v) {tag_lhs_end = v; return this;} private int tag_lhs_end; + public int Tag_rhs_bgn() {return tag_rhs_bgn;} public Html_nde Tag_rhs_bgn_(int v) {tag_rhs_bgn = v; return this;} private int tag_rhs_bgn; + public int Tag_rhs_end() {return tag_rhs_end;} public Html_nde Tag_rhs_end_(int v) {tag_rhs_end = v; return this;} private int tag_rhs_end; + public int Name_bgn() {return name_bgn;} public Html_nde Name_bgn_(int v) {name_bgn = v; return this;} private int name_bgn; + public int Name_end() {return name_end;} public Html_nde Name_end_(int v) {name_end = v; return this;} private int name_end; + public void Clear() {tag_lhs_bgn = tag_rhs_bgn = -1;} + public String Atrs_val_by_key_str(String find_key_str) {return String_.new_utf8_(Atrs_val_by_key_bry(Bry_.new_utf8_(find_key_str)));} + public byte[] Atrs_val_by_key_bry(byte[] find_key_bry) { + for (int i = 0; i < atrs_len; i ++) { + int atrs_idx = i * 5; + int atr_key_bgn = atrs[atrs_idx + 1]; + int atr_key_end = atrs[atrs_idx + 2]; + if (Bry_.Match(src, atr_key_bgn, atr_key_end, find_key_bry)) + return Atrs_vals_by_pos(src, atrs[atrs_idx + 0], atrs[atrs_idx + 3], atrs[atrs_idx + 4]); + } + return null; + } + byte[] Atrs_vals_by_pos(byte[] src, int quote_byte, int bgn, int end) { + Bry_bfr tmp_bfr = Bry_bfr.new_(); + boolean dirty = false; + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Backslash: + if (!dirty) {dirty = true; tmp_bfr.Add_mid(src, bgn, i);} + ++i; + tmp_bfr.Add_byte(src[i]); + break; + default: + if (b == quote_byte) { + byte next_byte = src[i + 1]; + if (next_byte == b) { + if (!dirty) {dirty = true; tmp_bfr.Add_mid(src, bgn, i);} + ++i; + tmp_bfr.Add_byte(src[i]); + } + } + else { + if (dirty) + tmp_bfr.Add_byte(b); + } + break; + } + } + return dirty ? tmp_bfr.XtoAryAndClear() : Bry_.Mid(src, bgn, end); + } + public byte[] Data(byte[] src) { + return Bry_.Mid(src, tag_lhs_end, tag_rhs_bgn); + } +} +// class Xoh_atr { +// public byte[] Key_bry() {return key_bry;} private byte[] key_bry; +// public byte[] Val_bry() {return val_bry;} private byte[] val_bry; +// public int Key_bgn() {return key_bgn;} private int key_bgn; +// public int Key_end() {return key_end;} private int key_end; +// public int Val_bgn() {return val_bgn;} private int val_bgn; +// public int Val_end() {return val_end;} private int val_end; +// public byte Val_quote_tid() {return val_quote_tid;} private byte val_quote_tid; +// } diff --git a/400_xowa/src/gplx/html/Html_parser.java b/400_xowa/src/gplx/html/Html_parser.java new file mode 100644 index 000000000..fac131420 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_parser.java @@ -0,0 +1,165 @@ +/* +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.html; import gplx.*; +import gplx.core.bytes.*; +public class Html_parser { + public Html_parser() { + Bry_bldr bry_bldr = new Bry_bldr(); + bry_xnde_name = bry_bldr.New_256().Set_rng_xml_identifier(Scan_valid).Set_rng_ws(Scan_stop).Val(); + bry_atr_key = bry_bldr.New_256().Set_rng_xml_identifier(Scan_valid).Set_rng_ws(Scan_stop).Set_many(Scan_stop, Byte_ascii.Eq).Val(); + } + byte[] src; int pos, end; byte[] bry_xnde_name, bry_atr_key; + int cur_atrs_idx = 0; int[] cur_atrs = new int[250];// define max of 50 atrs; + public Html_nde[] Parse_as_ary(byte[] src) {return Parse_as_ary(src, 0, src.length, Wildcard, Wildcard);} + public Html_nde[] Parse_as_ary(byte[] src, int bgn, int end) {return Parse_as_ary(src, bgn, end, Wildcard, Wildcard);} + public Html_nde[] Parse_as_ary(byte[] src, int bgn, int end, byte[] find_key, byte[] find_val) { // flattens html into a list of hndes; only used for Options + this.src = src; pos = bgn; this.end = end; + ListAdp rv = ListAdp_.new_(); + while (pos < end) { + byte b = src[pos++]; + switch (b) { + case Byte_ascii.Lt: + if (xnde_init) { + if (Parse_xnde_lhs()) { + if (tag_tid_is_inline) + rv.Add(new Html_nde(src, tag_tid_is_inline, cur_lhs_bgn, cur_lhs_end, cur_rhs_bgn, pos, cur_name_bgn, cur_name_end, cur_atrs, cur_atrs_idx)); + else + xnde_init = false; + } + } + else { + if (Parse_xnde_rhs()) { + rv.Add(new Html_nde(src, tag_tid_is_inline, cur_lhs_bgn, cur_lhs_end, cur_rhs_bgn, pos, cur_name_bgn, cur_name_end, cur_atrs, cur_atrs_idx)); + } + xnde_init = true; + } + break; + default: + break; + } + } + return (Html_nde[])rv.XtoAry(Html_nde.class); + } + int cur_lhs_bgn, cur_lhs_end, cur_name_bgn, cur_name_end, cur_rhs_bgn; boolean xnde_init = true, tag_tid_is_inline = false; + private boolean Parse_xnde_rhs() { + cur_rhs_bgn = pos - 1; // -1 b/c "<" is already read + byte b = src[pos]; + if (b != Byte_ascii.Slash) return false; + ++pos; + int name_len = cur_name_end - cur_name_bgn; + if (pos + name_len >= end) return false; + if (!Bry_.Match(src, pos, pos + name_len, src, cur_name_bgn, cur_name_end)) return false; + pos += name_len; + if (src[pos] != Byte_ascii.Gt) return false; + ++pos; + return true; + } + private boolean Parse_xnde_lhs() { + cur_atrs_idx = 0; + cur_lhs_bgn = pos - 1; + cur_name_bgn = pos; + tag_tid_is_inline = false; + byte rslt = Skip_while_valid(this.bry_atr_key); + if (rslt == Scan_invalid) return false; + cur_name_end = pos; + int key_bgn, key_end, val_bgn, quote_type; + while (true) { + if (pos >= end) return false; + key_bgn = key_end = val_bgn = quote_type = -1; + Skip_ws(); + byte b = src[pos]; + if (b == Byte_ascii.Slash) { + ++pos; + if (pos == end) return false; + byte next = src[pos]; + if (next == Byte_ascii.Gt) { + tag_tid_is_inline = true; + ++pos; + break; + } + else return false; // NOTE: don't consume byte b/c false + } + else if (b == Byte_ascii.Gt) { + ++pos; + break; + } + key_bgn = pos; + rslt = Skip_while_valid(this.bry_atr_key); + if (rslt == Scan_invalid) return false; + key_end = pos; + Skip_ws(); + if (src[pos++] != Byte_ascii.Eq) return false; + Skip_ws(); + byte quote_byte = src[pos]; + switch (quote_byte) { + case Byte_ascii.Quote: quote_type = quote_byte; break; + case Byte_ascii.Apos: quote_type = quote_byte; break; + default: return false; + } + val_bgn = ++pos; // ++pos: start val after quote + if (!Skip_to_quote_end(quote_byte)) return false; + cur_atrs[cur_atrs_idx + 0] = quote_type; + cur_atrs[cur_atrs_idx + 1] = key_bgn; + cur_atrs[cur_atrs_idx + 2] = key_end; + cur_atrs[cur_atrs_idx + 3] = val_bgn; + cur_atrs[cur_atrs_idx + 4] = pos - 1; // NOTE: Skip_to_quote_end positions after quote + cur_atrs_idx += 5; + } + cur_lhs_end = pos; + return true; + } + private void Skip_ws() { + while (pos < end) { + switch (src[pos]) { + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + ++pos; + break; + default: + return; + } + } + } + boolean Skip_to_quote_end(byte v) { + while (pos < end) { + byte b = src[pos++]; + if (b == v) { + if (pos == end) return false; + byte next = src[pos]; + if (next != v) return true; + else ++pos; + } + else if (b == Byte_ascii.Backslash) { + ++pos; + } + } + return false; + } + byte Skip_while_valid(byte[] comp) { + while (pos < end) { + byte rv = comp[src[pos]]; + if (rv == Scan_valid) + ++pos; + else + return rv; + } + return Scan_invalid; + } + private static final byte Scan_invalid = 0, Scan_valid = 1, Scan_stop = 2; + public static final byte[] Wildcard = null; + public static final String Wildcard_str = null; +} diff --git a/400_xowa/src/gplx/html/Html_parser_tst.java b/400_xowa/src/gplx/html/Html_parser_tst.java new file mode 100644 index 000000000..4c347e013 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_parser_tst.java @@ -0,0 +1,53 @@ +/* +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.html; import gplx.*; +import org.junit.*; +public class Html_parser_tst { + @Before public void init() {fxt.Clear();} private Xoh_parser_fxt fxt = new Xoh_parser_fxt(); + @Test public void One() {fxt.Test_parse_find_all("", "id0");} + @Test public void Many() {fxt.Test_parse_find_all("", "id0", "id1", "id2");} + @Test public void Inline() {fxt.Test_parse_find_all("", "id0");} + @Test public void Mix() {fxt.Test_parse_find_all("012id=id2345abc", "id0", "id1", "id2");} + @Test public void Quote_double() {fxt.Test_parse_find_all("", "id'0");} + @Test public void Quote_escape() {fxt.Test_parse_find_all("", "id'0");} +} +class Xoh_parser_fxt { + public void Clear() { + if (parser == null) { + parser = new Html_parser(); + } + } private Html_parser parser; + public Xoh_parser_fxt Test_parse_find_all(String raw_str, String... expd) {return Test_parse_find(raw_str, Html_parser.Wildcard_str, Html_parser.Wildcard_str, expd);} + public Xoh_parser_fxt Test_parse_find(String raw_str, String find_key, String find_val, String... expd) { + byte[] raw = Bry_.new_ascii_(raw_str); + Html_nde[] actl_ndes = parser.Parse_as_ary(raw, 0, raw.length, Bry_.new_ascii_(find_key), Bry_.new_ascii_(find_val)); + String[] actl = Xto_ids(raw, actl_ndes); + Tfds.Eq_ary_str(expd, actl); + return this; + } + private String[] Xto_ids(byte[] src, Html_nde[] ary) { + int len = ary.length; + String[] rv = new String[len]; + for (int i = 0; i < len; i++) { + Html_nde itm = ary[i]; + String atr_val = itm.Atrs_val_by_key_str("id"); + rv[i] = atr_val; + } + return rv; + } +} diff --git a/400_xowa/src/gplx/html/Html_selecter.java b/400_xowa/src/gplx/html/Html_selecter.java new file mode 100644 index 000000000..810dd1cb7 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_selecter.java @@ -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 . +*/ +package gplx.html; import gplx.*; +public class Html_selecter { + public static Html_nde[] Select(byte[] src, Html_nde[] ary, Hash_adp_bry hash) { + ListAdp list = ListAdp_.new_(); + int xndes_len = ary.length; + for (int i = 0; i < xndes_len; i++) { + Html_nde hnde = ary[i]; + int[] atrs = hnde.Atrs(); + int atrs_len = atrs.length; + for (int j = 0; j < atrs_len; j += 5) { + int atr_key_bgn = atrs[j + 1]; + int atr_key_end = atrs[j + 2]; + if (hash.Get_by_mid(src, atr_key_bgn, atr_key_end) != null) { + list.Add(hnde); + break; + } + } + } + Html_nde[] rv = (Html_nde[])list.XtoAry(Html_nde.class); + list.Clear(); + return rv; + } +} diff --git a/400_xowa/src/gplx/html/Html_tags.java b/400_xowa/src/gplx/html/Html_tags.java new file mode 100644 index 000000000..8ab6e61cb --- /dev/null +++ b/400_xowa/src/gplx/html/Html_tags.java @@ -0,0 +1,36 @@ +/* +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.html; import gplx.*; +public class Html_tags { + public static final String + A_str = "a" + , Img_str = "img" + ; + public static final byte[] + Body_lhs = Bry_.new_ascii_("") + , Body_rhs = Bry_.new_ascii_("") + , Html_rhs = Bry_.new_ascii_("") + , Head_lhs_bgn = Bry_.new_ascii_("") + , Style_lhs_w_type = Bry_.new_ascii_("") + , Script_lhs = Bry_.new_ascii_("") + ; +} diff --git a/400_xowa/src/gplx/html/Html_utl.java b/400_xowa/src/gplx/html/Html_utl.java new file mode 100644 index 000000000..447023623 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_utl.java @@ -0,0 +1,167 @@ +/* +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.html; import gplx.*; +public class Html_utl { + public static byte[] Escape_for_atr_val_as_bry(String s, byte quote_byte) { + Bry_bfr tmp_bfr = null; + if (s == null) return null; + byte[] bry = Bry_.new_utf8_(s); + boolean dirty = false; + int len = bry.length; + for (int i = 0; i < len; i++) { + byte b = bry[i]; + if (b == quote_byte) { + if (!dirty) { + tmp_bfr = Bry_bfr.reset_(256); + tmp_bfr.Add_mid(bry, 0, i); + dirty = true; + } + switch (quote_byte) { + case Byte_ascii.Apos: tmp_bfr.Add(Html_consts.Apos); break; + case Byte_ascii.Quote: tmp_bfr.Add(Html_consts.Quote); break; + default: throw Err_.unhandled(quote_byte); + } + } + else { + if (dirty) + tmp_bfr.Add_byte(b); + } + } + return dirty ? tmp_bfr.XtoAryAndClear() : bry; + } + public static String Escape_html_as_str(String v) {return String_.new_utf8_(Escape_html_as_bry(Bry_.new_utf8_(v)));} + public static byte[] Escape_html_as_bry(Bry_bfr tmp, byte[] bry) {return Escape_html(false, tmp, bry, 0, bry.length, true, true, true, true, true);} + public static byte[] Escape_html_as_bry(byte[] bry) {return Escape_html(false, Bry_bfr.new_(), bry, 0, bry.length, true, true, true, true, true);} + public static byte[] Escape_html_as_bry(byte[] bry, boolean lt, boolean gt, boolean amp, boolean quote, boolean apos) + {return Escape_html(false, Bry_bfr.new_(), bry, 0, bry.length, lt, gt, amp, quote, apos);} + + public static void Escape_html_to_bfr(Bry_bfr bfr, byte[] bry, int bgn, int end, boolean escape_lt, boolean escape_gt, boolean escape_amp, boolean escape_quote, boolean escape_apos) { + Escape_html(true, bfr, bry, bgn, end, escape_lt, escape_gt, escape_amp, escape_quote, escape_apos); + } + private static byte[] Escape_html(boolean write_to_bfr, Bry_bfr bfr, byte[] bry, int bgn, int end, boolean escape_lt, boolean escape_gt, boolean escape_amp, boolean escape_quote, boolean escape_apos) { + if (bry == null) return null; + boolean dirty = write_to_bfr ? true : false; // if write_to_bfr, then mark true, else bfr.Add_mid(bry, 0, i); will write whole bry again + byte[] escaped = null; + for (int i = bgn; i < end; i++) { + byte b = bry[i]; + switch (b) { + case Byte_ascii.Lt: if (escape_lt) escaped = Html_consts.Lt; break; + case Byte_ascii.Gt: if (escape_gt) escaped = Html_consts.Gt; break; + case Byte_ascii.Amp: if (escape_amp) escaped = Html_consts.Amp; break; + case Byte_ascii.Quote: if (escape_quote) escaped = Html_consts.Quote; break; + case Byte_ascii.Apos: if (escape_apos) escaped = Html_consts.Apos; break; + default: + if (dirty || write_to_bfr) + bfr.Add_byte(b); + continue; + } + // handle lt, gt, amp, quote; everything else handled by default: continue above + if (escaped == null) { // handle do-not-escape calls; EX: Escape(y, y, n, y); + if (dirty || write_to_bfr) + bfr.Add_byte(b); + } + else { + if (!dirty) { + bfr.Add_mid(bry, bgn, i); + dirty = true; + } + bfr.Add(escaped); + escaped = null; + } + } + if (write_to_bfr) + return null; + else + return dirty ? bfr.XtoAryAndClear() : bry; + } + + private static final ByteTrieMgr_slim unescape_trie = ByteTrieMgr_slim.ci_ascii_() + .Add_bry_bval(Html_consts.Lt , Byte_ascii.Lt) + .Add_bry_bval(Html_consts.Gt , Byte_ascii.Gt) + .Add_bry_bval(Html_consts.Amp , Byte_ascii.Amp) + .Add_bry_bval(Html_consts.Quote , Byte_ascii.Quote) + .Add_bry_bval(Html_consts.Apos , Byte_ascii.Apos) + ; + public static String Unescape_as_str(String src) { + Bry_bfr bfr = Bry_bfr.reset_(255); + byte[] bry = Bry_.new_utf8_(src); + Unescape(Bool_.Y, bfr, bry, 0, bry.length, Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y); + return bfr.XtoStrAndClear(); + } + public static byte[] Unescape(boolean write_to_bfr, Bry_bfr bfr, byte[] bry, int bgn, int end, boolean escape_lt, boolean escape_gt, boolean escape_amp, boolean escape_quote, boolean escape_apos) { + if (bry == null) return null; + boolean dirty = write_to_bfr ? true : false; // if write_to_bfr, then mark true, else bfr.Add_mid(bry, 0, i); will write whole bry again + int pos = bgn; + while (pos < end) { + byte b = bry[pos]; + Object o = unescape_trie.Match(b, bry, pos, end); + if (o == null) { + if (dirty || write_to_bfr) + bfr.Add_byte(b); + ++pos; + } + else { + Byte_obj_val unescaped_bval = (Byte_obj_val)o; + byte unescaped_byte = unescaped_bval.Val(); + boolean unescape = false; + switch (unescaped_byte) { + case Byte_ascii.Lt: if (escape_lt) unescape = true; break; + case Byte_ascii.Gt: if (escape_gt) unescape = true; break; + case Byte_ascii.Amp: if (escape_amp) unescape = true; break; + case Byte_ascii.Quote: if (escape_quote) unescape = true; break; + case Byte_ascii.Apos: if (escape_apos) unescape = true; break; + } + if (unescape) { + if (!dirty) { + bfr.Add_mid(bry, bgn, pos); + dirty = true; + } + bfr.Add_byte(unescaped_byte); + } + else { + if (dirty || write_to_bfr) + bfr.Add_byte(b); + } + pos = unescape_trie.Match_pos(); + } + } + if (write_to_bfr) + return null; + else + return dirty ? bfr.XtoAryAndClear() : bry; + } + public static byte[] Del_comments(Bry_bfr bfr, byte[] src) {return Del_comments(bfr, src, 0, src.length);} + public static byte[] Del_comments(Bry_bfr bfr, byte[] src, int pos, int end) { + while (true) { + if (pos >= end) break; + int comm_bgn = Bry_finder.Find_fwd(src, Html_consts.Comm_bgn, pos); // look for + if (comm_end == Bry_finder.Not_found) { // not found; consume rest + bfr.Add_mid(src, pos, end); + break; + } + bfr.Add_mid(src, pos, comm_bgn); // add everything between pos and comm_bgn + pos = comm_end + Html_consts.Comm_end_len; // reposition pos after comm_end + } + return bfr.XtoAryAndClear(); + } +} diff --git a/400_xowa/src/gplx/html/Html_utl_tst.java b/400_xowa/src/gplx/html/Html_utl_tst.java new file mode 100644 index 000000000..b1d1b7a2e --- /dev/null +++ b/400_xowa/src/gplx/html/Html_utl_tst.java @@ -0,0 +1,62 @@ +/* +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.html; import gplx.*; +import org.junit.*; +public class Html_utl_tst { + @Before public void init() {fxt.Clear();} private Html_utl_fxt fxt = new Html_utl_fxt(); + @Test public void Basic() {fxt.Test_del_comments("ac" , "ac");} + @Test public void Bgn_missing() {fxt.Test_del_comments("a b c" , "a b c");} + @Test public void End_missing() {fxt.Test_del_comments("ace" , "ace");} + @Test public void Escape() { + fxt.Test_escape_html(Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, "a present + fxt.Test_escape_html(Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, "a<>'&\"b" , "a<>'&"b"); + } + @Test public void Escape_for_atr_val() { + fxt.Test_escape_for_atr("abc" , Bool_.N, "abc"); // basic + fxt.Test_escape_for_atr("a'\"b" , Bool_.Y, "a'\"b"); // quote is ' + fxt.Test_escape_for_atr("a'\"b" , Bool_.N, "a'"b"); // quote is " + } + @Test public void Unescape() { + fxt.Test_unescape_html(Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, Bool_.Y, "a<>'&"b" , "a<>'&\"b"); // basic + } +} +class Html_utl_fxt { + private Bry_bfr tmp_bfr = Bry_bfr.reset_(255); + public void Clear() { + tmp_bfr.Clear(); + } + public void Test_del_comments(String src, String expd) { + byte[] actl = Html_utl.Del_comments(tmp_bfr, Bry_.new_utf8_(src)); + Tfds.Eq(expd, String_.new_ascii_(actl)); + } + public void Test_escape_html(boolean lt, boolean gt, boolean amp, boolean quote, boolean apos, String src, String expd) { + byte[] actl = Html_utl.Escape_html_as_bry(Bry_.new_ascii_(src), lt, gt, amp, quote, apos); + Tfds.Eq(expd, String_.new_ascii_(actl)); + } + public void Test_escape_for_atr(String src, boolean quote_is_apos, String expd) { + byte[] actl = Html_utl.Escape_for_atr_val_as_bry(src, quote_is_apos ? Byte_ascii.Apos : Byte_ascii.Quote); + Tfds.Eq(expd, String_.new_utf8_(actl)); + } + public void Test_unescape_html(boolean lt, boolean gt, boolean amp, boolean quote, boolean apos, String src, String expd) { + byte[] bry = Bry_.new_utf8_(src); + byte[] actl = Html_utl.Unescape(false, tmp_bfr, bry, 0, bry.length, lt, gt, amp, quote, apos); + Tfds.Eq(expd, String_.new_ascii_(actl)); + } +} diff --git a/400_xowa/src/gplx/html/Html_wtr.java b/400_xowa/src/gplx/html/Html_wtr.java new file mode 100644 index 000000000..1ec307875 --- /dev/null +++ b/400_xowa/src/gplx/html/Html_wtr.java @@ -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 . +*/ +package gplx.html; import gplx.*; +public class Html_wtr { + private Bry_bfr bfr = Bry_bfr.reset_(255); + private ListAdp nde_stack = ListAdp_.new_(); + public byte Atr_quote() {return atr_quote;} public Html_wtr Atr_quote_(byte v) {atr_quote = v; return this;} private byte atr_quote = Byte_ascii.Quote; + public Html_wtr Nde_full_atrs(byte[] tag, byte[] text, boolean text_escape, byte[]... atrs) { + Nde_bgn(tag); + int atrs_len = atrs.length; + for (int i = 0; i < atrs_len; i += 2) { + byte[] key = atrs[i]; + byte[] val = atrs[i + 1]; + Atr(key, val); + } + Nde_end_hdr(); + if (text_escape) + Txt(text); + else + bfr.Add(text); + Nde_end(); + return this; + } + public Html_wtr Nde_full(byte[] tag, byte[] text) { + Nde_bgn_hdr(tag); + Txt(text); + Nde_end(); + return this; + } + public Html_wtr Txt_mid(byte[] src, int bgn, int end) {bfr.Add_mid(src, bgn, end); return this;} + public Html_wtr Txt_byte(byte v) {bfr.Add_byte(v); return this;} + public Html_wtr Txt_raw(byte[] v) {bfr.Add(v); return this;} + public Html_wtr Txt(byte[] v) { + if (v != null) { + bfr.Add(Html_utl.Escape_html_as_bry(v)); + } + return this; + } + public Html_wtr Nde_bgn_hdr(byte[] name) { + this.Nde_bgn(name); + this.Nde_end_hdr(); + return this; + } + public Html_wtr Nde_bgn(byte[] name) { + bfr.Add_byte(Byte_ascii.Lt); + bfr.Add(name); + nde_stack.Add(name); + return this; + } + public Html_wtr Atr(byte[] key, byte[] val) { + Write_atr(bfr, atr_quote, key, val); + return this; + } + public Html_wtr Nde_end_inline() { + bfr.Add_byte(Byte_ascii.Slash).Add_byte(Byte_ascii.Gt); + nde_stack.PopLast(); + return this; + } + public Html_wtr Nde_end_hdr() { + bfr.Add_byte(Byte_ascii.Gt); + return this; + } + public Html_wtr Nde_end() { + byte[] name = (byte[])nde_stack.PopLast(); + bfr.Add_byte(Byte_ascii.Lt).Add_byte(Byte_ascii.Slash); + bfr.Add(name); + bfr.Add_byte(Byte_ascii.Gt); + return this; + } + public byte[] X_to_bry_and_clear() {return bfr.XtoAryAndClear();} + public byte[] X_to_bry() {return bfr.XtoAry();} + public String X_to_str() {return bfr.XtoStr();} + public static void Write_atr(Bry_bfr bfr, byte[] key, byte[] val) {Write_atr(bfr, Byte_ascii.Quote, key, val);} + public static void Write_atr(Bry_bfr bfr, byte atr_quote, byte[] key, byte[] val) { + if (Bry_.Len_eq_0(val)) return; // don't write empty + bfr.Add_byte_space(); + bfr.Add(key); + bfr.Add_byte(Byte_ascii.Eq); + bfr.Add_byte(atr_quote); + Html_utl.Escape_html_to_bfr(bfr, val, 0, val.length, false, false, false, true, true); + bfr.Add_byte(atr_quote); + } +} diff --git a/400_xowa/src/gplx/ios/Io_stream_rdr_process.java b/400_xowa/src/gplx/ios/Io_stream_rdr_process.java new file mode 100644 index 000000000..8824ef574 --- /dev/null +++ b/400_xowa/src/gplx/ios/Io_stream_rdr_process.java @@ -0,0 +1,66 @@ +/* +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.ios; import gplx.*; +import java.io.InputStream; +public class Io_stream_rdr_process implements Io_stream_rdr { + private Process process; + private InputStream stream_read; + private String[] process_args; + Io_stream_rdr_process(Io_url process_exe, Io_url stream_url, String[] process_args) {this.process_exe = process_exe; this.url = stream_url; this.process_args = process_args;} + public byte Tid() {return Io_stream_.Tid_bzip2;} // for now, classify as bzip2; not sure if separate tid is necessary + public Io_url Url() {return url;} public Io_stream_rdr Url_(Io_url v) {url = v; return this;} private Io_url url; + public long Len() {return len;} public Io_stream_rdr Len_(long v) {len = v; return this;} private long len; + public Io_url Process_exe() {return process_exe;} private Io_url process_exe; + public Io_stream_rdr Open() { + ProcessBuilder pb = new ProcessBuilder(process_args); + pb.redirectErrorStream(false); + try {process = pb.start();} + catch (Exception e) {throw Err_.err_(e, "process init failed: {0} {1}", String_.AryXtoStr(process_args), Err_.Message_gplx_brief(e));} + stream_read = process.getInputStream(); + return this; + } + public void Open_mem(byte[] v) {throw Err_.not_implemented_();} + public Object Under() {throw Err_.not_implemented_();} + + public int Read(byte[] bry, int bgn, int len) { + try { + int rv = 0; + int cur_pos = bgn; + int cur_len = len; + while (true) { + int read = stream_read.read(bry, cur_pos, cur_len); + if (read <= 0) break; + rv += read; + cur_pos += read; + cur_len -= read; + if (rv >= len) break; + } + return rv; + } catch (Exception e) {throw Err_.err_(e, "process read failed: bgn={0} len={1} err={2}", bgn, len, Err_.Message_gplx_brief(e));} + } + public long Skip(long len) { + try {return stream_read.skip(len);} + catch (Exception e) {throw Err_.err_(e, "process skip failed: len={0} err={1}", len, Err_.Message_gplx_brief(e));} + } + public void Rls() { + try {stream_read.close();} + catch (Exception e) {throw Err_.err_(e, "process rls failed: err={0}", Err_.Message_gplx_brief(e));} + process.destroy(); + } + public static Io_stream_rdr_process new_(Io_url process_exe, Io_url stream_url, String... process_args) {return new Io_stream_rdr_process(process_exe, stream_url, process_args);} +} diff --git a/400_xowa/src/gplx/ios/Io_stream_zip_mgr.java b/400_xowa/src/gplx/ios/Io_stream_zip_mgr.java new file mode 100644 index 000000000..4ea925a31 --- /dev/null +++ b/400_xowa/src/gplx/ios/Io_stream_zip_mgr.java @@ -0,0 +1,54 @@ +/* +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.ios; import gplx.*; +public class Io_stream_zip_mgr { + private Bry_bfr bfr = Bry_bfr.reset_(256); + private Io_stream_wtr wtr_gzip, wtr_zip, wtr_bzip2; + private Io_stream_rdr rdr_gzip, rdr_zip, rdr_bzip2; + public byte[] Zip(byte type, byte[] val) { + if (type == Io_stream_.Tid_file) return val; + Io_stream_wtr wtr = Wtr(type); + wtr.Write(val, 0, val.length); + wtr.Flush(); + return wtr.Xto_ary_and_clear(); + } + public byte[] Unzip(byte type, byte[] val) { + if (type == Io_stream_.Tid_file) return val; + Io_stream_rdr rdr = Rdr(type); + rdr.Open_mem(val); + return Io_stream_rdr_.Load_all_as_bry(bfr, rdr); + } + private Io_stream_wtr Wtr(byte type) { + switch (type) { + case Io_stream_.Tid_gzip : if (wtr_gzip == null) wtr_gzip = Io_stream_wtr_.new_by_mem(bfr, Io_stream_.Tid_gzip) ; return wtr_gzip.Open(); + case Io_stream_.Tid_zip : if (wtr_zip == null) wtr_zip = Io_stream_wtr_.new_by_mem(bfr, Io_stream_.Tid_zip) ; return wtr_zip.Open(); + case Io_stream_.Tid_bzip2 : if (wtr_bzip2 == null) wtr_bzip2 = Io_stream_wtr_.new_by_mem(bfr, Io_stream_.Tid_bzip2) ; return wtr_bzip2.Open(); + case Io_stream_.Tid_file : + default : throw Err_.unhandled(type); + } + } + private Io_stream_rdr Rdr(byte type) { + switch (type) { + case Io_stream_.Tid_gzip : if (rdr_gzip == null) rdr_gzip = Io_stream_rdr_.new_by_tid_(Io_stream_.Tid_gzip) ; return rdr_gzip; + case Io_stream_.Tid_zip : if (rdr_zip == null) rdr_zip = Io_stream_rdr_.new_by_tid_(Io_stream_.Tid_zip) ; return rdr_zip; + case Io_stream_.Tid_bzip2 : if (rdr_bzip2 == null) rdr_bzip2 = Io_stream_rdr_.new_by_tid_(Io_stream_.Tid_bzip2) ; return rdr_bzip2; + case Io_stream_.Tid_file : + default : throw Err_.unhandled(type); + } + } +} diff --git a/400_xowa/src/gplx/json/Json_doc.java b/400_xowa/src/gplx/json/Json_doc.java new file mode 100644 index 000000000..16834bfd1 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_doc.java @@ -0,0 +1,65 @@ +/* +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.json; import gplx.*; +public class Json_doc { + public void Ctor(byte[] src, Json_itm_nde root) {this.src = src; this.root = root;} + public Bry_bfr Bfr() {return bfr;} Bry_bfr bfr = Bry_bfr.new_(); + public NumberParser Utl_num_parser() {return utl_num_parser;} NumberParser utl_num_parser = new NumberParser(); + public byte[] Str_utf8_bry() {return str_utf8_bry;} private byte[] str_utf8_bry = new byte[6]; + public byte[] Src() {return src;} private byte[] src; + public Json_itm_nde Root() {return root;} Json_itm_nde root; + 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, qry_bry, qry_bry.length - 1, 0); + return nde == null || nde.Tid() != Json_itm_.Tid_string ? 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, qry_bry, qry_bry.length - 1, 0); + return nde == null || nde.Tid() != Json_itm_.Tid_string ? or : (String)nde.Data(); + } + public Json_grp Get_grp(byte[] qry_bry) { + tmp_qry_bry[0] = qry_bry; + Json_itm rv = Find_nde(root, tmp_qry_bry, 0, 0); if (rv == null) return null; + return (Json_grp)rv; + } byte[][] tmp_qry_bry = new byte[1][]; + public Json_grp Get_grp(byte[][] qry_bry) { + Json_itm rv = Find_nde(root, 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, tmp_qry_bry, 0, 0); + } + private Json_itm Find_nde(Json_itm_nde owner, byte[][] paths, int paths_last, int paths_idx) { + byte[] path = paths[paths_idx]; + int subs_len = owner.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Json_itm_kv itm = Json_itm_kv.cast_(owner.Subs_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_itm_nde sub_nde = Json_itm_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 Json_doc new_apos_concat_nl(String... ary) {return new_apos_(String_.Concat_lines_nl(ary));} + public static Json_doc new_apos_(String v) {return new_(Bry_.Replace(Bry_.new_utf8_(v), Byte_ascii.Apos, Byte_ascii.Quote));} + public static Json_doc new_(String v) {return new_(Bry_.new_utf8_(v));} + public static Json_doc new_(byte[] v) {return parser.Parse(v);} static Json_parser parser = new Json_parser(); +} \ No newline at end of file diff --git a/400_xowa/src/gplx/json/Json_doc_bldr.java b/400_xowa/src/gplx/json/Json_doc_bldr.java new file mode 100644 index 000000000..8319bd1dc --- /dev/null +++ b/400_xowa/src/gplx/json/Json_doc_bldr.java @@ -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 . +*/ +package gplx.json; import gplx.*; +public class Json_doc_bldr { + public Json_itm_nde Nde() {return factory.Nde(-1);} + public Json_itm_nde Nde(Json_grp owner) { + Json_itm_nde rv = factory.Nde(-1); + owner.Subs_add(rv); + return rv; + } + public Json_itm Str(byte[] v) {return Str(String_.new_utf8_(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_itm_kv Kv_int(Json_grp owner, String key, int val) {Json_itm_kv rv = factory.Kv(Json_itm_tmp.new_str_(key), Json_itm_tmp.new_int_(val)); owner.Subs_add(rv); return rv;} + public Json_itm_kv Kv_str(Json_grp owner, String key, String val) {Json_itm_kv rv = factory.Kv(Json_itm_tmp.new_str_(key), Json_itm_tmp.new_str_(val)); owner.Subs_add(rv); return rv;} + public Json_itm_ary Kv_ary(Json_grp owner, String key, Json_itm... subs) { + Json_itm key_itm = Json_itm_tmp.new_str_(key); + Json_itm_ary val_ary = factory.Ary(-1, -1); + Json_itm_kv kv = factory.Kv(key_itm, val_ary); + owner.Subs_add(kv); + int len = subs.length; + for (int i = 0; i < len; i++) + val_ary.Subs_add(subs[i]); + return val_ary; + } + Json_doc doc = new Json_doc(); Json_factory factory = new Json_factory(); +} diff --git a/400_xowa/src/gplx/json/Json_doc_srl.java b/400_xowa/src/gplx/json/Json_doc_srl.java new file mode 100644 index 000000000..b7a6bb6e3 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_doc_srl.java @@ -0,0 +1,89 @@ +/* +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.json; import gplx.*; +public class Json_doc_srl { + private int indent = -1; + private Bry_bfr bfr = Bry_bfr.reset_(255); + public boolean Ws_enabled() {return ws_enabled;} public void Ws_enabled_(boolean v) {ws_enabled = v;} private boolean ws_enabled = false; + public byte[] Bld() {return bfr.XtoAryAndClear();} + public String Bld_as_str() {return bfr.XtoStrAndClear();} + public Json_doc_srl Write_root(byte[] key, Object val) { + Write_nde_bgn(); + Write_obj(false, key, val); + Write_nde_end(); + return this; + } + public void Write_obj(boolean comma, byte[] key, Object val) { + Class t = ClassAdp_.ClassOf_obj(val); + if (ClassAdp_.Is_array(t)) + Write_kv_ary(comma, key, (Object[])val); + else + Write_kv_str(comma, key, Object_.XtoStr_OrEmpty(val)); + } + private void Write_kv_ary(boolean comma, byte[] key, Object[] val) { + Write_key(comma, key); Write_new_line(); // '"key":\n' + Write_ary_bgn(); // '[\n' + Indent_add(); // --> + int len = val.length; + for (int i = 0; i < len; i++) { + Write_itm_hdr(i != 0); // ', ' + Write_str(Bry_.new_utf8_(Object_.XtoStr_OrNull(val[i]))); + Write_new_line(); + } + Indent_del(); + Write_ary_end(); + } + private void Write_kv_str(boolean comma, byte[] key, String val) { + Write_key(comma, key); // "key": + Write_str(Bry_.new_utf8_(val)); // "val" + Write_new_line(); // \n + } + private void Write_key(boolean comma, byte[] key) { // "key": + Write_indent(); + Write_str(key); + bfr.Add_byte(Byte_ascii.Colon); + } + private void Write_indent() {if (ws_enabled && indent > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent);} + private void Write_str(byte[] v) { + if (v == null) + bfr.Add(Bry_null); + else + bfr.Add_byte(Byte_ascii.Quote).Add(v).Add_byte(Byte_ascii.Quote); + } private static final byte[] Bry_null = Bry_.new_ascii_("null"); + private void Write_comma(boolean comma) { + if (comma) + bfr.Add_byte(Byte_ascii.Comma); + else { + if (ws_enabled) + bfr.Add_byte(Byte_ascii.Space); + } + if (ws_enabled) + bfr.Add_byte(Byte_ascii.Space); + } + private void Write_ary_bgn() {Indent_add(); Write_indent(); bfr.Add_byte(Byte_ascii.Brack_bgn); Write_new_line();} + private void Write_ary_end() { Write_indent(); bfr.Add_byte(Byte_ascii.Brack_end); Write_new_line(); Indent_del();} + private void Write_nde_bgn() {Indent_add(); Write_indent(); bfr.Add_byte(Byte_ascii.Curly_bgn); Write_new_line();} + private void Write_nde_end() { Write_indent(); bfr.Add_byte(Byte_ascii.Curly_end); Write_new_line(); Indent_del();} + private void Write_itm_hdr(boolean comma) { + Write_indent(); + Write_comma(comma); + } + private void Indent_add() {indent += 2;} + private void Indent_del() {indent -= 2;} + private void Write_new_line() {if (ws_enabled) bfr.Add_byte_nl();} +} diff --git a/400_xowa/src/gplx/json/Json_doc_tst.java b/400_xowa/src/gplx/json/Json_doc_tst.java new file mode 100644 index 000000000..248c7b828 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_doc_tst.java @@ -0,0 +1,45 @@ +/* +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.json; import gplx.*; +import org.junit.*; +public class Json_doc_tst { + Json_qry_mgr_fxt fxt = new Json_qry_mgr_fxt(); + @Before public void init() {} + @Test public void Select() { + Json_doc doc = Json_doc.new_apos_(String_.Concat_lines_nl + ( "{'0':" + , " {'0_0':" + , " {'0_0_0':'000'" + , " }," + , " '0_1':" + , " {'0_1_0':'010'" + , " }" + , " }" + , "}" + )); + fxt.Test_get_val_as_str(doc, "0/0_0/0_0_0", "000"); + fxt.Test_get_val_as_str(doc, "0/0_1/0_1_0", "010"); + fxt.Test_get_val_as_str(doc, "x", null); + } +} +class Json_qry_mgr_fxt { + public void Test_get_val_as_str(Json_doc doc, String qry, String expd){ + byte[][] qry_bry = Bry_.Split(Bry_.new_utf8_(qry), Byte_ascii.Slash); + Tfds.Eq(expd, doc.Get_val_as_str_or(qry_bry, null)); + } +} diff --git a/400_xowa/src/gplx/json/Json_doc_wtr.java b/400_xowa/src/gplx/json/Json_doc_wtr.java new file mode 100644 index 000000000..fc06091c5 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_doc_wtr.java @@ -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 . +*/ +package gplx.json; import gplx.*; +public class Json_doc_wtr { + private int indent = -2; + private Bry_bfr bfr = Bry_bfr.reset_(255); + 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(Bry_null); + else + bfr.Add_byte(Byte_ascii.Quote).Add(v).Add_byte(Byte_ascii.Quote); + return this; + } private static final byte[] Bry_null = Bry_.new_ascii_("null"); + 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, 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.XtoAryAndClear();} + public String Bld_as_str() {return bfr.XtoStrAndClear();} +} diff --git a/400_xowa/src/gplx/json/Json_factory.java b/400_xowa/src/gplx/json/Json_factory.java new file mode 100644 index 000000000..5732e9e98 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_factory.java @@ -0,0 +1,29 @@ +/* +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.json; import gplx.*; +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 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_itm_kv Kv(Json_itm key, Json_itm val) {return new Json_itm_kv(key, val);} + public Json_itm_ary Ary(int bgn, int end) {return new Json_itm_ary(bgn, end);} + public Json_itm_nde Nde(int bgn) {return new Json_itm_nde(bgn);} +} diff --git a/400_xowa/src/gplx/json/Json_grp.java b/400_xowa/src/gplx/json/Json_grp.java new file mode 100644 index 000000000..e4bcb4ff7 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_grp.java @@ -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 . +*/ +package gplx.json; import gplx.*; +public interface Json_grp extends Json_itm { + void Src_end_(int v); + int Subs_len(); + Json_itm Subs_get_at(int i); + void Subs_add(Json_itm itm); +} +class Json_grp_ { + public static final Json_grp[] Ary_empty = new Json_grp[0]; + public static void Print_nl(Bry_bfr bfr) { // \n\n can be caused by nested groups (EX: "[[]]"); only print 1 + if (bfr.Bfr()[bfr.Len() - 1] != Byte_ascii.NewLine) + bfr.Add_byte_nl(); + } + public static void Print_indent(Bry_bfr bfr, int depth) { + if (depth > 0) bfr.Add_byte_repeat(Byte_ascii.Space, depth * 2); // indent + } +} diff --git a/400_xowa/src/gplx/json/Json_itm.java b/400_xowa/src/gplx/json/Json_itm.java new file mode 100644 index 000000000..7d27490c2 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm.java @@ -0,0 +1,116 @@ +/* +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.json; import gplx.*; +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(Bry_null);} + @Override public byte[] Data_bry() {return Bry_null;} + private static final byte[] Bry_null = Bry_.new_ascii_("null"); + public static Json_itm_null Null = new Json_itm_null(); +} +class Json_itm_bool extends Json_itm_base { + public Json_itm_bool(boolean data) {this.data = data; this.Ctor(-1, -1);} private boolean data; + @Override public byte Tid() {return Json_itm_.Tid_bool;} + @Override public Object Data() {return data;} + @Override public byte[] Data_bry() {return data ? Json_itm_.Const_true : Json_itm_.Const_false;} + @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add(data ? Json_itm_.Const_true: Json_itm_.Const_false);} + public static Json_itm_bool Bool_n = new Json_itm_bool(false), Bool_y = new Json_itm_bool(true); +} +class Json_itm_decimal extends Json_itm_base { + public Json_itm_decimal(Json_doc doc, int src_bgn, int src_end) {this.Ctor(src_bgn, src_end); this.doc = doc;} Json_doc doc; + @Override public byte Tid() {return Json_itm_.Tid_decimal;} + @Override public Object Data() { + if (data == null) + data = DecimalAdp_.parse_(String_.new_ascii_(this.Data_bry())); + return data; + } DecimalAdp data; + @Override public byte[] Data_bry() { + if (data_bry == null) data_bry = Bry_.Mid(doc.Src(), this.Src_bgn(), this.Src_end()); + return data_bry; + } byte[] data_bry; + @Override public void Print_as_json(Bry_bfr bfr, int depth) {bfr.Add_mid(doc.Src(), this.Src_bgn(), this.Src_end());} +} +class Json_itm_str extends Json_itm_base { + 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;} private boolean exact; Json_doc doc; + @Override public byte Tid() {return Json_itm_.Tid_string;} + @Override public void Print_as_json(Bry_bfr bfr, int depth) { + bfr.Add_byte(Byte_ascii.Quote); + gplx.html.Html_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() { + if (data_str == null) { + if (data_bry == null) + data_bry = Data_make_bry(); + data_str = String_.new_utf8_(data_bry); + } + return data_str; + } private String 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(comp, doc.Src(), this.Src_bgn(), this.Src_end()); + if (data_bry == null) data_bry = Data_make_bry(); + return Bry_.Match(data_bry, comp); + } byte[] data_bry = null; + 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.Str_utf8_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.NewLine); break; + case Byte_ascii.Ltr_r: bfr.Add_byte(Byte_ascii.CarriageReturn); 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: + int utf8_val = gplx.texts.HexDecUtl.parse_or_(src, i + 1, i + 5, -1); + int len = gplx.intl.Utf16_.Encode_int(utf8_val, utf8_bry, 0); + bfr.Add_mid(utf8_bry, 0, len); + i += 4; + break; // \uFFFF 4 hex-dec + 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.XtoAryAndClear(); + } +} diff --git a/400_xowa/src/gplx/json/Json_itm_.java b/400_xowa/src/gplx/json/Json_itm_.java new file mode 100644 index 000000000..8d195f653 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_.java @@ -0,0 +1,24 @@ +/* +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.json; import gplx.*; +public class Json_itm_ { + public static final Json_itm[] Ary_empty = new Json_itm[0]; + public static final byte Tid_unknown = 0, Tid_null = 1, Tid_bool = 2, Tid_int = 3, Tid_decimal = 4, Tid_string = 5, Tid_kv = 6, Tid_array = 7, Tid_nde = 8; + public static final byte[][] Names = Bry_.Ary("unknown", "null", "boolean", "int", "decimal", "string", "keyval", "array", "nde"); + public static final byte[] Const_true = Bry_.new_ascii_("true"), Const_false = Bry_.new_ascii_("false"), Const_null = Bry_.new_ascii_("null"); +} diff --git a/400_xowa/src/gplx/json/Json_itm_ary.java b/400_xowa/src/gplx/json/Json_itm_ary.java new file mode 100644 index 000000000..b5b3c57dc --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_ary.java @@ -0,0 +1,64 @@ +/* +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.json; import gplx.*; +public class Json_itm_ary extends Json_itm_base implements Json_grp { + public Json_itm_ary(int src_bgn, int src_end) {this.Ctor(src_bgn, src_end);} + @Override public byte Tid() {return Json_itm_.Tid_array;} + 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 Subs_len() {return subs_len;} private int subs_len = 0, subs_max = 0; + public Json_itm Subs_get_at(int i) {return subs[i];} + public Json_itm_ary Subs_add_many(Json_itm... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Subs_add(ary[i]); + return this; + } + public void Subs_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_.CopyTo(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(); + } + Json_itm[] subs = Json_itm_.Ary_empty; + public static Json_itm_ary cast_(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid_array ? null : (Json_itm_ary)v;} +} diff --git a/400_xowa/src/gplx/json/Json_itm_base.java b/400_xowa/src/gplx/json/Json_itm_base.java new file mode 100644 index 000000000..bdb526ff3 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_base.java @@ -0,0 +1,29 @@ +/* +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.json; import gplx.*; +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.XtoStrAndClear();} + public abstract void Print_as_json(Bry_bfr bfr, int depth); + @gplx.Virtual public boolean Data_eq(byte[] comp) {return false;} +} diff --git a/400_xowa/src/gplx/json/Json_itm_int.java b/400_xowa/src/gplx/json/Json_itm_int.java new file mode 100644 index 000000000..63f13b763 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_int.java @@ -0,0 +1,33 @@ +/* +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.json; import gplx.*; +public class Json_itm_int extends Json_itm_base { + public Json_itm_int(Json_doc doc, int src_bgn, int src_end) {this.Ctor(src_bgn, src_end); this.doc = doc;} Json_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()).AsInt(); + data_is_null = false; + } + return data; + } + @Override public Object Data() {return Data_as_int();} int data; boolean data_is_null = true; + @Override public byte[] Data_bry() {if (data_bry == null) data_bry = Bry_.Mid(doc.Src(), this.Src_bgn(), this.Src_end()); return data_bry;} private byte[] 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;} +} diff --git a/400_xowa/src/gplx/json/Json_itm_kv.java b/400_xowa/src/gplx/json/Json_itm_kv.java new file mode 100644 index 000000000..b353824a2 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_kv.java @@ -0,0 +1,35 @@ +/* +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.json; import gplx.*; +public class Json_itm_kv extends Json_itm_base { + public Json_itm_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;} Json_itm key; + public Json_itm Val() {return val;} Json_itm val; + public String Key_as_str() {return (String)key.Data();} + 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_itm_kv[] Ary_empty = new Json_itm_kv[0]; + public static Json_itm_kv cast_(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid_kv ? null : (Json_itm_kv)v;} +} diff --git a/400_xowa/src/gplx/json/Json_itm_nde.java b/400_xowa/src/gplx/json/Json_itm_nde.java new file mode 100644 index 000000000..714e250ed --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_nde.java @@ -0,0 +1,72 @@ +/* +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.json; import gplx.*; +public class Json_itm_nde extends Json_itm_base implements Json_grp { + public Json_itm_nde(int src_bgn) {this.Ctor(src_bgn, -1);} + @Override public byte Tid() {return Json_itm_.Tid_nde;} + 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 Subs_len() {return subs_len;} private int subs_len = 0, subs_max = 0; + public Json_itm Subs_get_at(int i) {return subs[i];} + public Json_itm Subs_get_by_key(byte[] key) { + for (int i = 0; i < subs_len; i++) { + Json_itm itm = subs[i]; + if (itm.Tid() == Json_itm_.Tid_kv) { + Json_itm_kv itm_as_kv = (Json_itm_kv)itm; + if (Bry_.Eq(key, itm_as_kv.Key().Data_bry())) + return itm; + } + } + return null; + } + public Json_itm_nde Subs_add_many(Json_itm... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Subs_add(ary[i]); + return this; + } + public void Subs_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_.CopyTo(subs, 0, new_subs, 0, subs_len); + subs = new_subs; + } + subs[subs_len] = (Json_itm)itm; + subs_len = new_len; + } + @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(); + } + Json_itm[] subs = Json_itm_.Ary_empty; + public static Json_itm_nde cast_(Json_itm v) {return v == null || v.Tid() != Json_itm_.Tid_nde ? null : (Json_itm_nde)v;} +} diff --git a/400_xowa/src/gplx/json/Json_itm_tmp.java b/400_xowa/src/gplx/json/Json_itm_tmp.java new file mode 100644 index 000000000..99fec3778 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_itm_tmp.java @@ -0,0 +1,31 @@ +/* +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.json; import gplx.*; +public class Json_itm_tmp implements Json_itm { + 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_utf8_(Object_.XtoStr_OrEmpty(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(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_string, "\"" + v + "\"");} + public static Json_itm new_int_(int v) {return new Json_itm_tmp(Json_itm_.Tid_int, Int_.XtoStr(v));} +} diff --git a/400_xowa/src/gplx/json/Json_kv_ary_srl.java b/400_xowa/src/gplx/json/Json_kv_ary_srl.java new file mode 100644 index 000000000..64f5effc0 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_kv_ary_srl.java @@ -0,0 +1,61 @@ +/* +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.json; import gplx.*; +public class Json_kv_ary_srl { + public static KeyVal Kv_by_itm(Json_itm itm) { + switch (itm.Tid()) { + case Json_itm_.Tid_kv: + Json_itm_kv kv = (Json_itm_kv)itm; + return KeyVal_.new_(kv.Key_as_str(), Val_by_itm(kv.Val())); + default: + throw Err_.unhandled(itm.Tid()); + } + } + private static Object Val_by_itm(Json_itm itm) { + switch (itm.Tid()) { + case Json_itm_.Tid_bool: return Bool_.XtoStr_lower(Bool_.cast_(itm.Data())); + case Json_itm_.Tid_int: + case Json_itm_.Tid_null: + case Json_itm_.Tid_string: + case Json_itm_.Tid_decimal: return itm.Data(); + case Json_itm_.Tid_array: return Val_by_itm_ary((Json_itm_ary)itm); + case Json_itm_.Tid_nde: return Val_by_itm_nde((Json_itm_nde)itm); + case Json_itm_.Tid_kv: // kv should never be val; EX: "a":"b":c; not possible + default: throw Err_.unhandled(itm.Tid()); + } + } + private static KeyVal[] Val_by_itm_ary(Json_itm_ary itm) { + int subs_len = itm.Subs_len(); + KeyVal[] rv = new KeyVal[subs_len]; + for (int i = 0; i < subs_len; i++) { + Json_itm sub = itm.Subs_get_at(i); + KeyVal kv = KeyVal_.new_(Int_.XtoStr(i + Int_.Base1), Val_by_itm(sub)); + rv[i] = kv; + } + return rv; + } + public static KeyVal[] Val_by_itm_nde(Json_itm_nde itm) { + int subs_len = itm.Subs_len(); + KeyVal[] rv = new KeyVal[subs_len]; + for (int i = 0; i < subs_len; i++) { + Json_itm sub = itm.Subs_get_at(i); + rv[i] = Kv_by_itm(sub); + } + return rv; + } +} diff --git a/400_xowa/src/gplx/json/Json_kv_ary_srl_tst.java b/400_xowa/src/gplx/json/Json_kv_ary_srl_tst.java new file mode 100644 index 000000000..923966645 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_kv_ary_srl_tst.java @@ -0,0 +1,50 @@ +/* +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.json; import gplx.*; +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 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", DecimalAdp_.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_utf8_(raw_str)); + Json_doc doc = parser.Parse(raw_bry); + KeyVal[] actl = Json_kv_ary_srl.Val_by_itm_nde(doc.Root()); + Tfds.Eq_str_lines(KeyVal_.Ary_x_to_str(expd), KeyVal_.Ary_x_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_.XtoStr_lower(val));} + public KeyVal kv_dec_(String key, DecimalAdp val) {return KeyVal_.new_(key, val.XtoStr());} +} diff --git a/400_xowa/src/gplx/json/Json_parser.java b/400_xowa/src/gplx/json/Json_parser.java new file mode 100644 index 000000000..92042bce7 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_parser.java @@ -0,0 +1,167 @@ +/* +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.json; import gplx.*; +public class Json_parser { + public Json_factory Factory() {return factory;} Json_factory factory = new Json_factory(); + private byte[] src; private int src_len, pos; private NumberParser num_parser = new NumberParser(); + private static final byte[] Bry_bool_rue = Bry_.new_ascii_("rue"), Bry_bool_alse = Bry_.new_ascii_("alse"), Bry_null_ull = Bry_.new_ascii_("ull"); + public Json_doc Parse(byte[] src) { + Json_doc doc = new Json_doc(); + this.src = src; this.src_len = src.length; pos = 0; + Skip_ws(); + if (src.length == 0) return null; + if (src[pos] != Byte_ascii.Curly_bgn) return null; + Skip_ws(); +// if (src[pos + 1] != Byte_ascii.Quote) return null; +// throw Err_.new_("doc must start with {"); + Json_itm_nde root = Make_nde(doc); + doc.Ctor(src, root); + return doc; + } + Json_itm_nde Make_nde(Json_doc doc) { + ++pos; // brack_bgn + Json_itm_nde nde = new Json_itm_nde(pos); + while (pos < src_len) { + Skip_ws(); + if (src[pos] == Byte_ascii.Curly_end) {++pos; return nde;} + else nde.Subs_add(Make_kv(doc)); + Skip_ws(); + switch (src[pos++]) { + case Byte_ascii.Comma: break; + case Byte_ascii.Curly_end: return nde; + default: throw Err_.unhandled(src[pos - 1]); + } + } + throw Err_.new_("eos inside nde"); + } + 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_itm_kv(key, val); + } + 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_.unhandled(Char_.XtoStr(b)); + } + throw Err_.new_("eos reached in val"); + } + Json_itm Make_literal(byte[] remainder, int remainder_len, Json_itm singleton) { + ++pos; // 1st char + int literal_end = pos + remainder_len; + if (Bry_.Eq(remainder, src, pos, literal_end)) { + pos = literal_end; + return singleton; + } + throw Err_.new_("invalid literal"); + } + 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 += 4; break; // \uFFFF 4 hex-dec + 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_("eos reached inside quote"); + } + Json_itm Make_num(Json_doc doc) { + int num_bgn = pos; + boolean loop = true; + while (loop) { + if (pos == src_len) throw Err_.new_("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); + return num_parser.HasFrac() + ? factory.Decimal(doc, num_bgn, pos) + : factory.Int(doc, num_bgn, pos); + } + Json_itm_ary Make_ary(Json_doc doc) { + Json_itm_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.Subs_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_("eos inside ary"); + } + private void Skip_ws() { + while (pos < src_len) { + switch (src[pos]) { + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.Tab: case Byte_ascii.CarriageReturn: ++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_.XtoStr(expd), Char_.XtoStr(src[pos])); + } + Err err_(byte[] src, int bgn, String fmt, Object... args) {return err_(src, bgn, src.length, fmt, args);} + Err err_(byte[] src, int bgn, int src_len, String fmt, Object... args) { + String msg = String_.Format(fmt, args) + " " + Int_.XtoStr(bgn) + " " + String_.new_utf8_len_safe_(src, bgn, 20); + return Err_.new_(msg); + } +} diff --git a/400_xowa/src/gplx/json/Json_parser_tst.java b/400_xowa/src/gplx/json/Json_parser_tst.java new file mode 100644 index 000000000..51acc62c8 --- /dev/null +++ b/400_xowa/src/gplx/json/Json_parser_tst.java @@ -0,0 +1,99 @@ +/* +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.json; import gplx.*; +import org.junit.*; +public class Json_parser_tst { + 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_esc_hex4() {fxt.Test_parse_val0("{'k0':'a\\u0021b'}" , "a!b");} + @Test public void Num_dec() {fxt.Test_parse("{'k0':1.23}" , fxt.itm_nde_().Subs_add_many(fxt.itm_kv_dec_("k0", "1.23")));} + @Test public void Num_exp() {fxt.Test_parse("{'k0':1e+2}" , fxt.itm_nde_().Subs_add_many(fxt.itm_kv_dec_("k0", "1e+2")));} + @Test public void Num_mix() {fxt.Test_parse("{'k0':-1.23e-1}" , fxt.itm_nde_().Subs_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_().Subs_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_().Subs_add_many(fxt.itm_kv_ary_int_("k0")));} + @Test public void Ary_int() {fxt.Test_parse("{'k0':[1,2,3]}", fxt.itm_nde_().Subs_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_().Subs_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_().Subs_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_().Subs_add_many(fxt.itm_kv_("k0", fxt.itm_nde_().Subs_add_many(fxt.itm_kv_("k00", 1)))));} + @Test public void Subs_empty() {fxt.Test_parse("{'k0':{}}", fxt.itm_nde_().Subs_add_many(fxt.itm_kv_("k0", fxt.itm_nde_())));} + @Test public void Subs_ws() {fxt.Test_parse("{'k0': { 'k00' : 1 } }", fxt.itm_nde_().Subs_add_many(fxt.itm_kv_("k0", fxt.itm_nde_().Subs_add_many(fxt.itm_kv_("k00", 1)))));} + @Test public void Ws() {fxt.Test_parse(" { 'k0' : 'v0' } ", fxt.itm_nde_().Subs_add_many(fxt.itm_kv_("k0", "v0")));} + public static String Replace_apos_as_str(String v) {return String_.new_utf8_(Replace_apos(Bry_.new_utf8_(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); + 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_itm_ary itm_ary_() {return factory.Ary(-1, -1);} + public Json_itm_nde itm_nde_() {return factory.Nde(-1);} + public Json_itm_kv itm_kv_null_(String k) {return factory.Kv(itm_str_(k), factory.Null());} + public Json_itm_kv itm_kv_(String k, String v) {return factory.Kv(itm_str_(k), itm_str_(v));} + public Json_itm_kv itm_kv_(String k, int v) {return factory.Kv(itm_str_(k), itm_int_(v));} + public Json_itm_kv itm_kv_(String k, boolean v) {return factory.Kv(itm_str_(k), v ? factory.Bool_y() : factory.Bool_n());} + public Json_itm_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_itm_kv itm_kv_(String k, Json_itm_nde v) {return factory.Kv(itm_str_(k), v);} + public Json_itm_kv itm_kv_ary_int_(String k, int... v) { + Json_itm_ary ary = factory.Ary(-1, -1); + int len = v.length; + for (int i = 0; i < len; i++) + ary.Subs_add(itm_int_(v[i])); + return factory.Kv(itm_str_(k), ary); + } + public Json_itm_kv itm_kv_ary_str_(String k, String... v) { + Json_itm_ary ary = factory.Ary(-1, -1); + int len = v.length; + for (int i = 0; i < len; i++) + ary.Subs_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_utf8_(raw_str)); + Json_doc doc = parser.Parse(raw); + doc.Root().Print_as_json(tmp_bfr, 0); + String actl = tmp_bfr.XtoStrAndClear(); + 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_utf8_(raw_str)); + Json_doc doc = parser.Parse(raw); + Json_itm_kv kv = Json_itm_kv.cast_(doc.Root().Subs_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.XtoStrAndClear(); + } +} diff --git a/400_xowa/src/gplx/php/Php_ctx.java b/400_xowa/src/gplx/php/Php_ctx.java new file mode 100644 index 000000000..167495169 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_ctx.java @@ -0,0 +1,21 @@ +/* +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.php; import gplx.*; +public class Php_ctx { + public byte[] Src() {return src;} public Php_ctx Src_(byte[] v) {this.src = v; return this;} private byte[] src; +} diff --git a/400_xowa/src/gplx/php/Php_evaluator.java b/400_xowa/src/gplx/php/Php_evaluator.java new file mode 100644 index 000000000..bcf34c0be --- /dev/null +++ b/400_xowa/src/gplx/php/Php_evaluator.java @@ -0,0 +1,263 @@ +/* +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.php; import gplx.*; +/* +NOTE: naive implementation of PHP evaluator. intended only for parsing Messages**.php files in MediaWiki. Specifically, it assumes the following: +- all lines are assignment lines: EX: $a = b; +- only the assignment operator is allowed (=); EX: $a = 5 + 7; fails b/c of + operator; +- no functions are supported: EX: strlen('a') fails +*/ +public class Php_evaluator implements Php_tkn_wkr { + byte mode = Mode_key_bgn, next_tid = 0, next_mode = 0; + Php_line_assign cur_line; Php_itm_ary cur_ary; Php_key cur_kv_key; + ListAdp frame_stack = ListAdp_.new_(); + public Php_evaluator(Gfo_msg_log msg_log) {this.msg_log = msg_log;} Gfo_msg_log msg_log; + public void Init(Php_ctx ctx) {src = ctx.Src(); frame_stack.Clear();} private byte[] src; + public ListAdp List() {return lines;} ListAdp lines = ListAdp_.new_(); + public Gfo_msg_log Msg_log() {return msg_log;} + public void Clear() { + lines.Clear(); msg_log.Clear(); + cur_line = null; + cur_ary = null; + cur_kv_key = null; + mode = Mode_key_bgn; + next_tid = next_mode = 0; + } + public void Process(Php_tkn tkn) { + byte tkn_tid = tkn.Tkn_tid(); + switch (tkn_tid) { + case Php_tkn_.Tid_declaration: case Php_tkn_.Tid_comment: case Php_tkn_.Tid_ws: // always discard, regardless of mode + return; + } + switch (mode) { + case Mode_expect: // handles sequences like "array(" + if (tkn_tid == next_tid) + mode = next_mode; + else { + Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(next_tid), Php_tkn_.Xto_str(tkn_tid)); + Fail(); + } + break; + case Mode_suspend: + if (tkn_tid == Php_tkn_.Tid_semic) mode = Mode_key_bgn; + break; + case Mode_key_bgn: + if (tkn_tid == Php_tkn_.Tid_var) { + cur_ary = null; + cur_line = new Php_line_assign(); + lines.Add(cur_line); + + Php_tkn_var var_tkn = (Php_tkn_var)tkn; + cur_line.Key_(new Php_itm_var(var_tkn.Var_name(src))); + + mode = Mode_key_end; + } + else { + Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(Php_tkn_.Tid_var), Php_tkn_.Xto_str(tkn_tid)); + Fail(); + } + break; + case Mode_key_end: + switch (tkn_tid) { + case Php_tkn_.Tid_eq: mode = Mode_val; break; + case Php_tkn_.Tid_brack_bgn: mode = Mode_brack_itm; break; + case Php_tkn_.Tid_brack_end: Expect(Php_tkn_.Tid_eq, Mode_val); break; + default: { + Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(Php_tkn_.Tid_var), Php_tkn_.Xto_str(tkn_tid)); + Fail(); + break; + } + } + break; + case Mode_brack_itm: + switch (tkn_tid) { + case Php_tkn_.Tid_quote: + Php_tkn_quote tkn_quote = (Php_tkn_quote)tkn; + Php_itm_quote key_sub = new Php_itm_quote(tkn_quote.Quote_text(src)); + cur_line.Key_subs_(new Php_key[] {key_sub}); + mode = Mode_key_end; + break; + default: { + Msg_many(src, tkn.Src_bgn(), tkn.Src_end(), Expecting_itm_failed, Php_tkn_.Xto_str(Php_tkn_.Tid_var), Php_tkn_.Xto_str(tkn_tid)); + Fail(); + break; + } + } + break; + case Mode_val: + Php_itm line_val = null; + switch (tkn_tid) { + case Php_tkn_.Tid_null: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); line_val = Php_itm_null._; break; + case Php_tkn_.Tid_false: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); line_val = Php_itm_bool_false._; break; + case Php_tkn_.Tid_true: Expect(Php_tkn_.Tid_semic, Mode_key_bgn); line_val = Php_itm_bool_true._; break; + case Php_tkn_.Tid_quote: + Expect(Php_tkn_.Tid_semic, Mode_key_bgn); + Php_tkn_quote tkn_quote = (Php_tkn_quote)tkn; + line_val = new Php_itm_quote(tkn_quote.Quote_text(src)); + break; + case Php_tkn_.Tid_ary: + Expect(Php_tkn_.Tid_paren_bgn, Mode_ary_subs); + Php_itm_ary ary = new Php_itm_ary(); + if (cur_ary == null) + line_val = ary; + else { + cur_ary.Subs_add(ary); + frame_stack.Add(new Php_scanner_frame(cur_ary)); + cur_kv_key = null; + } + this.cur_ary = ary; + break; + case Php_tkn_.Tid_txt: + case Php_tkn_.Tid_var: + break; + case Php_tkn_.Tid_eq: + case Php_tkn_.Tid_eq_kv: + case Php_tkn_.Tid_semic: + case Php_tkn_.Tid_comma: + case Php_tkn_.Tid_paren_bgn: + case Php_tkn_.Tid_paren_end: + case Php_tkn_.Tid_num: + break; + } + cur_line.Val_(line_val); + break; + case Mode_ary_subs: + switch (tkn_tid) { + case Php_tkn_.Tid_null: Ary_add_itm(Php_itm_null._); break; + case Php_tkn_.Tid_false: Ary_add_itm(Php_itm_bool_false._); break; + case Php_tkn_.Tid_true: Ary_add_itm(Php_itm_bool_true._); break; + case Php_tkn_.Tid_quote: + Php_tkn_quote tkn_quote = (Php_tkn_quote)tkn; + Ary_add_itm(new Php_itm_quote(tkn_quote.Quote_text(src))); + break; + case Php_tkn_.Tid_num: + Php_tkn_num tkn_num = (Php_tkn_num)tkn; + Ary_add_itm(new Php_itm_int(tkn_num.Num_val_int(src))); + break; + case Php_tkn_.Tid_var: + Php_tkn_var tkn_var = (Php_tkn_var)tkn; + Ary_add_itm(new Php_itm_var(Bry_.Mid(src, tkn_var.Src_bgn(), tkn_var.Src_end()))); + break; + case Php_tkn_.Tid_txt: + Php_tkn_txt tkn_txt = (Php_tkn_txt)tkn; + Ary_add_itm(new Php_itm_var(Bry_.Mid(src, tkn_txt.Src_bgn(), tkn_txt.Src_end()))); + break; + case Php_tkn_.Tid_ary: + Expect(Php_tkn_.Tid_paren_bgn, Mode_ary_subs); + Php_itm_ary ary = new Php_itm_ary(); + if (cur_ary == null) + line_val = ary; + else { + frame_stack.Add(new Php_scanner_frame(cur_ary)); + if (cur_kv_key == null) + cur_ary.Subs_add(ary); + else { + Php_itm_kv ary_itm = new Php_itm_kv().Key_(cur_kv_key).Val_(ary); + cur_ary.Subs_add(ary_itm); + cur_kv_key = null; + } + } + this.cur_ary = ary; + break; + case Php_tkn_.Tid_paren_end: + mode = Mode_ary_term; + if (frame_stack.Count() == 0) + cur_ary = null; + else { + Php_scanner_frame frame = (Php_scanner_frame)ListAdp_.Pop(frame_stack); + cur_ary = frame.Ary(); + frame.Rls(); + } + break; + case Php_tkn_.Tid_semic: // NOTE: will occur in following construct array(array()); + mode = Mode_key_bgn; + break; + case Php_tkn_.Tid_eq: + case Php_tkn_.Tid_eq_kv: + case Php_tkn_.Tid_comma: + case Php_tkn_.Tid_paren_bgn: + break; + } + break; + case Mode_ary_dlm: + switch (tkn_tid) { + case Php_tkn_.Tid_comma: + mode = Mode_ary_subs; + break; + case Php_tkn_.Tid_paren_end: + mode = Mode_ary_term; + if (frame_stack.Count() == 0) + cur_ary = null; + else { + Php_scanner_frame frame = (Php_scanner_frame)ListAdp_.Pop(frame_stack); + cur_ary = frame.Ary(); + frame.Rls(); + } + break; + case Php_tkn_.Tid_eq_kv: + Php_itm_sub tmp_key = cur_ary.Subs_pop(); + cur_kv_key = (Php_key)tmp_key; + mode = Mode_ary_subs; + break; + } + break; + case Mode_ary_term: + switch (tkn_tid) { + case Php_tkn_.Tid_comma: + case Php_tkn_.Tid_paren_end: // NOTE: paren_end occurs in multiple nests; EX: array(array()) + mode = Mode_ary_subs; + break; + case Php_tkn_.Tid_semic: + mode = Mode_key_bgn; + break; + } + break; + } + } + private void Fail() {mode = Mode_suspend;} + private void Ary_add_itm(Php_itm val) { + mode = Mode_ary_dlm; + if (cur_kv_key == null) + cur_ary.Subs_add((Php_itm_sub)val); + else { + Php_itm_kv ary_itm = new Php_itm_kv().Key_(cur_kv_key).Val_(val); + cur_ary.Subs_add(ary_itm); + cur_kv_key = null; + } + } + private void Expect(byte next_tid, byte next_mode) { + mode = Mode_expect; + this.next_tid = next_tid; + this.next_mode = next_mode; + } + public void Msg_many(byte[] src, int bgn, int end, Gfo_msg_itm itm, Object... args) { + msg_log.Add_itm_many(itm, src, bgn, end, args); + } + public static final Gfo_msg_itm Expecting_itm_failed = Gfo_msg_itm_.new_warn_(Php_parser.Log_nde, "expecting_itm_failed", "expecting_itm ~{0} but got ~{1} instead"); + static final byte Mode_key_bgn = 1, Mode_key_end = 2, Mode_expect = 3, Mode_suspend = 4, Mode_val = 5, Mode_ary_subs = 6, Mode_ary_dlm = 7, Mode_ary_term = 8, Mode_brack_itm = 9; + +} +class Php_scanner_frame { + public Php_scanner_frame(Php_itm_ary ary) {this.ary = ary;} + public Php_itm_ary Ary() {return ary;} Php_itm_ary ary; + public void Rls() {ary = null;} +} +class Php_parser_interrupt { + public static final Php_parser_interrupt Char = new Php_parser_interrupt(); +} + diff --git a/400_xowa/src/gplx/php/Php_itm.java b/400_xowa/src/gplx/php/Php_itm.java new file mode 100644 index 000000000..a9b7d753c --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm.java @@ -0,0 +1,44 @@ +/* +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.php; import gplx.*; +public interface Php_itm { + byte Itm_tid(); + byte[] Val_obj_bry(); +} +class Php_itm_null implements Php_itm, Php_itm_sub { + public byte Itm_tid() {return Php_itm_.Tid_null;} + public byte[] Val_obj_bry() {return null;} + public static final Php_itm_null _ = new Php_itm_null(); Php_itm_null() {} +} +class Php_itm_bool_true implements Php_itm, Php_itm_sub { + public byte Itm_tid() {return Php_itm_.Tid_bool_true;} + public byte[] Val_obj_bry() {return Bry_true;} + public static final Php_itm_bool_true _ = new Php_itm_bool_true(); Php_itm_bool_true() {} + private static final byte[] Bry_true = Bry_.new_ascii_("true"); +} +class Php_itm_bool_false implements Php_itm, Php_itm_sub { + public byte Itm_tid() {return Php_itm_.Tid_bool_false;} + public byte[] Val_obj_bry() {return Bry_true;} + public static final Php_itm_bool_false _ = new Php_itm_bool_false(); Php_itm_bool_false() {} + private static final byte[] Bry_true = Bry_.new_ascii_("false"); +} +class Php_itm_var implements Php_itm, Php_itm_sub, Php_key { + public Php_itm_var(byte[] v) {this.val_obj_bry = v;} + public byte Itm_tid() {return Php_itm_.Tid_var;} + public byte[] Val_obj_bry() {return val_obj_bry;} private byte[] val_obj_bry; +} diff --git a/400_xowa/src/gplx/php/Php_itm_.java b/400_xowa/src/gplx/php/Php_itm_.java new file mode 100644 index 000000000..c85bda1af --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm_.java @@ -0,0 +1,44 @@ +/* +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.php; import gplx.*; +public class Php_itm_ { + public static final byte Tid_null = 0, Tid_bool_false = 1, Tid_bool_true = 2, Tid_int = 3, Tid_quote = 4, Tid_ary = 5, Tid_kv = 6, Tid_var = 7; + public static int Parse_int_or(Php_itm itm, int or) { + int rv = -1; + switch (itm.Itm_tid()) { + case Php_itm_.Tid_int: + rv = ((Php_itm_int)itm).Val_obj_int(); + return rv; + case Php_itm_.Tid_quote: + byte[] bry = ((Php_itm_quote)itm).Val_obj_bry(); + rv = Bry_.X_to_int_or(bry, -1); + return (rv == -1) ? or : rv; + default: + return or; + } + } + public static byte[] Parse_bry(Php_itm itm) { + switch (itm.Itm_tid()) { + case Php_itm_.Tid_kv: + case Php_itm_.Tid_ary: + throw Err_mgr._.unhandled_(itm.Itm_tid()); + default: + return itm.Val_obj_bry(); + } + } +} diff --git a/400_xowa/src/gplx/php/Php_itm_ary.java b/400_xowa/src/gplx/php/Php_itm_ary.java new file mode 100644 index 000000000..25cda9b15 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm_ary.java @@ -0,0 +1,37 @@ +/* +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.php; import gplx.*; +public class Php_itm_ary implements Php_itm, Php_itm_sub { + public Php_itm_ary() {} + public byte Itm_tid() {return Php_itm_.Tid_ary;} + public byte[] Val_obj_bry() {return null;} + public int Subs_len() {return subs_len;} private int subs_len; + public Php_itm_sub Subs_get(int i) {return ary[i];} + public Php_itm_sub Subs_pop() {return ary[--subs_len];} + public void Subs_add(Php_itm_sub v) { + int new_len = subs_len + 1; + if (new_len > subs_max) { // ary too small >>> expand + subs_max = new_len * 2; + Php_itm_sub[] new_ary = new Php_itm_sub[subs_max]; + Array_.CopyTo(ary, 0, new_ary, 0, subs_len); + ary = new_ary; + } + ary[subs_len] = v; + subs_len = new_len; + } Php_itm_sub[] ary = Php_itm_sub_.Ary_empty; int subs_max; +} diff --git a/400_xowa/src/gplx/php/Php_itm_int.java b/400_xowa/src/gplx/php/Php_itm_int.java new file mode 100644 index 000000000..cdf025830 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm_int.java @@ -0,0 +1,24 @@ +/* +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.php; import gplx.*; +public class Php_itm_int implements Php_itm, Php_itm_sub, Php_key { + public Php_itm_int(int v) {this.val_obj_int = v;} + public byte Itm_tid() {return Php_itm_.Tid_int;} + public byte[] Val_obj_bry() {return Bry_.XbyInt(val_obj_int);} + public int Val_obj_int() {return val_obj_int;} private int val_obj_int; +} diff --git a/400_xowa/src/gplx/php/Php_itm_kv.java b/400_xowa/src/gplx/php/Php_itm_kv.java new file mode 100644 index 000000000..bbff32194 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm_kv.java @@ -0,0 +1,24 @@ +/* +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.php; import gplx.*; +public class Php_itm_kv implements Php_itm, Php_itm_sub { + public byte Itm_tid() {return Php_itm_.Tid_kv;} + public byte[] Val_obj_bry() {return null;} + public Php_key Key() {return key;} public Php_itm_kv Key_(Php_key v) {this.key = v; return this;} Php_key key; + public Php_itm Val() {return val;} public Php_itm_kv Val_(Php_itm v) {this.val = v; return this;} Php_itm val; +} diff --git a/400_xowa/src/gplx/php/Php_itm_quote.java b/400_xowa/src/gplx/php/Php_itm_quote.java new file mode 100644 index 000000000..70e9df602 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm_quote.java @@ -0,0 +1,23 @@ +/* +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.php; import gplx.*; +public class Php_itm_quote implements Php_itm, Php_itm_sub, Php_key { + public Php_itm_quote(byte[] v) {this.val_obj_bry = v;} // NOTE: use Php_text_itm_parser to parse \" and related + public byte Itm_tid() {return Php_itm_.Tid_quote;} + public byte[] Val_obj_bry() {return val_obj_bry;} private byte[] val_obj_bry; +} diff --git a/400_xowa/src/gplx/php/Php_itm_sub.java b/400_xowa/src/gplx/php/Php_itm_sub.java new file mode 100644 index 000000000..3227e9c9b --- /dev/null +++ b/400_xowa/src/gplx/php/Php_itm_sub.java @@ -0,0 +1,23 @@ +/* +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.php; import gplx.*; +public interface Php_itm_sub extends Php_itm { +} +class Php_itm_sub_ { + public static final Php_itm_sub[] Ary_empty = new Php_itm_sub[0]; +} diff --git a/400_xowa/src/gplx/php/Php_key.java b/400_xowa/src/gplx/php/Php_key.java new file mode 100644 index 000000000..7d46af8e2 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_key.java @@ -0,0 +1,23 @@ +/* +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.php; import gplx.*; +public interface Php_key extends Php_itm { +} +class Php_key_ { + public static final Php_key[] Ary_empty = new Php_key[0]; +} diff --git a/400_xowa/src/gplx/php/Php_line.java b/400_xowa/src/gplx/php/Php_line.java new file mode 100644 index 000000000..4a3fa2bff --- /dev/null +++ b/400_xowa/src/gplx/php/Php_line.java @@ -0,0 +1,19 @@ +/* +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.php; import gplx.*; +public interface Php_line {} diff --git a/400_xowa/src/gplx/php/Php_line_assign.java b/400_xowa/src/gplx/php/Php_line_assign.java new file mode 100644 index 000000000..7f926dfa5 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_line_assign.java @@ -0,0 +1,23 @@ +/* +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.php; import gplx.*; +public class Php_line_assign implements Php_line { + public Php_key Key() {return key;} public Php_line_assign Key_(Php_key v) {this.key = v; return this;} Php_key key; + public Php_key[] Key_subs() {return key_subs;} public Php_line_assign Key_subs_(Php_key[] v) {this.key_subs = v; return this;} Php_key[] key_subs = Php_key_.Ary_empty; + public Php_itm Val() {return val;} public Php_line_assign Val_(Php_itm v) {this.val = v; return this;} Php_itm val; +} diff --git a/400_xowa/src/gplx/php/Php_lxr.java b/400_xowa/src/gplx/php/Php_lxr.java new file mode 100644 index 000000000..0569ac843 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_lxr.java @@ -0,0 +1,282 @@ +/* +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.php; import gplx.*; +interface Php_lxr { + byte Lxr_tid(); + void Lxr_ini(ByteTrieMgr_slim trie, Php_parser_interrupt[] parser_interrupts); + void Lxr_bgn(byte[] src, int src_len, Php_tkn_wkr tkn_wkr, Php_tkn_factory tkn_factory); + int Lxr_make(Php_ctx ctx, int bgn, int cur); +} +class Php_lxr_ { + public static final byte Tid_declaration = 1, Tid_ws = 2, Tid_comment = 3, Tid_var = 4, Tid_sym = 5, Tid_keyword = 6, Tid_num = 7, Tid_quote = 8; +} +abstract class Php_lxr_base implements Php_lxr { + protected byte[] src; protected int src_len; protected Php_tkn_wkr tkn_wkr; protected Php_tkn_factory tkn_factory; + public abstract byte Lxr_tid(); + public abstract void Lxr_ini(ByteTrieMgr_slim trie, Php_parser_interrupt[] parser_interrupts); + public void Lxr_bgn(byte[] src, int src_len, Php_tkn_wkr tkn_wkr, Php_tkn_factory tkn_factory) {this.src = src; this.src_len = src_len; this.tkn_wkr = tkn_wkr; this.tkn_factory = tkn_factory;} + public abstract int Lxr_make(Php_ctx ctx, int bgn, int cur); +} +class Php_lxr_declaration extends Php_lxr_base { + @Override public byte Lxr_tid() {return Php_lxr_.Tid_declaration;} + @Override public void Lxr_ini(ByteTrieMgr_slim trie, Php_parser_interrupt[] parser_interrupts) { + trie.Add(Bry_declaration, this); + parser_interrupts[Byte_ascii.Lt] = Php_parser_interrupt.Char; + } + @Override public int Lxr_make(Php_ctx ctx, int bgn, int cur) { + boolean loop = true; + boolean ws_found = false; + while (loop) { + if (cur == src_len) break; + byte b = src[cur]; + switch (b) { + case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + ws_found = true; + ++cur; + break; + default: + if (ws_found) loop = false; + else return Php_parser.NotFound; + break; + } + } + tkn_wkr.Process(tkn_factory.Declaration(bgn, cur)); + return cur; + } + private static final byte[] Bry_declaration = Bry_.new_ascii_(" -1; i--) { // count preceding backslashes + if (src[i] == Byte_ascii.Backslash) + ++backslash_count; + else + break; + } + if (backslash_count % 2 == 1) { // odd backslashes; this means that ' is escaped; EX: \' and \\\'; note that even backslashes means not escaped; EX: \\' + end_quote = false; + cur = end + 1; + } + } + if (end_quote) { + cur = end + quote_bry.length; + break; + } + } + } + tkn_wkr.Process(tkn_factory.Quote(bgn, cur, quote_tid)); + return cur; + } + public static final Gfo_msg_itm Dangling_quote = Gfo_msg_itm_.new_warn_(Php_parser.Log_nde, "dangling_quote", "dangling_quote"); + public static final byte[] Quote_bry_single = Bry_.new_ascii_("'"), Quote_bry_double = Bry_.new_ascii_("\""); +} +class Php_lxr_keyword extends Php_lxr_base { + public Php_lxr_keyword(String hook_str, byte tkn_tid) {this.hook = Bry_.new_ascii_(hook_str); this.tkn_tid = tkn_tid;} private byte[] hook; byte tkn_tid; + @Override public byte Lxr_tid() {return Php_lxr_.Tid_keyword;} + @Override public void Lxr_ini(ByteTrieMgr_slim trie, Php_parser_interrupt[] parser_interrupts) {trie.Add(hook, this);} + @Override public int Lxr_make(Php_ctx ctx, int bgn, int cur) { + if (cur < src_len) { + byte next_byte = src[cur]; + switch (next_byte) { // valid characters for end of word; EX: 'null '; 'null='; etc.. + case Byte_ascii.Space: case Byte_ascii.Tab: case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: + case Byte_ascii.Hash: case Byte_ascii.Slash: + case Byte_ascii.Quote: case Byte_ascii.Apos: + case Byte_ascii.Bang: case Byte_ascii.Dollar: case Byte_ascii.Percent: case Byte_ascii.Amp: + case Byte_ascii.Paren_bgn: case Byte_ascii.Paren_end: case Byte_ascii.Asterisk: case Byte_ascii.Plus: + case Byte_ascii.Comma: case Byte_ascii.Dash: case Byte_ascii.Dot: case Byte_ascii.Semic: + case Byte_ascii.Lt: case Byte_ascii.Eq: case Byte_ascii.Gt: case Byte_ascii.Question: case Byte_ascii.At: + case Byte_ascii.Brack_bgn: case Byte_ascii.Backslash: case Byte_ascii.Brack_end: case Byte_ascii.Pow: case Byte_ascii.Tick: + case Byte_ascii.Curly_bgn: case Byte_ascii.Pipe: case Byte_ascii.Curly_end: case Byte_ascii.Tilde: + break; + default: // num,ltr or extended utf8 character sequence; treat keyword as false match; EX: 'nulla'; 'null0' + return Php_parser.NotFound; + } + } + tkn_wkr.Process(tkn_factory.Generic(bgn, cur, tkn_tid)); + return cur; + } +} +class Php_lxr_num extends Php_lxr_base { + @Override public byte Lxr_tid() {return Php_lxr_.Tid_keyword;} + @Override public void Lxr_ini(ByteTrieMgr_slim trie, Php_parser_interrupt[] parser_interrupts) { + for (int i = 0; i < 10; i++) + trie.Add(new byte[] {(byte)(i + Byte_ascii.Num_0)}, this); + } + @Override public int Lxr_make(Php_ctx ctx, int bgn, int cur) { + boolean loop = true; + while (loop) { + if (cur == src_len) break; + byte b = src[cur]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + ++cur; + break; + default: + loop = false; + break; + } + } + tkn_wkr.Process(tkn_factory.Num(bgn, cur)); + return cur; + } +} \ No newline at end of file diff --git a/400_xowa/src/gplx/php/Php_parser.java b/400_xowa/src/gplx/php/Php_parser.java new file mode 100644 index 000000000..f1b8dcbe8 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_parser.java @@ -0,0 +1,120 @@ +/* +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.php; import gplx.*; +public class Php_parser { + Php_lxr[] lxrs; int lxrs_len; + int txt_bgn; Php_tkn_txt txt_tkn; + private ByteTrieMgr_slim trie = ByteTrieMgr_slim.ci_ascii_(); // NOTE:ci:PHP tkns are ASCII + byte[] src; int src_len; Php_tkn_wkr tkn_wkr; Php_tkn_factory tkn_factory = new Php_tkn_factory(); Php_ctx ctx = new Php_ctx(); + Php_parser_interrupt[] parser_interrupts = new Php_parser_interrupt[256]; + public Php_parser() { + ListAdp list = ListAdp_.new_(); + Init_lxr(list, new Php_lxr_declaration()); + Init_lxr(list, new Php_lxr_ws(Php_tkn_ws.Tid_space)); + Init_lxr(list, new Php_lxr_ws(Php_tkn_ws.Tid_nl)); + Init_lxr(list, new Php_lxr_ws(Php_tkn_ws.Tid_tab)); + Init_lxr(list, new Php_lxr_ws(Php_tkn_ws.Tid_cr)); + Init_lxr(list, new Php_lxr_comment(Php_tkn_comment.Tid_mult)); + Init_lxr(list, new Php_lxr_comment(Php_tkn_comment.Tid_slash)); + Init_lxr(list, new Php_lxr_comment(Php_tkn_comment.Tid_hash)); + Init_lxr(list, new Php_lxr_var()); + Init_lxr(list, new Php_lxr_sym(";", Php_tkn_.Tid_semic)); + Init_lxr(list, new Php_lxr_sym("=", Php_tkn_.Tid_eq)); + Init_lxr(list, new Php_lxr_sym("=>", Php_tkn_.Tid_eq_kv)); + Init_lxr(list, new Php_lxr_sym(",", Php_tkn_.Tid_comma)); + Init_lxr(list, new Php_lxr_sym("(", Php_tkn_.Tid_paren_bgn)); + Init_lxr(list, new Php_lxr_sym(")", Php_tkn_.Tid_paren_end)); + Init_lxr(list, new Php_lxr_sym("[", Php_tkn_.Tid_brack_bgn)); + Init_lxr(list, new Php_lxr_sym("]", Php_tkn_.Tid_brack_end)); + Init_lxr(list, new Php_lxr_keyword("null", Php_tkn_.Tid_null)); + Init_lxr(list, new Php_lxr_keyword("false", Php_tkn_.Tid_false)); + Init_lxr(list, new Php_lxr_keyword("true", Php_tkn_.Tid_true)); + Init_lxr(list, new Php_lxr_keyword("array", Php_tkn_.Tid_ary)); + Init_lxr(list, new Php_lxr_num()); + Init_lxr(list, new Php_lxr_quote(Byte_ascii.Apos)); + Init_lxr(list, new Php_lxr_quote(Byte_ascii.Quote)); + lxrs = (Php_lxr[])list.XtoAry(Php_lxr.class); + lxrs_len = list.Count(); + } + private void Init_lxr(ListAdp list, Php_lxr lxr) { + lxr.Lxr_ini(trie, parser_interrupts); + list.Add(lxr); + } + public void Parse_tkns(String src, Php_tkn_wkr tkn_wkr) {Parse_tkns(Bry_.new_utf8_(src), tkn_wkr);} + public void Parse_tkns(byte[] src, Php_tkn_wkr tkn_wkr) { + this.src = src; this.src_len = src.length; this.tkn_wkr = tkn_wkr; + ctx.Src_(src); + tkn_wkr.Init(ctx); + if (src_len == 0) return; + + for (int i = 0; i < lxrs_len; i++) + lxrs[i].Lxr_bgn(src, src_len, tkn_wkr, tkn_factory); + + int pos = 0; + byte b = src[pos]; + txt_tkn = null; txt_bgn = 0; + boolean loop_raw = true, loop_txt = true; + while (loop_raw) { + Object o = trie.Match(b, src, pos, src_len); + if (o == null) { // char does not hook into a lxr + loop_txt = true; + while (loop_txt) { // keep looping until end of String or parser_interrupt + ++pos; + if (pos == src_len) {loop_raw = false; break;} + b = src[pos]; + if (parser_interrupts[b & 0xFF] == Php_parser_interrupt.Char) { + Make_txt(txt_bgn, pos); + break; + } + } + if (!loop_raw) break; + continue; // continue b/c b is set to interrupt char, and should be matched against trie + } + else { // char hooks into lxr + if (txt_bgn != pos) // txt_bgn is set; make text tkn + Make_txt(txt_bgn, pos); + Php_lxr lxr = (Php_lxr)o; + int match_pos = trie.Match_pos(); + int make_pos = lxr.Lxr_make(ctx, pos, match_pos); + if (make_pos == Php_parser.NotFound) { + Make_txt(txt_bgn, pos); + ++pos; + } + else { + txt_tkn = null; + txt_bgn = pos = make_pos; + } + } + if (pos == src_len) break; + b = src[pos]; + } + if (txt_bgn != pos) + Make_txt(txt_bgn, pos); + } + int Make_txt(int bgn, int end) { + if (txt_tkn == null) { + txt_tkn = tkn_factory.Txt(bgn, end); + tkn_wkr.Process(txt_tkn); + } + else + txt_tkn.Src_end_(end); + return end; + } + public static final int NotFound = -1; + public static final Gfo_msg_grp Log_nde = Gfo_msg_grp_.new_(Gfo_msg_grp_.Root_gplx, "php_parser"); +} diff --git a/400_xowa/src/gplx/php/Php_parser_tst.java b/400_xowa/src/gplx/php/Php_parser_tst.java new file mode 100644 index 000000000..70da36462 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_parser_tst.java @@ -0,0 +1,399 @@ +/* +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.php; import gplx.*; +import org.junit.*; +public class Php_parser_tst { + Php_parser_fxt fxt = new Php_parser_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Text() { + fxt.tst_tkns("text", fxt.tkn_txt(0, 4)); + } + @Test public void Declaration_pass() { + fxt.tst_tkns(",()", fxt.tkn_generic(0, 1, Php_tkn_.Tid_semic), fxt.tkn_generic(1, 2, Php_tkn_.Tid_eq), fxt.tkn_generic(2, 4, Php_tkn_.Tid_eq_kv), fxt.tkn_generic(4, 5, Php_tkn_.Tid_comma), fxt.tkn_generic(5, 6, Php_tkn_.Tid_paren_bgn), fxt.tkn_generic(6, 7, Php_tkn_.Tid_paren_end)); + } + @Test public void Keyword() { + fxt.tst_tkns("null=nulla", fxt.tkn_generic(0, 4, Php_tkn_.Tid_null), fxt.tkn_generic(4, 5, Php_tkn_.Tid_eq), fxt.tkn_txt(5, 10)); + } + @Test public void Num() { + fxt.tst_tkns("0=123", fxt.tkn_num(0, 1, 0), fxt.tkn_generic(1, 2, Php_tkn_.Tid_eq), fxt.tkn_num(2, 5, 123)); + } + @Test public void Quote_apos() { + fxt.tst_tkns("'a\"b'", fxt.tkn_quote_apos(0, 5)); + } + @Test public void Quote_quote() { + fxt.tst_tkns("\"a'b\"", fxt.tkn_quote_quote(0, 5)); + } + @Test public void Quote_escape() { + fxt.tst_tkns("'a\\'b'", fxt.tkn_quote_apos(0, 6)); + } + @Test public void Brack() { + fxt.tst_tkns("['a']", fxt.tkn_generic(0, 1, Php_tkn_.Tid_brack_bgn), fxt.tkn_quote_apos(1, 4), fxt.tkn_generic(4, 5, Php_tkn_.Tid_brack_end)); + } + @Test public void Line_assign_false() { + fxt.tst_lines("$a = false;", fxt.line_assign("a", fxt.itm_bool_false())); + } + @Test public void Line_assign_quote_charcode() { + fxt.tst_lines("$a = 'bc';", fxt.line_assign("a", fxt.itm_quote("bc"))); + } + @Test public void Line_assign_mult() { + fxt.tst_lines("$a = 'b';\n$c='d';", fxt.line_assign("a", fxt.itm_quote("b")), fxt.line_assign("c", fxt.itm_quote("d"))); + } + @Test public void Line_ary_flat() { + fxt.tst_lines("$a = array('b', 'c', 'd');", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_quote("b"), fxt.itm_quote("c"), fxt.itm_quote("d")))); + } + @Test public void Line_ary_flat_escape() { // PURPOSE.fix: \\' was being interpreted incorrectly; \\ should escape \, but somehow \' was being escaped + fxt.tst_lines("$a = array('b\\\\', 'c');", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_quote("b\\\\"), fxt.itm_quote("c")))); + } + @Test public void Line_ary_flat_escape2() { // PURPOSE.fix: \\' was being interpreted incorrectly; \\ should escape \, but somehow \' was being escaped + fxt.tst_lines("$a = array('b\\\\\\'c', 'd');", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_quote("b\\\\\\'c"), fxt.itm_quote("d")))); + } + @Test public void Line_ary_kv() { + fxt.tst_lines("$a = array(k0 => 'v0', k1 => 'v1', k2 => 'v2');", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_kv_quote("k0", "v0"), fxt.itm_kv_quote("k1", "v1"), fxt.itm_kv_quote("k2", "v2")))); + } + @Test public void Line_ary_kv_num() { + fxt.tst_lines("$a = array(k0 => 0, k1 => 1);", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_kv_int("k0", 0), fxt.itm_kv_int("k1", 1)))); + } + @Test public void Line_ary_nest() { + fxt.tst_lines("$a = array('b', array('c', 'd'), 'e');", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_quote("b"), fxt.itm_ary().Subs_(fxt.itm_quote("c"), fxt.itm_quote("d")), fxt.itm_quote("e")))); + } + @Test public void Line_ary_nest_kv() { + fxt.tst_lines("$a = array('i00' => array('01', '02'), 'i10' => array('11', '12'), 'i20' => array('21', '22'));" + , fxt.line_assign + ( "a" + , fxt.itm_ary().Subs_ + ( fxt.itm_kv_itm("i00", fxt.itm_ary().Subs_(fxt.itm_quote("01"), fxt.itm_quote("02"))) + , fxt.itm_kv_itm("i10", fxt.itm_ary().Subs_(fxt.itm_quote("11"), fxt.itm_quote("12"))) + , fxt.itm_kv_itm("i20", fxt.itm_ary().Subs_(fxt.itm_quote("21"), fxt.itm_quote("22"))) + ))); + } + @Test public void Line_ws() { + fxt.tst_lines("\r\n$a = false;", fxt.line_assign("a", fxt.itm_bool_false())); + } + @Test public void Empty_usr_array() { + fxt.tst_lines("$a = array();\n$b = array();" + , fxt.line_assign("a", fxt.itm_ary()) + , fxt.line_assign("b", fxt.itm_ary()) + ); + } + @Test public void Line_ary_kv_txt() { + fxt.tst_lines("$a = array('k0' => a, 'k1' => b);", fxt.line_assign("a", fxt.itm_ary().Subs_(fxt.itm_kv_txt("k0", "a"), fxt.itm_kv_txt("k1", "b")))); + } + @Test public void Line_brack() { + fxt.tst_lines("$a['b'] = 'c';", fxt.line_assign_subs("a", String_.Ary("b"), fxt.itm_quote("c"))); + } +} +class Php_parser_fxt { + Php_tkn_factory tkn_factory = new Php_tkn_factory(); + Php_parser parser = new Php_parser(); + Php_tkn_wkr_tkn tkn_wkr = new Php_tkn_wkr_tkn(); + Php_evaluator line_wkr = new Php_evaluator(new Gfo_msg_log("test")); + Tst_mgr tst_mgr = new Tst_mgr(); + Gfo_msg_log_chkr log_mgr_chkr = new Gfo_msg_log_chkr(); + public void Clear() {log_mgr_chkr.Clear(); tkn_wkr.Clear(); line_wkr.Clear();} + public Php_tkn_chkr_base tkn_declaration() {return Php_tkn_declaration_chkr._;} + public Php_tkn_chkr_base tkn_txt(int bgn, int end) {return new Php_tkn_txt_chkr(bgn, end);} + public Php_tkn_chkr_base tkn_ws(int bgn, int end) {return new Php_tkn_ws_chkr(bgn, end);} + public Php_tkn_chkr_base tkn_generic(int bgn, int end, byte tid) {return new Php_tkn_generic_chkr(bgn, end, tid);} + public Php_tkn_comment_chkr tkn_comment_mult(int bgn, int end) {return new Php_tkn_comment_chkr(bgn, end).Comment_tid_(Php_tkn_comment.Tid_mult);} + public Php_tkn_comment_chkr tkn_comment_slash(int bgn, int end) {return new Php_tkn_comment_chkr(bgn, end).Comment_tid_(Php_tkn_comment.Tid_slash);} + public Php_tkn_comment_chkr tkn_comment_hash(int bgn, int end) {return new Php_tkn_comment_chkr(bgn, end).Comment_tid_(Php_tkn_comment.Tid_hash);} + public Php_tkn_quote_chkr tkn_quote_apos(int bgn, int end) {return new Php_tkn_quote_chkr(bgn, end).Quote_tid_(Byte_ascii.Apos);} + public Php_tkn_quote_chkr tkn_quote_quote(int bgn, int end) {return new Php_tkn_quote_chkr(bgn, end).Quote_tid_(Byte_ascii.Quote);} + public Php_parser_fxt Msg(Gfo_msg_itm itm, int bgn, int end) { + log_mgr_chkr.Add_itm(itm, bgn, end); + return this; + } + public Php_tkn_var_chkr tkn_var(int bgn, int end, String v) {return new Php_tkn_var_chkr(bgn, end).Var_name_(v);} + public Php_tkn_num_chkr tkn_num(int bgn, int end, int v) {return new Php_tkn_num_chkr(bgn, end).Num_val_int_(v);} + public Php_line_assign_chkr line_assign(String key, Php_itm_chkr_base val) {return new Php_line_assign_chkr().Key_(key).Val_(val);} + public Php_line_assign_chkr line_assign_subs(String key, String[] subs, Php_itm_chkr_base val) {return new Php_line_assign_chkr().Key_(key).Subs_(subs).Val_(val);} + public Php_itm_chkr_base itm_bool_true() {return new Php_itm_generic_chkr(Php_itm_.Tid_bool_true);} + public Php_itm_chkr_base itm_bool_false() {return new Php_itm_generic_chkr(Php_itm_.Tid_bool_false);} + public Php_itm_chkr_base itm_null() {return new Php_itm_generic_chkr(Php_itm_.Tid_null);} + public Php_itm_chkr_base itm_quote(String v) {return new Php_itm_quote_chkr().Val_obj_str_(v);} + public Php_itm_chkr_base itm_int(int v) {return new Php_itm_int_chkr().Val_obj_int_(v);} + public Php_itm_chkr_base itm_txt(String v) {return new Php_itm_txt_chkr().Val_obj_str_(v);} + public Php_itm_ary_chkr itm_ary() {return new Php_itm_ary_chkr();} + public Php_itm_kv_chkr itm_kv_quote(String k, String v) {return new Php_itm_kv_chkr().Key_(k).Val_(itm_quote(v));} + public Php_itm_kv_chkr itm_kv_txt(String k, String v) {return new Php_itm_kv_chkr().Key_(k).Val_(itm_txt(v));} + public Php_itm_kv_chkr itm_kv_int(String k, int v) {return new Php_itm_kv_chkr().Key_(k).Val_(itm_int(v));} + public Php_itm_kv_chkr itm_kv_itm(String k, Php_itm_chkr_base v) {return new Php_itm_kv_chkr().Key_(k).Val_(v);} + public void tst_tkns(String raw, Php_tkn_chkr_base... expd) { + byte[] raw_bry = Bry_.new_utf8_(raw); + parser.Parse_tkns(raw_bry, tkn_wkr); + Php_tkn[] actl = (Php_tkn[])tkn_wkr.List().XtoAry(Php_tkn.class); + tst_mgr.Vars().Clear().Add("raw_bry", raw_bry); + tst_mgr.Tst_ary("", expd, actl); + log_mgr_chkr.tst(tst_mgr, tkn_wkr.Msg_log()); + } + public void tst_lines(String raw, Php_line_assign_chkr... expd) { + byte[] raw_bry = Bry_.new_utf8_(raw); + parser.Parse_tkns(raw_bry, line_wkr); + Php_line[] actl = (Php_line[])line_wkr.List().XtoAry(Php_line.class); + tst_mgr.Vars().Clear().Add("raw_bry", raw_bry); + tst_mgr.Tst_ary("", expd, actl); + log_mgr_chkr.tst(tst_mgr, line_wkr.Msg_log()); + } +} +abstract class Php_tkn_chkr_base implements Tst_chkr { + public abstract byte Tkn_tid(); + public abstract Class TypeOf(); + public int Src_bgn() {return src_bgn;} private int src_bgn = -1; + public int Src_end() {return src_end;} private int src_end = -1; + public void Src_rng_(int src_bgn, int src_end) {this.src_bgn = src_bgn; this.src_end = src_end;} + public int Chk(Tst_mgr mgr, String path, Object actl_obj) { + Php_tkn actl = (Php_tkn)actl_obj; + int rv = 0; + rv += mgr.Tst_val(false, path, "tkn_tid", this.Tkn_tid(), actl.Tkn_tid()); + rv += mgr.Tst_val(src_bgn == -1, path, "src_bgn", src_bgn, actl.Src_bgn()); + rv += mgr.Tst_val(src_end == -1, path, "src_end", src_end, actl.Src_end()); + rv += Chk_tkn(mgr, path, actl); + return rv; + } + @gplx.Virtual public int Chk_tkn(Tst_mgr mgr, String path, Php_tkn actl_obj) {return 0;} +} +class Php_tkn_declaration_chkr extends Php_tkn_chkr_base { + @Override public Class TypeOf() {return Php_tkn_declaration.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_declaration;} + public static final Php_tkn_declaration_chkr _ = new Php_tkn_declaration_chkr(); +} +class Php_tkn_txt_chkr extends Php_tkn_chkr_base { + public Php_tkn_txt_chkr(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public Class TypeOf() {return Php_tkn_txt.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_txt;} +} +class Php_tkn_ws_chkr extends Php_tkn_chkr_base { + public Php_tkn_ws_chkr(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public Class TypeOf() {return Php_tkn_ws.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_ws;} +} +class Php_tkn_comment_chkr extends Php_tkn_chkr_base { + public Php_tkn_comment_chkr(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public Class TypeOf() {return Php_tkn_comment.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_comment;} + public Php_tkn_comment_chkr Comment_tid_(byte v) {this.comment_tid = v; return this;} private byte comment_tid = Php_tkn_comment.Tid_null; + @Override public int Chk_tkn(Tst_mgr mgr, String path, Php_tkn actl_obj) { + Php_tkn_comment actl = (Php_tkn_comment)actl_obj; + int rv = 0; + rv += mgr.Tst_val(comment_tid == Php_tkn_comment.Tid_null, path, "comment_tid", comment_tid, actl.Comment_tid()); + return rv; + } +} +class Php_tkn_quote_chkr extends Php_tkn_chkr_base { + public Php_tkn_quote_chkr(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public Class TypeOf() {return Php_tkn_quote.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_quote;} + public Php_tkn_quote_chkr Quote_tid_(byte v) {this.quote_tid = v; return this;} private byte quote_tid = Byte_ascii.Nil; + @Override public int Chk_tkn(Tst_mgr mgr, String path, Php_tkn actl_obj) { + Php_tkn_quote actl = (Php_tkn_quote)actl_obj; + int rv = 0; + rv += mgr.Tst_val(quote_tid == Byte_ascii.Nil, path, "quote_tid", quote_tid, actl.Quote_tid()); + return rv; + } +} +class Php_tkn_var_chkr extends Php_tkn_chkr_base { + public Php_tkn_var_chkr(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public Class TypeOf() {return Php_tkn_var.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_var;} + public Php_tkn_var_chkr Var_name_(String v) {this.var_name = v; return this;} private String var_name; + @Override public int Chk_tkn(Tst_mgr mgr, String path, Php_tkn actl_obj) { + Php_tkn_var actl = (Php_tkn_var)actl_obj; + int rv = 0; + byte[] raw_bry = (byte[])mgr.Vars_get_by_key("raw_bry"); + rv += mgr.Tst_val(var_name == null, path, "var_name", var_name, String_.new_utf8_(actl.Var_name(raw_bry))); + return rv; + } +} +class Php_tkn_num_chkr extends Php_tkn_chkr_base { + public Php_tkn_num_chkr(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public Class TypeOf() {return Php_tkn_num.class;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_num;} + public Php_tkn_num_chkr Num_val_int_(int v) {this.num_val_int = v; return this;} private int num_val_int = Int_.MinValue; + @Override public int Chk_tkn(Tst_mgr mgr, String path, Php_tkn actl_obj) { + Php_tkn_num actl = (Php_tkn_num)actl_obj; + int rv = 0; + byte[] raw_bry = (byte[])mgr.Vars_get_by_key("raw_bry"); + rv += mgr.Tst_val(num_val_int == Int_.MinValue, path, "num_val_int", num_val_int, actl.Num_val_int(raw_bry)); + return rv; + } +} +class Php_tkn_generic_chkr extends Php_tkn_chkr_base { + public Php_tkn_generic_chkr(int src_bgn, int src_end, byte tkn_tid) {this.Src_rng_(src_bgn, src_end); this.tkn_tid = tkn_tid;} + @Override public Class TypeOf() {return Php_tkn.class;} + @Override public byte Tkn_tid() {return tkn_tid;} private byte tkn_tid; +} +class Php_line_assign_chkr implements Tst_chkr { + public Class TypeOf() {return Php_line_assign.class;} + public Php_line_assign_chkr Key_(String v) {key = v; return this;} private String key; + public Php_line_assign_chkr Subs_(String[] v) { + int subs_len = v.length; + subs = new Php_itm_quote_chkr[subs_len]; + for (int i = 0; i < subs_len; i++) + subs[i] = new Php_itm_quote_chkr().Val_obj_str_(v[i]); + return this; + } Php_itm_chkr_base[] subs; + public Php_line_assign_chkr Val_(Php_itm_chkr_base v) {val = v; return this;} Php_itm_chkr_base val; + public int Chk(Tst_mgr mgr, String path, Object actl_obj) { + Php_line_assign actl = (Php_line_assign)actl_obj; + int rv = 0; + rv += mgr.Tst_val(key == null, path, "key", key, String_.new_utf8_(actl.Key().Val_obj_bry())); + if (subs != null) rv += mgr.Tst_sub_ary(subs, actl.Key_subs(), "subs", rv); + rv += mgr.Tst_sub_obj(val, actl.Val(), "val", rv); + return rv; + } +} +abstract class Php_itm_chkr_base implements Tst_chkr { + public abstract byte Itm_tid(); + public abstract Class TypeOf(); + public int Chk(Tst_mgr mgr, String path, Object actl_obj) { + Php_itm actl = (Php_itm)actl_obj; + int rv = 0; + rv += mgr.Tst_val(false, path, "tkn_tid", this.Itm_tid(), actl.Itm_tid()); + rv += Chk_itm(mgr, path, actl); + return rv; + } + @gplx.Virtual public int Chk_itm(Tst_mgr mgr, String path, Php_itm actl_obj) {return 0;} + public static final Php_itm_chkr_base[] Ary_empty = new Php_itm_chkr_base[0]; +} +class Php_itm_generic_chkr extends Php_itm_chkr_base { + public Php_itm_generic_chkr(byte itm_tid) {this.itm_tid = itm_tid;} private byte itm_tid; + @Override public byte Itm_tid() {return itm_tid;} + @Override public Class TypeOf() {return Php_itm.class;} +} +class Php_itm_int_chkr extends Php_itm_chkr_base { + @Override public byte Itm_tid() {return Php_itm_.Tid_int;} + @Override public Class TypeOf() {return Php_itm.class;} + public Php_itm_int_chkr Val_obj_int_(int v) {this.val_obj_int = v; return this;} private int val_obj_int; + @Override public int Chk_itm(Tst_mgr mgr, String path, Php_itm actl_obj) { + Php_itm_int actl = (Php_itm_int)actl_obj; + int rv = 0; + rv += mgr.Tst_val(false, path, "val_obj_str", val_obj_int, actl.Val_obj_int()); + return rv; + } +} +class Php_itm_txt_chkr extends Php_itm_chkr_base { + @Override public byte Itm_tid() {return Php_itm_.Tid_var;} + @Override public Class TypeOf() {return Php_itm.class;} + public Php_itm_txt_chkr Val_obj_str_(String v) {this.val_obj_str = v; return this;} private String val_obj_str; + @Override public int Chk_itm(Tst_mgr mgr, String path, Php_itm actl_obj) { + Php_itm_var actl = (Php_itm_var)actl_obj; + int rv = 0; + rv += mgr.Tst_val(false, path, "val_obj_str", val_obj_str, String_.new_utf8_(actl.Val_obj_bry())); + return rv; + } +} +class Php_itm_quote_chkr extends Php_itm_chkr_base { + @Override public byte Itm_tid() {return Php_itm_.Tid_quote;} + @Override public Class TypeOf() {return Php_itm.class;} + public Php_itm_quote_chkr Val_obj_str_(String v) {this.val_obj_str = v; return this;} private String val_obj_str; + @Override public int Chk_itm(Tst_mgr mgr, String path, Php_itm actl_obj) { + Php_itm_quote actl = (Php_itm_quote)actl_obj; + int rv = 0; + rv += mgr.Tst_val(false, path, "val_obj_str", val_obj_str, String_.new_utf8_(actl.Val_obj_bry())); + return rv; + } +} +class Php_itm_ary_chkr extends Php_itm_chkr_base { + @Override public byte Itm_tid() {return Php_itm_.Tid_ary;} + @Override public Class TypeOf() {return Php_itm.class;} + public Php_itm_ary_chkr Subs_(Php_itm_chkr_base... v) {this.itms = v; return this;} Php_itm_chkr_base[] itms = Php_itm_chkr_base.Ary_empty; + @Override public int Chk_itm(Tst_mgr mgr, String path, Php_itm actl_obj) { + Php_itm_ary actl = (Php_itm_ary)actl_obj; + int rv = 0; + int actl_subs_len = actl.Subs_len(); + Php_itm[] actl_ary = new Php_itm[actl_subs_len]; + for (int i = 0; i < actl_subs_len; i++) { + actl_ary[i] = (Php_itm)actl.Subs_get(i); + } + rv += mgr.Tst_sub_ary(itms, actl_ary, "subs", rv); + return rv; + } +} +class Php_itm_kv_chkr extends Php_itm_chkr_base { + @Override public byte Itm_tid() {return Php_itm_.Tid_kv;} + @Override public Class TypeOf() {return Php_itm.class;} + public Php_itm_kv_chkr Key_(String v) {key = v; return this;} private String key; + public Php_itm_kv_chkr Val_(Php_itm_chkr_base v) {val = v; return this;} Php_itm_chkr_base val; + @Override public int Chk_itm(Tst_mgr mgr, String path, Php_itm actl_obj) { + Php_itm_kv actl = (Php_itm_kv)actl_obj; + int rv = 0; + rv += mgr.Tst_val(false, path, "key", key, String_.new_utf8_(actl.Key().Val_obj_bry())); + rv += mgr.Tst_sub_obj(val, actl.Val(), path, rv); + return rv; + } +} +class Gfo_msg_log_chkr implements Tst_chkr { + ListAdp itms = ListAdp_.new_(); + public Class TypeOf() {return Gfo_msg_log.class;} + public void Clear() {itms.Clear();} + public void Add_itm(Gfo_msg_itm itm, int bgn, int end) { + Gfo_msg_data_chkr chkr = new Gfo_msg_data_chkr(); + chkr.Itm_(itm).Excerpt_bgn_(bgn).Excerpt_end_(end); + itms.Add(chkr); + } + public int Chk(Tst_mgr mgr, String path, Object actl_obj) {return 0;} + public void tst(Tst_mgr mgr, Object actl_obj) { + Gfo_msg_log actl = (Gfo_msg_log)actl_obj; + int actl_itms_len = actl.Ary_len(); + Gfo_msg_data[] actl_itms = new Gfo_msg_data[actl_itms_len]; + for (int i = 0; i < actl_itms_len; i++) + actl_itms[i] = actl.Ary_get(i); + mgr.Tst_ary("itms", (Gfo_msg_data_chkr[])itms.XtoAry(Gfo_msg_data_chkr.class), actl_itms); + } +} +class Gfo_msg_data_chkr implements Tst_chkr { + public Class TypeOf() {return Gfo_msg_data.class;} + public Gfo_msg_data_chkr Itm_(Gfo_msg_itm v) {itm = v; return this;} Gfo_msg_itm itm; + public Gfo_msg_data_chkr Excerpt_bgn_(int v) {excerpt_bgn = v; return this;} private int excerpt_bgn = -1; + public Gfo_msg_data_chkr Excerpt_end_(int v) {excerpt_end = v; return this;} private int excerpt_end = -1; + public int Chk(Tst_mgr mgr, String path, Object actl_obj) { + Gfo_msg_data actl = (Gfo_msg_data)actl_obj; + int rv = 0; + rv += mgr.Tst_val(itm == null, path, "itm", itm.Path_str(), actl.Item().Path_str()); + rv += mgr.Tst_val(excerpt_bgn == -1, path, "excerpt_bgn", excerpt_bgn, actl.Src_bgn()); + rv += mgr.Tst_val(excerpt_end == -1, path, "excerpt_end", excerpt_end, actl.Src_end()); + return rv; + } +} diff --git a/400_xowa/src/gplx/php/Php_srl_itm.java b/400_xowa/src/gplx/php/Php_srl_itm.java new file mode 100644 index 000000000..e96e25b53 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_srl_itm.java @@ -0,0 +1,138 @@ +/* +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.php; import gplx.*; +interface Php_srl_itm { + byte Tid(); + int Src_bgn(); + int Src_end(); + Object Val(); + void Xto_bfr(Bry_bfr bfr, int depth); + void Clear(); +} +class Php_srl_itm_ { + public static final Php_srl_itm[] Ary_empty = new Php_srl_itm[0]; + public static final byte Tid_unknown = 0, Tid_nil = 1, Tid_bool = 2, Tid_int = 3, Tid_double = 4, Tid_string = 5, Tid_array = 6, Tid_function = 7; + public static final byte[][] Names = Bry_.Ary("unknown", "nil", "boolean", "int", "double", "string", "array", "function"); + public static final Object Val_nil = null, Val_table = null; +} +abstract class Php_srl_itm_base implements Php_srl_itm { + public abstract byte Tid(); + public void Ctor(int src_bgn, int src_end, Object val) {this.src_bgn = src_bgn; this.src_end = src_end; this.val = val;} + public int Src_bgn() {return src_bgn;} private int src_bgn; + public int Src_end() {return src_end;} private int src_end; + public Object Val() {return val;} Object val; + @gplx.Virtual public void Xto_bfr(Bry_bfr bfr, int depth) { + Php_srl_wtr.Indent(bfr, depth); + bfr.Add(Php_srl_itm_.Names[this.Tid()]).Add_byte(Byte_ascii.Colon); + bfr.Add_str(Object_.XtoStr_OrNullStr(this.Val())).Add_byte(Byte_ascii.Semic).Add_byte_nl(); + } + public void Clear() {} +} +class Php_srl_itm_nil extends Php_srl_itm_base { + public Php_srl_itm_nil() {this.Ctor(-1, -1, null);} + @Override public byte Tid() {return Php_srl_itm_.Tid_nil;} + public byte[] Bry_extract(byte[] raw) {return null;} + public static Php_srl_itm_nil Nil = new Php_srl_itm_nil(); +} +class Php_srl_itm_bool extends Php_srl_itm_base { + public Php_srl_itm_bool(boolean val, byte[] bry) {this.val = val; this.bry = bry; this.Ctor(-1, -1, val);} + @Override public byte Tid() {return Php_srl_itm_.Tid_bool;} + public byte[] Bry_extract(byte[] raw) {return bry;} private byte[] bry; + public boolean Val_as_bool() {return val;} private boolean val; + public static Php_srl_itm_bool Bool_n = new Php_srl_itm_bool(false, new byte[] {Byte_ascii.Num_0}), Bool_y = new Php_srl_itm_bool(true, new byte[] {Byte_ascii.Num_1}); +} +class Php_srl_itm_int extends Php_srl_itm_base { + public Php_srl_itm_int(int src_bgn, int src_end, int val) {this.val = val; this.Ctor(src_bgn, src_end, val);} + @Override public byte Tid() {return Php_srl_itm_.Tid_int;} + public int Val_as_int() {return val;} private int val; +} +class Php_srl_itm_double extends Php_srl_itm_base { + public Php_srl_itm_double(int src_bgn, int src_end, double val) {this.val = val; this.Ctor(src_bgn, src_end, val);} + @Override public byte Tid() {return Php_srl_itm_.Tid_double;} + public double Val_as_double() {return val;} double val; +} +class Php_srl_itm_str extends Php_srl_itm_base { + public Php_srl_itm_str(int src_bgn, int src_end, String val) {this.val = val; this.Ctor(src_bgn, src_end, val);} + @Override public byte Tid() {return Php_srl_itm_.Tid_string;} + public String Val_as_str() {return val;} private String val; +} +class Php_srl_itm_func extends Php_srl_itm_base { + public Php_srl_itm_func(int src_bgn, int src_end, int val) {this.val = val; this.Ctor(src_bgn, src_end, val);} + @Override public byte Tid() {return Php_srl_itm_.Tid_function;} + public int Val_as_int() {return val;} private int val; +} +class Php_srl_itm_ary extends Php_srl_itm_base { + public Php_srl_itm_ary(int src_bgn, int src_end) {this.Ctor(src_bgn, src_end, null);} + @Override public byte Tid() {return Php_srl_itm_.Tid_array;} + public Php_srl_itm_kv[] Subs_ary() {return subs;} + public int Subs_len() {return subs_len;} private int subs_len = 0, subs_max = 0; + public Php_srl_itm_kv Subs_get_at(int i) {return subs[i];} + public void Subs_clear() { + for (int i = 0; i < subs_len; i++) { + subs[i].Clear(); + } + subs = Php_srl_itm_kv.Ary_empty; + subs_len = subs_max = 0; + } + public Php_srl_itm_ary Subs_add_many(Php_srl_itm_kv... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) + Subs_add(ary[i]); + return this; + } + public Php_srl_itm_ary Subs_add(Php_srl_itm_kv itm) { + int new_len = subs_len + 1; + if (new_len > subs_max) { // ary too small >>> expand + subs_max = new_len * 2; + Php_srl_itm_kv[] new_subs = new Php_srl_itm_kv[subs_max]; + Array_.CopyTo(subs, 0, new_subs, 0, subs_len); + subs = new_subs; + } + subs[subs_len] = itm; + subs_len = new_len; + return this; + } + @Override public void Xto_bfr(Bry_bfr bfr, int depth) { + Php_srl_wtr.Indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Ltr_a).Add_byte(Byte_ascii.Brack_bgn).Add_int_variable(subs_len).Add(CONST_ary_bgn); + for (int i = 0; i < subs_len; i++) + subs[i].Xto_bfr(bfr, depth + 1); + Php_srl_wtr.Indent(bfr, depth); + bfr.Add_byte(Byte_ascii.Curly_end).Add_byte_nl(); + } static final byte[] CONST_ary_bgn = Bry_.new_ascii_("]{\n"); + Php_srl_itm_kv[] subs = Php_srl_itm_kv.Ary_empty; +} +class Php_srl_itm_kv { + public int Idx_int() {return idx_int;} public Php_srl_itm_kv Idx_int_(int v) {idx_int = v; return this;} private int idx_int = -1; + public Php_srl_itm Key() {return key;} public Php_srl_itm_kv Key_(Php_srl_itm v) {key = v; return this;} Php_srl_itm key; + public Php_srl_itm Val() {return val;} public Php_srl_itm_kv Val_(Php_srl_itm v) {val = v; return this;} Php_srl_itm val; + public void Clear() { + key.Clear(); + val.Clear(); + } + public void Xto_bfr(Bry_bfr bfr, int depth) { + key.Xto_bfr(bfr, depth); + val.Xto_bfr(bfr, depth); + } + public static final Php_srl_itm_kv[] Ary_empty = new Php_srl_itm_kv[0]; +} +class Php_srl_wtr { + public static void Indent(Bry_bfr bfr, int depth) { + if (depth > 0) bfr.Add_byte_repeat(Byte_ascii.Space, depth * 2); // indent + } +} \ No newline at end of file diff --git a/400_xowa/src/gplx/php/Php_srl_parser.java b/400_xowa/src/gplx/php/Php_srl_parser.java new file mode 100644 index 000000000..d853ec1bd --- /dev/null +++ b/400_xowa/src/gplx/php/Php_srl_parser.java @@ -0,0 +1,208 @@ +/* +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.php; import gplx.*; +import gplx.texts.*; +public class Php_srl_parser { + @gplx.Internal protected Php_srl_factory Factory() {return factory;} Php_srl_factory factory = new Php_srl_factory(); + byte[] raw; int raw_len, pos; + public KeyVal[] Parse_as_kvs(byte[] raw) { + Php_srl_itm_ary root = Parse(raw); + return Xto_kv_ary(root); + } + KeyVal[] Xto_kv_ary(Php_srl_itm_ary ary) { + int len = ary.Subs_len(); + KeyVal[] rv = new KeyVal[len]; + for (int i = 0; i < len; i++) + rv[i] = Xto_kv(ary.Subs_get_at(i)); + return rv; + } + KeyVal Xto_kv(Php_srl_itm_kv itm) { + Php_srl_itm itm_key = itm.Key(); + Object key = itm_key == null ? null : itm_key.Val(); + Php_srl_itm itm_val = itm.Val(); + Object val = null; + switch (itm_val.Tid()) { + case Php_srl_itm_.Tid_array: + Php_srl_itm_ary ary = (Php_srl_itm_ary)itm_val; + val = Xto_kv_ary(ary); + break; + case Php_srl_itm_.Tid_function: + val = new gplx.xowa.xtns.scribunto.Scrib_lua_proc(Object_.XtoStr_OrNullStr(key), Int_.cast_(itm_val.Val())); // NOTE: in most cases, key is a STRING (name of ScribFunction); however, for gsub it is an INT (arg_idx) b/c it is passed as a parameter + break; + default: + val = itm_val.Val(); + break; + } + return KeyVal_.obj_(key, val); + } + @gplx.Internal protected Php_srl_itm_ary Parse(byte[] raw) { + this.raw = raw; this.raw_len = raw.length; pos = 0; + Php_srl_itm_ary rv = new Php_srl_itm_ary(0, raw_len); + Php_srl_itm_kv cur_kv = factory.Kv(); + rv.Subs_add(cur_kv); + boolean mode_is_key = false; + while (true) { + if (pos >= raw_len) break; + if (mode_is_key) { + cur_kv.Key_(Parse_itm(pos)); + mode_is_key = false; + } + else { + cur_kv.Val_(Parse_itm(pos)); + mode_is_key = true; + } + } + return rv; + } + Php_srl_itm_ary Parse_array(int bgn, int subs_len) { // enters after '{'; EX: 'a:1{' -> Parse_array + Php_srl_itm_ary rv = factory.Ary(bgn, bgn); + for (int i = 0; i < subs_len; i++) { + Php_srl_itm_kv kv = factory.Kv(); + Php_srl_itm key_itm = Parse_itm(pos); + kv.Key_(key_itm); + Php_srl_itm val_itm = Parse_itm(pos); + kv.Val_(val_itm); + rv.Subs_add(kv); + } + return rv; + } + Php_srl_itm Parse_itm(int bgn) { + pos = bgn; + Php_srl_itm rv = null; + byte b = raw[pos]; + switch (b) { + case Byte_ascii.Ltr_N: // EX: 'N;' + rv = factory.Nil(); + pos = Chk(raw, pos + 1, Byte_ascii.Semic); + break; + case Byte_ascii.Ltr_b: // EX: 'b:0;' or 'b:1;' + pos = Chk(raw, pos + 1, Byte_ascii.Colon); + b = raw[pos]; + switch (b) { + case Byte_ascii.Num_1: rv = factory.Bool_y(); break; + case Byte_ascii.Num_0: rv = factory.Bool_n(); break; + default: throw err_(raw, pos, raw_len, "unknown boolean type {0}", Char_.XtoStr(b)); + } + pos = Chk(raw, pos + 1, Byte_ascii.Semic); + break; + case Byte_ascii.Ltr_i: // EX: 'i:123;' + rv = Parse_int(pos); + pos = Chk(raw, pos, Byte_ascii.Semic); + break; + case Byte_ascii.Ltr_d: // EX: 'd:1.23;' + pos = Chk(raw, pos + 1, Byte_ascii.Colon); + int double_end = Bry_finder.Find_fwd(raw, Byte_ascii.Semic, pos, raw_len); + String double_str = String_.new_ascii_(raw, pos, double_end); + double double_val = 0; + if (String_.Eq(double_str, "INF")) double_val = Double_.Inf_pos; + else if (String_.Eq(double_str, "NAN")) double_val = Double_.NaN; + else double_val = Double_.parse_(double_str); + rv = factory.Double(pos, double_end, double_val); + pos = Chk(raw, double_end, Byte_ascii.Semic); + break; + case Byte_ascii.Ltr_s: // EX: 's:3:"abc";' + int len_val = Parse_int(pos).Val_as_int(); + pos = Chk(raw, pos, Byte_ascii.Colon); + pos = Chk(raw, pos, Byte_ascii.Quote); + int str_end = pos + len_val; + String str_val = String_.new_utf8_(raw, pos, str_end); + rv = factory.Str(pos, str_end, str_val); + pos = Chk(raw, str_end, Byte_ascii.Quote); + pos = Chk(raw, pos, Byte_ascii.Semic); + break; + case Byte_ascii.Ltr_a: // EX: 'a:0:{}' + int subs_len = Parse_int(pos).Val_as_int(); + pos = Chk(raw, pos, Byte_ascii.Colon); + pos = Chk(raw, pos, Byte_ascii.Curly_bgn); + rv = Parse_array(pos, subs_len); + pos = Chk(raw, pos, Byte_ascii.Curly_end); + break; + case Byte_ascii.Ltr_O: // EX: 'O:42:"Scribunto_LuaStandaloneInterpreterFunction":1:{s:2:"id";i:123;}' + int func_bgn = pos; + pos += 62; // 64= len of constant String after ":42:"Scribunto...." + int func_id = Parse_int_val(pos); + rv = factory.Func(func_bgn, pos, func_id); + pos += 2; + break; + default: throw err_(raw, pos, "unexpected type: {0}", Char_.XtoStr(b)); + } + return rv; + } static final byte[] CONST_funct_bgn = Bry_.new_ascii_("O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:"), CONST_funct_end = Bry_.new_ascii_(";}"); + int Parse_int_val(int bgn) { + pos = bgn; + pos = Chk(raw, pos + 1, Byte_ascii.Colon); + int int_end = Skip_while_num(raw, raw_len, pos, true); + int int_val = Bry_.X_to_int_or(raw, pos, int_end, Int_.MinValue); + pos = int_end; + return int_val; + } + Php_srl_itm_int Parse_int(int bgn) { + pos = bgn; + pos = Chk(raw, pos + 1, Byte_ascii.Colon); + int int_end = Skip_while_num(raw, raw_len, pos, true); + int int_val = Bry_.X_to_int_or(raw, pos, int_end, Int_.MinValue); + Php_srl_itm_int rv = factory.Int(pos, int_end, int_val); + pos = int_end; + return rv; + } + int Chk(byte[] raw, int i, byte expd) { + byte actl = raw[i]; + if (actl == expd) + return i + 1; + else + throw err_(raw, i, "expected '{0}' but got '{1}'", Char_.XtoStr(expd), Char_.XtoStr(actl)); + } + int Skip_while_num(byte[] raw, int raw_len, int bgn, boolean num_is_int) { + int num_len = 1; + for (int i = bgn; i < raw_len; i++) { + byte b = raw[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + case Byte_ascii.Dot: + case Byte_ascii.Dash: + break; + default: + if (num_is_int && num_len < 11) { + return i; + } + else + return i; + } + } + throw err_(raw, raw_len, raw_len, "skip_ws found eos"); + } + Err err_(byte[] raw, int bgn, String fmt, Object... args) {return err_(raw, bgn, raw.length, fmt, args);} + Err err_(byte[] raw, int bgn, int raw_len, String fmt, Object... args) { + String msg = String_.Format(fmt, args) + " " + Int_.XtoStr(bgn) + " " + String_.new_utf8_len_safe_(raw, bgn, 20); + return Err_.new_(msg); + } +} +class Php_srl_factory { + public Php_srl_itm Nil() {return Php_srl_itm_nil.Nil;} + public Php_srl_itm Bool_n() {return Php_srl_itm_bool.Bool_n;} + public Php_srl_itm Bool_y() {return Php_srl_itm_bool.Bool_y;} + public Php_srl_itm_int Int(int bgn, int end, int v) {return new Php_srl_itm_int(bgn, end, v);} + public Php_srl_itm Double(int bgn, int end, double v) {return new Php_srl_itm_double(bgn, end, v);} + public Php_srl_itm Str(int bgn, int end) {return new Php_srl_itm_str(bgn, end, null);} + public Php_srl_itm Str(int bgn, int end, String v) {return new Php_srl_itm_str(bgn, end, v);} + public Php_srl_itm_func Func(int bgn, int end, int v) {return new Php_srl_itm_func(bgn, end, v);} + public Php_srl_itm_ary Ary(int bgn, int end) {return new Php_srl_itm_ary(bgn, end);} + public Php_srl_itm_kv Kv() {return new Php_srl_itm_kv();} +} diff --git a/400_xowa/src/gplx/php/Php_srl_parser_tst.java b/400_xowa/src/gplx/php/Php_srl_parser_tst.java new file mode 100644 index 000000000..e5780cefa --- /dev/null +++ b/400_xowa/src/gplx/php/Php_srl_parser_tst.java @@ -0,0 +1,112 @@ +/* +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.php; import gplx.*; +import org.junit.*; +public class Php_srl_parser_tst { + Php_srl_parser_fxt fxt = new Php_srl_parser_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Nil() {fxt.Test_parse("N;", fxt.itm_nil_());} + @Test public void Bool_y() {fxt.Test_parse("b:1;", fxt.itm_bool_y_());} + @Test public void Bool_n() {fxt.Test_parse("b:0;", fxt.itm_bool_n_());} + @Test public void Num_int() {fxt.Test_parse("i:123;", fxt.itm_int_(123));} + @Test public void Num_int_neg() {fxt.Test_parse("i:-123;", fxt.itm_int_(-123));} + @Test public void Num_double() {fxt.Test_parse("d:1.23;", fxt.itm_double_(1.23d));} + @Test public void Num_double_inf_pos(){fxt.Test_parse("d:INF;", fxt.itm_double_(Double_.Inf_pos));} + @Test public void Num_double_exp() {fxt.Test_parse("d:1.2e+2;", fxt.itm_double_(120));} + @Test public void Num_double_nan() {fxt.Test_parse("d:NAN;", fxt.itm_double_(Double_.NaN));} + @Test public void Str_len_3() {fxt.Test_parse("s:3:\"abc\";", fxt.itm_str_("abc"));} + @Test public void Str_len_4() {fxt.Test_parse("s:4:\"abcd\";", fxt.itm_str_("abcd"));} + @Test public void Str_len_0() {fxt.Test_parse("s:0:\"\";", fxt.itm_str_(""));} + @Test public void Ary_empty() {fxt.Test_parse("a:0:{}", fxt.itm_ary_());} + @Test public void Ary_flat_one() {fxt.Test_parse("a:1:{i:1;i:9;}", fxt.itm_ary_().Subs_add(fxt.itm_kvi_(1, fxt.itm_int_(9))));} + @Test public void Ary_flat_many() { + fxt.Test_parse(String_.Concat + ( "a:3:{" + , "i:1;i:9;" + , "i:2;i:8;" + , "i:3;i:7;" + , "}"), fxt.itm_ary_().Subs_add_many + ( fxt.itm_kvi_(1, fxt.itm_int_(9)) + , fxt.itm_kvi_(2, fxt.itm_int_(8)) + , fxt.itm_kvi_(3, fxt.itm_int_(7)) + )); + } + @Test public void Ary_nest_one() { + fxt.Test_parse(String_.Concat + ( "a:1:{" + , "i:1;" + , "a:2:{" + , "i:1;i:9;" + , "i:2;i:8;" + , "}" + , "}" + ) + , fxt.itm_ary_().Subs_add_many + ( fxt.itm_kvi_(1, fxt.itm_ary_().Subs_add_many + ( fxt.itm_kvi_(1, fxt.itm_int_(9)) + , fxt.itm_kvi_(2, fxt.itm_int_(8)) + )))); + } + @Test public void Ary_key_str() { + fxt.Test_parse(String_.Concat + ( "a:1:{" + , "s:3:\"abc\";" + , "i:987;" + , "}"), fxt.itm_ary_().Subs_add_many + ( fxt.itm_kvs_("abc", fxt.itm_int_(987)) + )); + } + @Test public void Func() { + fxt.Test_parse("O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:123;}", fxt.itm_func_(123)); + } + @Test public void Smoke() { +// fxt.Test_parse("a:2:{s:6:\"values\";a:1:{i:1;a:9:{s:21:\"makeProt"+"ectedEnvFuncs\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:2;}s:3:\"log\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:3;}s:14:\"clearLogBuffer\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:4;}s:5:\"setup\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:5;}s:5:\"clone\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:6;}s:15:\"getCurrentFrame\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:7;}s:13:\"executeModule\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:8;}s:15:\"executeFunction\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:9;}s:12:\"getLogBuffer\";O:42:\"Scribunto_LuaStandaloneInterpreterFunction\":1:{s:2:\"id\";i:10;}}}s:2:\"op\";s:6:\"return\";}"); + } +} +class Php_srl_parser_fxt { + public void Clear() { + parser = new Php_srl_parser(); + factory = parser.Factory(); + } Php_srl_parser parser; Php_srl_factory factory; Bry_bfr tmp_bfr = Bry_bfr.reset_(255); + public Php_srl_itm itm_nil_() {return factory.Nil();} + public Php_srl_itm itm_bool_n_() {return factory.Bool_n();} + public Php_srl_itm itm_bool_y_() {return factory.Bool_y();} + public Php_srl_itm itm_int_(int v) {return factory.Int(-1, -1, v);} + public Php_srl_itm itm_double_(double v) {return factory.Double(-1, -1, v);} + public Php_srl_itm itm_str_(String v) {return factory.Str(-1, -1, v);} + public Php_srl_itm itm_func_(int v) {return factory.Func(-1, -1, v);} + public Php_srl_itm_ary itm_ary_() {return factory.Ary(-1, -1);} + public Php_srl_itm_kv itm_kvi_(int k, Php_srl_itm v){return factory.Kv().Key_(itm_int_(k)).Val_(v);} + public Php_srl_itm_kv itm_kvs_(String k, Php_srl_itm v){return factory.Kv().Key_(itm_str_(k)).Val_(v);} + public void Test_parse(String raw_str, Php_srl_itm... expd_ary) { + byte[] raw = Bry_.new_utf8_(raw_str); + Php_srl_itm_ary root = parser.Parse(raw); + Php_srl_itm root_sub = root.Subs_get_at(0).Val(); + root_sub.Xto_bfr(tmp_bfr, 0); + String actl = tmp_bfr.XtoStrAndClear(); + String expd = Xto_str(expd_ary, 0, expd_ary.length); + Tfds.Eq_str_lines(expd, actl, actl); + } + String Xto_str(Php_srl_itm[] ary, int bgn, int end) { + for (int i = bgn; i < end; i++) { + Php_srl_itm itm = ary[i]; + itm.Xto_bfr(tmp_bfr, 0); + } + return tmp_bfr.XtoStrAndClear(); + } +} diff --git a/400_xowa/src/gplx/php/Php_text_itm.java b/400_xowa/src/gplx/php/Php_text_itm.java new file mode 100644 index 000000000..631364148 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_text_itm.java @@ -0,0 +1,62 @@ +/* +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.php; import gplx.*; +interface Php_text_itm { + byte Tid(); + int Src_bgn(); + int Src_end(); + void Bld(Bry_bfr bfr, byte[] src); +} +class Php_text_itm_ { + public static final byte Tid_text = 0, Tid_escaped = 1, Tid_arg = 2, Tid_utf16 = 3; +} +class Php_text_itm_text implements Php_text_itm { + public Php_text_itm_text(int src_bgn, int src_end) {this.src_bgn = src_bgn; this.src_end = src_end;} + public byte Tid() {return Php_text_itm_.Tid_text;} + public int Src_bgn() {return src_bgn;} private int src_bgn; + public int Src_end() {return src_end;} private int src_end; + public void Bld(Bry_bfr bfr, byte[] src) {bfr.Add_mid(src, src_bgn, src_end);} +} +class Php_text_itm_escaped implements Php_text_itm { + public Php_text_itm_escaped(int src_bgn, int src_end, byte literal) {this.src_bgn = src_bgn; this.src_end = src_end; this.literal = literal;} + public byte Tid() {return Php_text_itm_.Tid_escaped;} + public int Src_bgn() {return src_bgn;} private int src_bgn; + public int Src_end() {return src_end;} private int src_end; + public byte Literal() {return literal;} private byte literal; + public void Bld(Bry_bfr bfr, byte[] src) {bfr.Add_byte(literal);} +} +class Php_text_itm_utf16 implements Php_text_itm { + public Php_text_itm_utf16(int src_bgn, int src_end, byte[] literal) {this.src_bgn = src_bgn; this.src_end = src_end; this.literal = literal;} + public byte Tid() {return Php_text_itm_.Tid_utf16;} + public int Src_bgn() {return src_bgn;} private int src_bgn; + public int Src_end() {return src_end;} private int src_end; + public byte[] Literal() {return literal;} private byte[] literal; + public void Bld(Bry_bfr bfr, byte[] src) {bfr.Add(literal);} +} +class Php_text_itm_arg implements Php_text_itm { + public Php_text_itm_arg(int src_bgn, int src_end, int idx) {this.src_bgn = src_bgn; this.src_end = src_end; this.idx = idx;} + public byte Tid() {return Php_text_itm_.Tid_escaped;} + public int Src_bgn() {return src_bgn;} private int src_bgn; + public int Src_end() {return src_end;} private int src_end; + public int Idx() {return idx;} private int idx; + public void Bld(Bry_bfr bfr, byte[] src) { + bfr.Add_byte(Byte_ascii.Tilde).Add_byte(Byte_ascii.Curly_bgn) + .Add_int_variable(idx - ListAdp_.Base1) // php is super 1 + .Add_byte(Byte_ascii.Curly_end); + } +} diff --git a/400_xowa/src/gplx/php/Php_text_itm_parser.java b/400_xowa/src/gplx/php/Php_text_itm_parser.java new file mode 100644 index 000000000..6fd7c0165 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_text_itm_parser.java @@ -0,0 +1,126 @@ +/* +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.php; import gplx.*; +public class Php_text_itm_parser { + public static final byte Rslt_orig = 0, Rslt_dirty = 1, Rslt_fmt = 2; + public byte[] Parse_as_bry(ListAdp tmp_list, byte[] raw, Byte_obj_ref rslt_ref, Bry_bfr tmp_bfr) { + Parse(tmp_list, raw, rslt_ref); + byte[] rv = raw; + switch (rslt_ref.Val()) { + case Rslt_orig: break; + case Rslt_dirty: + case Rslt_fmt: + tmp_bfr.Clear(); + int tmp_list_len = tmp_list.Count(); + for (int i = 0; i < tmp_list_len; i++) { + Php_text_itm itm = (Php_text_itm)tmp_list.FetchAt(i); + itm.Bld(tmp_bfr, raw); + } + rv = tmp_bfr.XtoAryAndClear(); + break; + } + return rv; + } + public void Parse(ListAdp tmp_list, byte[] raw) { + Parse(tmp_list, raw, Byte_obj_ref.zero_()); + } + public void Parse(ListAdp tmp_list, byte[] raw, Byte_obj_ref rslt) { + tmp_list.Clear(); + int raw_len = raw.length; int raw_last = raw_len - 1; + int txt_bgn = -1; + byte rslt_val = Rslt_orig; + for (int i = 0; i < raw_len; i++) { + byte b = raw[i]; + switch (b) { + case Byte_ascii.Backslash: + if (txt_bgn != -1) {tmp_list.Add(new Php_text_itm_text(txt_bgn, i)); txt_bgn = -1; rslt_val = Rslt_dirty;} + if (i == raw_last) throw Err_mgr._.fmt_auto_(GRP_KEY, "backslash_is_last_char", String_.new_utf8_(raw)); + int next_pos = i + 1; + byte next_char = raw[next_pos]; + switch (next_char) { + case Byte_ascii.Ltr_N: + case Byte_ascii.Ltr_n: next_char = Byte_ascii.NewLine; break; + case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_t: next_char = Byte_ascii.Tab; break; + case Byte_ascii.Ltr_R: + case Byte_ascii.Ltr_r: next_char = Byte_ascii.CarriageReturn; break; + case Byte_ascii.Ltr_U: + case Byte_ascii.Ltr_u: { // EX: "\u007C" + rslt_val = Rslt_dirty; + Parse_utf16(tmp_list, raw, next_pos + 1, raw_len); // +1 to skip u + i = next_pos + 4; // +4 to skip utf16 seq; EX: \u007C; +4 for 007C + continue; + } + case Byte_ascii.Ltr_X: + case Byte_ascii.Ltr_x: { // EX: "\xc2" + rslt_val = Rslt_dirty; + byte[] literal = Bry_.Add(CONST_utf_prefix, Bry_.Mid(raw, next_pos + 1, next_pos + 3)); + tmp_list.Add(new Php_text_itm_utf16(i, i + 4, literal)); + i = next_pos + 2; // +2 to skip rest; EX: \xc2; +2 for c2 + continue; + } + } + tmp_list.Add(new Php_text_itm_escaped(i, next_pos, next_char)); rslt_val = Rslt_dirty; + i = next_pos; + break; + case Byte_ascii.Dollar: + if (txt_bgn != -1) {tmp_list.Add(new Php_text_itm_text(txt_bgn, i)); txt_bgn = -1;} + if (i == raw_last) { + //throw Err_mgr._.fmt_auto_(GRP_KEY, "dollar_is_last_char", String_.new_utf8_(raw)); + } + int int_end = Find_fwd_non_int(raw, i + 1, raw_len); // +1 to search after $ + int int_val = Bry_.X_to_int_or(raw, i + 1, int_end, -1); // +1 to search after $ + if (int_val == -1) { + tmp_list.Add(new Php_text_itm_text(i, i + 1)); + continue; + } + //throw Err_mgr._.fmt_auto_(GRP_KEY, "invalid_arg", String_.new_utf8_(raw)); + tmp_list.Add(new Php_text_itm_arg(i, int_end, int_val)); + rslt_val = Rslt_fmt; + i = int_end - 1; // -1 b/c i++ in for loop + break; + default: + if (txt_bgn == -1) txt_bgn = i; + break; + } + } + if (txt_bgn != -1) {tmp_list.Add(new Php_text_itm_text(txt_bgn, raw_len)); txt_bgn = -1; rslt_val = Rslt_dirty;} + rslt.Val_(rslt_val); + } private static final byte[] CONST_utf_prefix = Bry_.new_ascii_("\\u00"); + private void Parse_utf16(ListAdp rv, byte[] src, int bgn, int src_len) { + int end = bgn + 4; + if (end >= src_len) throw Err_mgr._.fmt_auto_(GRP_KEY, "utf16_parse", String_.new_utf8_(src)); + int v = Int_.Xto_int_hex(src, bgn, end); // +2; skip "\" + "u" + byte[] literal = gplx.intl.Utf16_.Encode_int_to_bry(v); + rv.Add(new Php_text_itm_utf16(bgn, end, literal)); + } + private static final String GRP_KEY = "xowa.php.quote_text_parser"; + public static int Find_fwd_non_int(byte[] src, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + default: + return i; + } + } + return end; + } +} diff --git a/400_xowa/src/gplx/php/Php_text_itm_tst.java b/400_xowa/src/gplx/php/Php_text_itm_tst.java new file mode 100644 index 000000000..ca9c5bfad --- /dev/null +++ b/400_xowa/src/gplx/php/Php_text_itm_tst.java @@ -0,0 +1,39 @@ +/* +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.php; import gplx.*; +import org.junit.*; +public class Php_text_itm_tst { + @Test public void Basic() {Tst_("abcde", "abcde");} + @Test public void Escaped() {Tst_("a\\$b\\\"c\\td\\ne", "a$b\"c\td\ne");} + @Test public void Fmt() {Tst_("a$1b$2c", "a~{0}b~{1}c");} + @Test public void Utf16() {Tst_("a\\u007Cd", "a|d");} + @Test public void Utf8_nbsp() {Tst_("a\\xc2\\xa0d", "a\\u00c2\\u00a0d");} + private void Tst_(String raw_str, String expd) { + Php_text_itm_parser parser = new Php_text_itm_parser(); + ListAdp list = ListAdp_.new_(); + byte[] raw = Bry_.new_utf8_(raw_str); + parser.Parse(list, raw); + Bry_bfr bfr = Bry_bfr.reset_(255); + int list_len = list.Count(); + for (int i = 0; i < list_len; i++) { + Php_text_itm itm = (Php_text_itm)list.FetchAt(i); + itm.Bld(bfr, raw); + } + Tfds.Eq(expd, bfr.XtoStrAndClear()); + } +} diff --git a/400_xowa/src/gplx/php/Php_tkn.java b/400_xowa/src/gplx/php/Php_tkn.java new file mode 100644 index 000000000..7a70f583b --- /dev/null +++ b/400_xowa/src/gplx/php/Php_tkn.java @@ -0,0 +1,74 @@ +/* +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.php; import gplx.*; +public interface Php_tkn { + byte Tkn_tid(); + int Src_bgn(); + int Src_end(); +} +class Php_tkn_ { + public static final byte Tid_txt = 1, Tid_declaration = 2, Tid_ws = 3, Tid_comment = 4, Tid_var = 5, Tid_eq = 6, Tid_eq_kv = 7, Tid_semic = 8, Tid_comma = 9, Tid_paren_bgn = 10, Tid_paren_end = 11, Tid_null = 12, Tid_false = 13, Tid_true = 14, Tid_ary = 15, Tid_num = 16, Tid_quote = 17, Tid_brack_bgn = 18, Tid_brack_end = 19; + public static String Xto_str(byte tid) {return Byte_.XtoStr(tid);} +} +abstract class Php_tkn_base implements Php_tkn { + public abstract byte Tkn_tid(); + public int Src_bgn() {return src_bgn;} private int src_bgn; + public int Src_end() {return src_end;} public void Src_end_(int v) {this.src_end = v;} private int src_end; + public void Src_rng_(int src_bgn, int src_end) {this.src_bgn = src_bgn; this.src_end = src_end;} +} +class Php_tkn_generic extends Php_tkn_base { + public Php_tkn_generic(int src_bgn, int src_end, byte tid) {this.Src_rng_(src_bgn, src_end); this.tid = tid;} + @Override public byte Tkn_tid() {return tid;} private byte tid; +} +class Php_tkn_txt extends Php_tkn_base { + public Php_tkn_txt(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_txt;} +} +class Php_tkn_ws extends Php_tkn_base { + public Php_tkn_ws(int src_bgn, int src_end, byte ws_tid) {this.Src_rng_(src_bgn, src_end); this.ws_tid = ws_tid;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_ws;} + public byte Ws_tid() {return ws_tid;} private byte ws_tid; + public static final byte Tid_space = 0, Tid_nl = 1, Tid_tab = 2, Tid_cr = 3; +} +class Php_tkn_comment extends Php_tkn_base { + public Php_tkn_comment(int src_bgn, int src_end, byte comment_tid) {this.Src_rng_(src_bgn, src_end); this.comment_tid = comment_tid;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_comment;} + public byte Comment_tid() {return comment_tid;} private byte comment_tid; + public static final byte Tid_null = 0, Tid_mult = 1, Tid_slash = 2, Tid_hash = 3; +} +class Php_tkn_var extends Php_tkn_base { + public Php_tkn_var(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_var;} + public byte[] Var_name(byte[] src) {return Bry_.Mid(src, this.Src_bgn() + 1, this.Src_end());} // NOTE: assume vars are of form $abc; +1 to skip first $ +} +class Php_tkn_num extends Php_tkn_base { + public Php_tkn_num(int src_bgn, int src_end) {this.Src_rng_(src_bgn, src_end);} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_num;} + public int Num_val_int(byte[] src) {return Bry_.X_to_int_or(src, this.Src_bgn(), this.Src_end(), Int_.MinValue);} +} +class Php_tkn_quote extends Php_tkn_base { + public Php_tkn_quote(int src_bgn, int src_end, byte quote_tid) {this.Src_rng_(src_bgn, src_end); this.quote_tid = quote_tid;} + @Override public byte Tkn_tid() {return Php_tkn_.Tid_quote;} + public byte Quote_tid() {return quote_tid;} private byte quote_tid; + public byte[] Quote_text(byte[] src) {return Bry_.Mid(src, this.Src_bgn() + 1, this.Src_end() - 1);} // NOTE: assume quote are of form 'abc'; +1, -1 to skip flanking chars + public static final byte Tid_null = 0, Tid_mult = 1, Tid_slash = 2, Tid_hash = 3; +} +class Php_tkn_declaration extends Php_tkn_base { + @Override public byte Tkn_tid() {return Php_tkn_.Tid_declaration;} + public static final Php_tkn_declaration _ = new Php_tkn_declaration(); +} diff --git a/400_xowa/src/gplx/php/Php_tkn_factory.java b/400_xowa/src/gplx/php/Php_tkn_factory.java new file mode 100644 index 000000000..6c1b1071f --- /dev/null +++ b/400_xowa/src/gplx/php/Php_tkn_factory.java @@ -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 . +*/ +package gplx.php; import gplx.*; +class Php_tkn_factory { + public Php_tkn_generic Generic(int bgn, int end, byte tid) {return new Php_tkn_generic(bgn, end, tid);} + public Php_tkn_txt Txt(int bgn, int end) {return new Php_tkn_txt(bgn, end);} + public Php_tkn Declaration(int bgn, int end) {return Php_tkn_declaration._;} + public Php_tkn_ws Ws(int bgn, int end, byte ws_tid) {return new Php_tkn_ws(bgn, end, ws_tid);} + public Php_tkn_var Var(int bgn, int end) {return new Php_tkn_var(bgn, end);} + public Php_tkn_num Num(int bgn, int end) {return new Php_tkn_num(bgn, end);} + public Php_tkn_comment Comment(int bgn, int end, byte comment_tid) {return new Php_tkn_comment(bgn, end, comment_tid);} + public Php_tkn_quote Quote(int bgn, int end, byte quote_tid) {return new Php_tkn_quote(bgn, end, quote_tid);} +} diff --git a/400_xowa/src/gplx/php/Php_tkn_wkr.java b/400_xowa/src/gplx/php/Php_tkn_wkr.java new file mode 100644 index 000000000..f5e7d74c9 --- /dev/null +++ b/400_xowa/src/gplx/php/Php_tkn_wkr.java @@ -0,0 +1,35 @@ +/* +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.php; import gplx.*; +public interface Php_tkn_wkr { + void Init(Php_ctx ctx); + void Process(Php_tkn tkn); + void Msg_many(byte[] src, int bgn, int end, Gfo_msg_itm itm, Object... args); +} +class Php_tkn_wkr_tkn implements Php_tkn_wkr { + public void Init(Php_ctx ctx) {} + public ListAdp List() {return lines;} ListAdp lines = ListAdp_.new_(); + public Gfo_msg_log Msg_log() {return msg_log;} Gfo_msg_log msg_log = new Gfo_msg_log("gplx.php"); + public void Clear() {lines.Clear(); msg_log.Clear();} + public void Process(Php_tkn tkn) { + lines.Add(tkn); + } + public void Msg_many(byte[] src, int bgn, int end, Gfo_msg_itm itm, Object... args) { + msg_log.Add_itm_many(itm, src, bgn, end, args); + } +} diff --git a/400_xowa/src/gplx/srls/dsvs/Dsv_fld_parser.java b/400_xowa/src/gplx/srls/dsvs/Dsv_fld_parser.java new file mode 100644 index 000000000..fe67f1aae --- /dev/null +++ b/400_xowa/src/gplx/srls/dsvs/Dsv_fld_parser.java @@ -0,0 +1,22 @@ +/* +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.srls.dsvs; import gplx.*; import gplx.srls.*; +public interface Dsv_fld_parser { + void Init(byte fld_dlm, byte row_dlm); + int Parse(Dsv_tbl_parser tbl_parser, Dsv_wkr_base mgr, byte[] src, int pos, int src_len, int fld_idx, int fld_bgn); +} diff --git a/400_xowa/src/gplx/srls/dsvs/Dsv_fld_parser_.java b/400_xowa/src/gplx/srls/dsvs/Dsv_fld_parser_.java new file mode 100644 index 000000000..067b84797 --- /dev/null +++ b/400_xowa/src/gplx/srls/dsvs/Dsv_fld_parser_.java @@ -0,0 +1,85 @@ +/* +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.srls.dsvs; import gplx.*; import gplx.srls.*; +public class Dsv_fld_parser_ { + public static final Dsv_fld_parser Bry_parser = Dsv_fld_parser_bry._; + public static final Dsv_fld_parser Int_parser = Dsv_fld_parser_int._; + public static Err err_fld_unhandled(Dsv_fld_parser parser, Dsv_wkr_base wkr, int fld_idx, byte[] src, int bgn, int end) { + throw Err_.new_fmt_("fld unhandled; parser={0} wkr={1} fld_idx={2} val={3}", ClassAdp_.NameOf_obj(parser), ClassAdp_.NameOf_obj(wkr), fld_idx, String_.new_utf8_(src, bgn, end)); + } +} +class Dsv_fld_parser_bry implements Dsv_fld_parser { + private byte fld_dlm = Byte_ascii.Pipe, row_dlm = Byte_ascii.NewLine; + public void Init(byte fld_dlm, byte row_dlm) { + this.fld_dlm = fld_dlm; this.row_dlm = row_dlm; + } + public int Parse(Dsv_tbl_parser parser, Dsv_wkr_base wkr, byte[] src, int pos, int src_len, int fld_idx, int fld_bgn) { + while (true) { + boolean pos_is_last = pos == src_len; + byte b = pos_is_last ? row_dlm : src[pos]; + if (b == fld_dlm) { + boolean pass = wkr.Write_bry(parser, fld_idx, src, fld_bgn, pos); + if (!pass) throw Dsv_fld_parser_.err_fld_unhandled(this, wkr, fld_idx, src, fld_bgn, pos); + int rv = pos + 1; // fld_dlm is always 1 byte + parser.Update_by_fld(rv); + return rv; + } + else if (b == row_dlm) { + boolean pass = wkr.Write_bry(parser, fld_idx, src, fld_bgn, pos); + if (!pass) throw Dsv_fld_parser_.err_fld_unhandled(this, wkr, fld_idx, src, fld_bgn, pos); + wkr.Commit_itm(parser, pos); + int rv = pos + 1; // row_dlm is always 1 byte + parser.Update_by_row(rv); + return rv; + } + else + ++pos; + } + } + public static final Dsv_fld_parser_bry _ = new Dsv_fld_parser_bry(); Dsv_fld_parser_bry() {} +} +class Dsv_fld_parser_int implements Dsv_fld_parser { + private byte fld_dlm = Byte_ascii.Pipe, row_dlm = Byte_ascii.NewLine; + public void Init(byte fld_dlm, byte row_dlm) { + this.fld_dlm = fld_dlm; this.row_dlm = row_dlm; + } + public int Parse(Dsv_tbl_parser parser, Dsv_wkr_base wkr, byte[] src, int pos, int src_len, int fld_idx, int fld_bgn) { + while (true) { + boolean pos_is_last = pos == src_len; + byte b = pos_is_last ? row_dlm : src[pos]; + if (b == fld_dlm) { + boolean pass = wkr.Write_int(parser, fld_idx, pos, Bry_.X_to_int_or(src, fld_bgn, pos, -1)); + if (!pass) throw Dsv_fld_parser_.err_fld_unhandled(this, wkr, fld_idx, src, fld_bgn, pos); + int rv = pos + 1; // fld_dlm is always 1 byte + parser.Update_by_fld(rv); + return rv; + } + else if (b == row_dlm) { + boolean pass = wkr.Write_int(parser, fld_idx, pos, Bry_.X_to_int_or(src, fld_bgn, pos, -1)); + if (!pass) throw Dsv_fld_parser_.err_fld_unhandled(this, wkr, fld_idx, src, fld_bgn, pos); + wkr.Commit_itm(parser, pos); + int rv = pos + 1; // row_dlm is always 1 byte + parser.Update_by_row(rv); + return rv; + } + else + ++pos; + } + } + public static final Dsv_fld_parser_int _ = new Dsv_fld_parser_int(); Dsv_fld_parser_int() {} +} diff --git a/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser.java b/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser.java new file mode 100644 index 000000000..9988efb3a --- /dev/null +++ b/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser.java @@ -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 . +*/ +package gplx.srls.dsvs; import gplx.*; import gplx.srls.*; +public class Dsv_tbl_parser implements GfoInvkAble, RlsAble { + private Dsv_wkr_base mgr; + private Dsv_fld_parser[] fld_parsers = new Dsv_fld_parser[2]; + public byte[] Src() {return src;} private byte[] src; + public int Fld_bgn() {return fld_bgn;} private int fld_bgn = 0; + public int Fld_idx() {return fld_idx;} private int fld_idx = 0; + public int Row_bgn() {return row_bgn;} private int row_bgn = 0; + public int Row_idx() {return row_idx;} private int row_idx = 0; + public boolean Skip_blank_lines() {return skip_blank_lines;} public Dsv_tbl_parser Skip_blank_lines_(boolean v) {skip_blank_lines = v; return this;} private boolean skip_blank_lines = true; + public byte Fld_dlm() {return fld_dlm;} public Dsv_tbl_parser Fld_dlm_(byte v) {fld_dlm = v; return this;} private byte fld_dlm = Byte_ascii.Pipe; + public byte Row_dlm() {return row_dlm;} public Dsv_tbl_parser Row_dlm_(byte v) {row_dlm = v; return this;} private byte row_dlm = Byte_ascii.NewLine; + public void Init(Dsv_wkr_base mgr, Dsv_fld_parser... fld_parsers) { + this.mgr = mgr; + this.fld_parsers = fld_parsers; + int fld_parsers_len = fld_parsers.length; + for (int i = 0; i < fld_parsers_len; i++) + fld_parsers[i].Init(fld_dlm, row_dlm); + } + public void Clear() { + fld_bgn = fld_idx = row_bgn = row_idx = 0; + } + public Err Err_row_bgn(String fmt, int pos) { + return Err_.new_fmt_(fmt + "; line={0}", String_.new_utf8_(src, row_bgn, pos)); + } + public void Update_by_fld(int pos) { + fld_bgn = pos; + ++fld_idx; + } + public void Update_by_row(int pos) { + row_bgn = fld_bgn = pos; + ++row_idx; + fld_idx = 0; + } + public void Parse(byte[] src) { + this.src = src; + int src_len = src.length; + int pos = 0; + while (pos < src_len) { + if (fld_idx == 0 && skip_blank_lines) { // row committed; skip blank lines + while (pos < src_len) { + if (src[pos] == row_dlm) { + ++pos; + row_bgn = fld_bgn = pos; + } + else + break; + } + } + Dsv_fld_parser fld_parser = fld_parsers[fld_idx]; + pos = fld_parser.Parse(this, mgr, src, pos, src_len, fld_idx, fld_bgn); + } + } + public void Rls() { + src = null; fld_parsers = null; mgr = null; + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_load_by_str)) Parse(m.ReadBry("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } private static final String Invk_load_by_str = "load_by_str"; +} diff --git a/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser_int_tst.java b/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser_int_tst.java new file mode 100644 index 000000000..0ab4a2a8a --- /dev/null +++ b/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser_int_tst.java @@ -0,0 +1,64 @@ +/* +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.srls.dsvs; import gplx.*; import gplx.srls.*; +import org.junit.*; +public class Dsv_tbl_parser_int_tst { + private Dsv_mok_fxt fxt = new Dsv_mok_fxt(); + @Test public void Basic() { + fxt .Test_load(String_.Concat_lines_nl_skip_last + ( "a|1|3" + , "b|2|4" + ) + , fxt.mgr_int_() + , fxt.itm_int_("a", 1, 3) + , fxt.itm_int_("b", 2, 4) + ); + } +} +class Mok_int_itm implements XtoStrAble { + private String fld_0; + private int fld_1, fld_2; + public Mok_int_itm(String fld_0, int fld_1, int fld_2) {this.fld_0 = fld_0; this.fld_1 = fld_1; this.fld_2 = fld_2;} + public String XtoStr() {return String_.Concat_with_str("|", fld_0, Int_.XtoStr(fld_1), Int_.XtoStr(fld_2));} +} +class Mok_int_mgr extends Mok_mgr_base { + public void Clear() {itms.Clear();} + @Override public XtoStrAble[] Itms() {return (XtoStrAble[])itms.XtoAry(XtoStrAble.class);} private ListAdp itms = ListAdp_.new_(); + private String fld_0; + private int fld_1, fld_2; + @Override public Dsv_fld_parser[] Fld_parsers() { + return new Dsv_fld_parser[] {Dsv_fld_parser_bry._, Dsv_fld_parser_int._, Dsv_fld_parser_int._}; + } + @Override public boolean Write_bry(Dsv_tbl_parser parser, int fld_idx, byte[] src, int bgn, int end) { + switch (fld_idx) { + case 0: fld_0 = String_.new_utf8_(src, bgn, end); return true; + default: return false; + } + } + @Override public boolean Write_int(Dsv_tbl_parser parser, int fld_idx, int pos, int val_int) { + switch (fld_idx) { + case 1: fld_1 = val_int; return true; + case 2: fld_2 = val_int; return true; + default: return false; + } + } + @Override public void Commit_itm(Dsv_tbl_parser parser, int pos) { + Mok_int_itm itm = new Mok_int_itm(fld_0, fld_1, fld_2); + itms.Add(itm); + } +} diff --git a/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser_str_tst.java b/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser_str_tst.java new file mode 100644 index 000000000..2c6402cb1 --- /dev/null +++ b/400_xowa/src/gplx/srls/dsvs/Dsv_tbl_parser_str_tst.java @@ -0,0 +1,102 @@ +/* +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.srls.dsvs; import gplx.*; import gplx.srls.*; +import org.junit.*; +public class Dsv_tbl_parser_str_tst { + private Dsv_mok_fxt fxt = new Dsv_mok_fxt(); + @Test public void Basic() { + fxt .Test_load(String_.Concat_lines_nl_skip_last + ( "a|A" + , "b|B" + ) + , fxt.mgr_str_(2) + , fxt.itm_str_("a", "A") + , fxt.itm_str_("b", "B") + ); + } + @Test public void Blank_lines() { + fxt .Test_load(String_.Concat_lines_nl_skip_last + ( "" + , "a|A" + , "" + , "b|B" + , "" + ) + , fxt.mgr_str_(2) + , fxt.itm_str_("a", "A") + , fxt.itm_str_("b", "B") + ); + } + @Test public void Incomplete_row() { + fxt .Test_load(String_.Concat_lines_nl_skip_last + ( "a" + , "b" + , "" + ) + , fxt.mgr_str_(2) + , fxt.itm_str_("a") + , fxt.itm_str_("b") + ); + } +} +abstract class Mok_mgr_base extends Dsv_wkr_base { + public abstract XtoStrAble[] Itms(); +} +class Dsv_mok_fxt { + private Dsv_tbl_parser tbl_parser = new Dsv_tbl_parser(); + public Dsv_mok_fxt Clear() { + tbl_parser.Clear(); + return this; + } + public Mok_mgr_base mgr_int_() {return new Mok_int_mgr();} + public Mok_mgr_base mgr_str_(int len) {return new Mok_str_mgr(len);} + public Mok_str_itm itm_str_(String... flds) {return new Mok_str_itm(flds);} + public Mok_int_itm itm_int_(String fld_0, int fld_1, int fld_2) {return new Mok_int_itm(fld_0, fld_1, fld_2);} + public void Test_load(String src, Mok_mgr_base mgr, XtoStrAble... expd) { + mgr.Load_by_bry(Bry_.new_utf8_(src)); + Tfds.Eq_ary_str(expd, mgr.Itms()); + } +} +class Mok_str_itm implements XtoStrAble { + private String[] flds; + public Mok_str_itm(String[] flds) {this.flds = flds;} + public String XtoStr() {return String_.Concat_with_str("|", flds);} +} +class Mok_str_mgr extends Mok_mgr_base { + private int flds_len; + public Mok_str_mgr(int flds_len) { + this.flds_len = flds_len; + } + public void Clear() {itms.Clear();} + @Override public XtoStrAble[] Itms() {return (XtoStrAble[])itms.XtoAry(XtoStrAble.class);} private ListAdp itms = ListAdp_.new_(); + private ListAdp flds = ListAdp_.new_(); + @Override public boolean Write_bry(Dsv_tbl_parser parser, int fld_idx, byte[] src, int bgn, int end) { + flds.Add(String_.new_utf8_(src, bgn, end)); + return true; + } + @Override public Dsv_fld_parser[] Fld_parsers() { + Dsv_fld_parser[] rv = new Dsv_fld_parser[flds_len]; + for (int i = 0; i < flds_len; i++) + rv[i] = Dsv_fld_parser_.Bry_parser; + return rv; + } + @Override public void Commit_itm(Dsv_tbl_parser parser, int pos) { + Mok_str_itm itm = new Mok_str_itm((String[])flds.XtoAryAndClear(String.class)); + itms.Add(itm); + } +} diff --git a/400_xowa/src/gplx/srls/dsvs/Dsv_wkr_base.java b/400_xowa/src/gplx/srls/dsvs/Dsv_wkr_base.java new file mode 100644 index 000000000..b7af2bec2 --- /dev/null +++ b/400_xowa/src/gplx/srls/dsvs/Dsv_wkr_base.java @@ -0,0 +1,36 @@ +/* +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.srls.dsvs; import gplx.*; import gplx.srls.*; +public abstract class Dsv_wkr_base implements GfoInvkAble { + public abstract Dsv_fld_parser[] Fld_parsers(); + public abstract void Commit_itm(Dsv_tbl_parser parser, int pos); + @gplx.Virtual public boolean Write_bry(Dsv_tbl_parser parser, int fld_idx, byte[] src, int bgn, int end) {return false;} + @gplx.Virtual public boolean Write_int(Dsv_tbl_parser parser, int fld_idx, int pos, int val_int) {return false;} + public void Load_by_bry(byte[] src) { + Dsv_tbl_parser tbl_parser = new Dsv_tbl_parser(); // NOTE: this proc should only be called once, so don't bother caching tbl_parser + tbl_parser.Init(this, this.Fld_parsers()); + tbl_parser.Parse(src); + tbl_parser.Rls(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_load_by_str)) Load_by_bry(m.ReadBry("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + public static final String Invk_load_by_str = "load_by_str"; +} diff --git a/400_xowa/src/gplx/threads/Gfo_thread_pool.java b/400_xowa/src/gplx/threads/Gfo_thread_pool.java new file mode 100644 index 000000000..14cec4694 --- /dev/null +++ b/400_xowa/src/gplx/threads/Gfo_thread_pool.java @@ -0,0 +1,62 @@ +/* +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.threads; import gplx.*; +public class Gfo_thread_pool implements GfoInvkAble { + private Object thread_lock = new Object(); + private ListAdp queue = ListAdp_.new_(); + private GfoMsg run_msg = GfoMsg_.new_cast_(Invk_run_wkr); + private boolean running = false; + public Gfo_usr_dlg Usr_dlg() {return usr_dlg;} public Gfo_thread_pool Usr_dlg_(Gfo_usr_dlg v) {usr_dlg = v; return this;} private Gfo_usr_dlg usr_dlg = Gfo_usr_dlg_.Null; + public void Clear() {synchronized (thread_lock) {queue.Clear(); running = false;}} + public Gfo_thread_pool Add_at_end(Gfo_thread_wkr wkr) { + synchronized (thread_lock) {queue.Add(wkr);} + return this; + } + public void Resume() { + synchronized (thread_lock) { + running = false; + } + this.Run(); + } + public void Run() { + Gfo_thread_wkr wkr = null; + synchronized (thread_lock) { + if (running) return; // already running; discard run request and rely on running-wkr to call Run when done + int len = queue.Count(); if (len == 0) return; // nothing in list; occurs when last item calls Run when done + running = true; + wkr = (Gfo_thread_wkr)ListAdp_.Pop_first(queue); + } + ThreadAdp_.Run_invk_msg(wkr.Name(), this, run_msg.Clear().Add("v", wkr)); + } + private void Run_wkr(Gfo_thread_wkr wkr) { + try {wkr.Exec();} + catch (Exception e) { + usr_dlg.Warn_many("", "", "uncaught exception while running thread; name=~{0} err=~{1}", wkr.Name(), Err_.Message_gplx_brief(e)); + } + finally { + if (wkr.Resume()) + this.Resume(); + } + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_run_wkr)) Run_wkr((Gfo_thread_wkr)m.ReadObj("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_run_wkr = "run_wkr"; +} diff --git a/400_xowa/src/gplx/threads/Gfo_thread_wkr.java b/400_xowa/src/gplx/threads/Gfo_thread_wkr.java new file mode 100644 index 000000000..a0d9bdac1 --- /dev/null +++ b/400_xowa/src/gplx/threads/Gfo_thread_wkr.java @@ -0,0 +1,23 @@ +/* +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.threads; import gplx.*; +public interface Gfo_thread_wkr { + String Name(); + void Exec(); + boolean Resume(); +} diff --git a/400_xowa/src/gplx/xowa/Xoa_app.java b/400_xowa/src/gplx/xowa/Xoa_app.java new file mode 100644 index 000000000..5037c8cf2 --- /dev/null +++ b/400_xowa/src/gplx/xowa/Xoa_app.java @@ -0,0 +1,213 @@ +/* +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.xowa; import gplx.*; +import gplx.ios.*; import gplx.threads.*; +import gplx.xowa.apps.*; import gplx.xowa.apps.caches.*; import gplx.xowa.apps.fsys.*; import gplx.xowa.apis.*; +import gplx.xowa.langs.*; import gplx.xowa.specials.*; import gplx.xowa.cfgs2.*; +import gplx.xowa.wikis.*; import gplx.xowa.users.*; import gplx.xowa.gui.*; import gplx.xowa.cfgs.*; import gplx.xowa.ctgs.*; import gplx.xowa.html.tocs.*; import gplx.xowa.fmtrs.*; import gplx.xowa.html.*; +import gplx.xowa.parsers.*; import gplx.xowa.parsers.amps.*; +import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.math.*; +import gplx.xowa.parsers.logs.*; import gplx.xowa.servers.tcp.*; import gplx.xowa.servers.http.*; +public class Xoa_app implements GfoInvkAble { + public Xoa_app(Gfo_usr_dlg usr_dlg, Io_url root_dir, Io_url user_dir, String bin_dir_name) { + this.usr_dlg = usr_dlg; + log_wtr = usr_dlg.Log_wtr(); + cfg_mgr = new Xoa_cfg_mgr(this); + api_root = new Xoapi_root(this); + url_cmd_eval = new Xoa_fsys_eval(this); + fsys_mgr = new Xoa_fsys_mgr(this, root_dir, bin_dir_name); + user = new Xou_user(this, user_dir); + log_wtr.Log_dir_(user.Fsys_mgr().App_temp_dir().GenSubDir("log")); + fsys_mgr.Temp_dir_(user.Fsys_mgr().App_temp_dir()); + lang_mgr = new Xoa_lang_mgr(this); + wiki_mgr = new Xoa_wiki_mgr(this); + gui_mgr = new Xoa_gui_mgr(this); + bldr = new Xob_bldr(this); + file_mgr.Init_app(this, usr_dlg); + href_parser = new Xoh_href_parser(url_converter_href, url_parser.Url_parser()); + sanitizer = new Xop_sanitizer(parser_amp_mgr, msg_log); + user_mgr = new Xou_user_mgr(this, user); + sys_cfg = new Xoa_sys_cfg(this); + cur_redirect = new Xoa_cur(this); + shell = new Xoa_shell(this); + setup_mgr = new Xoi_setup_mgr(this); + gfs_mgr = new Xoa_gfs_mgr(this); + xtn_mgr = new Xow_xtn_mgr().Ctor_by_app(this); + hive_mgr = new Xoa_hive_mgr(this); + Io_url.Http_file_str_encoder = url_converter_fsys; + tcp_server.App_ctor(this); + fmtr_mgr = new Xoa_fmtr_mgr(this); + log_mgr = new Xop_log_mgr(this); + http_server = new Http_server_mgr(this); + cfg_regy = new Xocfg_regy(this); + html_mgr = new Xoh_html_mgr(this); +// queue_file = new Xop_queue_mgr(this); + } + public NumberParser Utl_num_parser() {return utl_num_parser;} private NumberParser utl_num_parser = new NumberParser(); + public void Init() { + stage = Xoa_stage_.Tid_init; + xtn_mgr.Init_by_app(this); + log_wtr.Init(); + gui_mgr.Init_by_app(); + fsys_mgr.Init(); + user.Init_by_app(); + file_mgr.Init_by_app(); + wiki_mgr.Init_by_app(); + gplx.xowa.utls.upgrades.Xoa_upgrade_mgr.Check(this); + ctg_mgr.Init_by_app(this); + setup_mgr.Init_by_app(this); + thread_mgr.Usr_dlg_(usr_dlg); + html_mgr.Init_by_app(this); + } + public boolean Launch_done() {return stage == Xoa_stage_.Tid_launch;} + public void Launch() { + if (Launch_done()) return; + stage = Xoa_stage_.Tid_launch; + user.Cfg_mgr().Setup_mgr().Setup_run_check(this); log_bfr.Add("app.upgrade.done"); + gplx.xowa.users.prefs.Prefs_converter._.Check(this); + } + public byte Stage() {return stage;} public Xoa_app Stage_(byte v) {stage = v; return this;} private byte stage = Xoa_stage_.Tid_ctor; + public boolean Term_cbk() { + if (setup_mgr.Cmd_mgr().Working()) { + if (!gui_mgr.Kit().Ask_yes_no("", "", "An import is in progress. Are you sure you want to exit?")) return false; + } + gui_mgr.Browser_win().Usr_dlg().Canceled_y_(); + user.App_term(); + log_wtr.Term(); + log_mgr.Rls(); + if (Scrib_core.Core() != null) Scrib_core.Core().Term(); + wiki_mgr.Rls(); + return true; + } + public Xoa_wiki_mgr Wiki_mgr() {return wiki_mgr;} private Xoa_wiki_mgr wiki_mgr; + public Xou_user_mgr User_mgr() {return user_mgr;} private Xou_user_mgr user_mgr; + public Xof_file_mgr File_mgr() {return file_mgr;} private Xof_file_mgr file_mgr = new Xof_file_mgr(); + public Xoa_lang_mgr Lang_mgr() {return lang_mgr;} private Xoa_lang_mgr lang_mgr; + public Xoa_gui_mgr Gui_mgr() {return gui_mgr;} private Xoa_gui_mgr gui_mgr; + public Xou_user User() {return user;} private Xou_user user; + public Xob_bldr Bldr() {return bldr;} private Xob_bldr bldr; + public Xow_xtn_mgr Xtn_mgr() {return xtn_mgr;} private Xow_xtn_mgr xtn_mgr; + public Xoapi_root Api_root() {return api_root;} private Xoapi_root api_root; + public Xop_tkn_mkr Tkn_mkr() {return tkn_mkr;} private Xop_tkn_mkr tkn_mkr = new Xop_tkn_mkr(); + public Gfo_usr_dlg Usr_dlg() {return usr_dlg;} private Gfo_usr_dlg usr_dlg; + public Gfo_log_wtr Log_wtr() {return log_wtr;} private Gfo_log_wtr log_wtr; + public Xoa_gfs_mgr Gfs_mgr() {return gfs_mgr;} private Xoa_gfs_mgr gfs_mgr; + public Xoa_special_mgr Special_mgr() {return special_mgr;} private Xoa_special_mgr special_mgr = new gplx.xowa.specials.Xoa_special_mgr(); + public Xop_log_mgr Log_mgr() {return log_mgr;} private Xop_log_mgr log_mgr; + public Xoa_shell Shell() {return shell;} private Xoa_shell shell; + public byte Mode() {return mode;} public Xoa_app Mode_(byte v) {mode = v; return this;} private byte mode = Xoa_app_.Mode_console; + public Xop_amp_mgr Parser_amp_mgr() {return parser_amp_mgr;} private Xop_amp_mgr parser_amp_mgr = new Xop_amp_mgr(); + public Xoa_thread_mgr Thread_mgr() {return thread_mgr;} private Xoa_thread_mgr thread_mgr = new Xoa_thread_mgr(); + + + public Xoa_fsys_mgr Fsys_mgr() {return fsys_mgr;} private Xoa_fsys_mgr fsys_mgr; + public Xoa_hive_mgr Hive_mgr() {return hive_mgr;} private Xoa_hive_mgr hive_mgr; + public Xoa_url_parser Url_parser() {return url_parser;} private Xoa_url_parser url_parser = new Xoa_url_parser(); + public Xoh_href_parser Href_parser() {return href_parser;} private Xoh_href_parser href_parser; + public Xop_sanitizer Sanitizer() {return sanitizer;} private Xop_sanitizer sanitizer; + public Xop_xatr_parser Xatr_parser() {return xatr_parser;} private Xop_xatr_parser xatr_parser = new Xop_xatr_parser(); + public Xop_xnde_tag_regy Xnde_tag_regy() {return xnde_tag_regy;} private Xop_xnde_tag_regy xnde_tag_regy = new Xop_xnde_tag_regy(); + public Xof_math_subst_regy Math_subst_regy() {return math_subst_regy;} private Xof_math_subst_regy math_subst_regy = new Xof_math_subst_regy(); + public Gfo_usr_dlg Gui_wtr() {return gui_mgr.Browser_win().Usr_dlg();} + + public Xoi_setup_mgr Setup_mgr() {return setup_mgr;} private Xoi_setup_mgr setup_mgr; + public Gfo_msg_log Msg_log() {return msg_log;} private Gfo_msg_log msg_log = new Gfo_msg_log(Xoa_app_.Name); + public Gfo_msg_log Msg_log_null() {return msg_log_null;} private Gfo_msg_log msg_log_null = new Gfo_msg_log("null_log"); + + public Url_encoder Url_converter_id() {return url_converter_id;} private Url_encoder url_converter_id = Url_encoder.new_html_id_(); + public Url_encoder Url_converter_url() {return url_converter_url;} private Url_encoder url_converter_url = Url_encoder.new_http_url_(); + public Url_encoder Url_converter_url_ttl() {return url_converter_url_ttl;} private Url_encoder url_converter_url_ttl = Url_encoder.new_http_url_ttl_(); + public Url_encoder Url_converter_href() {return url_converter_href;} private Url_encoder url_converter_href = Url_encoder.new_html_href_mw_(); + public Url_encoder Url_converter_comma() {return url_converter_comma;} private Url_encoder url_converter_comma = Url_encoder.url_comma(); + public Url_encoder Url_converter_gfs() {return url_converter_gfs;} private Url_encoder url_converter_gfs = Url_encoder.new_gfs_(); + public Url_encoder Url_converter_fsys() {return url_converter_fsys;} private Url_encoder url_converter_fsys = Url_encoder.new_fsys_lnx_(); + public Url_encoder Url_converter_fsys_safe() {return url_converter_fsys_safe;} private Url_encoder url_converter_fsys_safe = Url_encoder.new_fsys_wnt_(); + public Xoh_file_main_wkr File_main_wkr() {return file_main_wkr;} private Xoh_file_main_wkr file_main_wkr = new Xoh_file_main_wkr(); + public ByteTrieMgr_slim Utl_trie_tblw_ws() {return utl_trie_tblw_ws;} private ByteTrieMgr_slim utl_trie_tblw_ws = Xop_tblw_ws_itm.trie_(); + public Bry_bfr_mkr Utl_bry_bfr_mkr() {return utl_bry_bfr_mkr;} Bry_bfr_mkr utl_bry_bfr_mkr = new Bry_bfr_mkr(); + public Gfo_fld_rdr Utl_fld_rdr() {return utl_fld_rdr;} Gfo_fld_rdr utl_fld_rdr = Gfo_fld_rdr.xowa_(); + public Gfo_log_bfr Log_bfr() {return log_bfr;} private Gfo_log_bfr log_bfr = new Gfo_log_bfr(); + public Xoa_sys_cfg Sys_cfg() {return sys_cfg;} private Xoa_sys_cfg sys_cfg; + public Bry_fmtr Tmp_fmtr() {return tmp_fmtr;} Bry_fmtr tmp_fmtr = Bry_fmtr.new_(""); + public boolean Xwiki_missing(byte[] wiki_key) {return user.Wiki().Xwiki_mgr().Get_by_key(wiki_key) == null;} // NOTE: only the user_wiki has a full list of all wikis b/c it has xwiki objects; wiki_mgr does not, b/c it has heavier wiki objects which are loaded dynamically; + public boolean Xwiki_exists(byte[] wiki_key) {return user.Wiki().Xwiki_mgr().Get_by_key(wiki_key) != null;} + public Xoa_ctg_mgr Ctg_mgr() {return ctg_mgr;} private Xoa_ctg_mgr ctg_mgr = new Xoa_ctg_mgr(); + public Xoa_fsys_eval Url_cmd_eval() {return url_cmd_eval;} Xoa_fsys_eval url_cmd_eval; + public Xoa_cur Cur_redirect() {return cur_redirect;} private Xoa_cur cur_redirect; + public Xoa_cfg_mgr Cfg_mgr() {return cfg_mgr;} private Xoa_cfg_mgr cfg_mgr; + public Xocfg_regy Cfg_regy() {return cfg_regy;} private Xocfg_regy cfg_regy; + public Io_stream_zip_mgr Zip_mgr() {return zip_mgr;} Io_stream_zip_mgr zip_mgr = new Io_stream_zip_mgr(); + public gplx.xowa.html.Xoh_html_mgr Html_mgr() {return html_mgr;} private Xoh_html_mgr html_mgr; + public Xoa_cache_mgr Cache_mgr() {return cache_mgr;} private Xoa_cache_mgr cache_mgr = new Xoa_cache_mgr(); + + public Xosrv_server Tcp_server() {return tcp_server;} private Xosrv_server tcp_server = new Xosrv_server(); + public Http_server_mgr Http_server() {return http_server;} private Http_server_mgr http_server; + + private Xoa_fmtr_mgr fmtr_mgr; + public void Reset_all() { + this.Free_mem(true); + gplx.xowa.xtns.scribunto.Scrib_core.Core_invalidate(); + Env_.GarbageCollect(); + } + public void Free_mem(boolean clear_ctx) { + utl_bry_bfr_mkr.Clear(); + msg_log.Clear(); + wiki_mgr.Free_mem(clear_ctx); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_gui)) return gui_mgr; + else if (ctx.Match(k, Invk_api)) return api_root; + else if (ctx.Match(k, Invk_bldr)) return bldr; + else if (ctx.Match(k, Invk_wikis)) return wiki_mgr; + else if (ctx.Match(k, Invk_fsys)) return fsys_mgr; + else if (ctx.Match(k, Invk_files)) return file_mgr; + else if (ctx.Match(k, Invk_langs)) return lang_mgr; + else if (ctx.Match(k, Invk_users)) return user_mgr; + else if (ctx.Match(k, Invk_user)) return user; + else if (ctx.Match(k, Invk_sys_cfg)) return sys_cfg; + else if (ctx.Match(k, Invk_cur)) return cur_redirect; + else if (ctx.Match(k, Invk_html)) return html_mgr; + else if (ctx.Match(k, Invk_shell)) return shell; + else if (ctx.Match(k, Invk_log)) return log_wtr; + else if (ctx.Match(k, Invk_setup)) return setup_mgr; + else if (ctx.Match(k, Invk_scripts)) return gfs_mgr; + else if (ctx.MatchPriv(k, Invk_term_cbk)) return this.Term_cbk(); + else if (ctx.Match(k, Invk_xtns)) return xtn_mgr; + else if (ctx.Match(k, Invk_ctg_mgr)) return ctg_mgr; + else if (ctx.Match(k, Invk_cfgs)) return cfg_mgr; + else if (ctx.Match(k, Invk_usr_dlg)) return usr_dlg; + else if (ctx.Match(k, Invk_specials)) return special_mgr; + else if (ctx.Match(k, Invk_server)) return tcp_server; + else if (ctx.Match(k, Invk_http_server)) return http_server; + else if (ctx.Match(k, Invk_app)) return this; + else if (ctx.Match(k, Invk_xowa)) return this; + else if (ctx.Match(k, Invk_fmtrs)) return fmtr_mgr; + else if (ctx.Match(k, Invk_cfg)) return cfg_regy; + else return GfoInvkAble_.Rv_unhandled; + } + public static final String Invk_gui = "gui", Invk_bldr = "bldr", Invk_wikis = "wikis", Invk_files = "files", Invk_langs = "langs", Invk_users = "users" + , Invk_sys_cfg = "sys_cfg", Invk_fsys = "fsys", Invk_cur = "cur", Invk_shell = "shell", Invk_log = "log" + , Invk_setup = "setup", Invk_scripts = "scripts", Invk_user = "user", Invk_xtns = "xtns", Invk_ctg_mgr = "ctg_mgr" + , Invk_cfgs = "cfgs", Invk_app = "app", Invk_xowa = "xowa", Invk_usr_dlg = "usr_dlg", Invk_specials = "specials", Invk_html = "html" + , Invk_server = "tcp_server", Invk_http_server = "http_server" + , Invk_fmtrs = "fmtrs" + , Invk_cfg = "cfg" + , Invk_api = "api" + ; + public static final String Invk_term_cbk = "term_cbk"; +} diff --git a/400_xowa/src/gplx/xowa/Xoa_app_.java b/400_xowa/src/gplx/xowa/Xoa_app_.java new file mode 100644 index 000000000..9f688cf0c --- /dev/null +++ b/400_xowa/src/gplx/xowa/Xoa_app_.java @@ -0,0 +1,194 @@ +/* +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.xowa; import gplx.*; +import gplx.gfui.*; import gplx.xowa.users.*; +public class Xoa_app_ { + public static void Run(String... args) { + Xoa_app_boot_mgr boot_mgr = new Xoa_app_boot_mgr(); + boot_mgr.Run(args); + } + public static final String Name = "xowa"; + public static final String Version = "1.6.5.1"; + public static String Build_date = "2012-12-30 00:00:00"; + public static String Op_sys; + public static String User_agent = ""; + public static final Gfo_msg_grp Nde = Gfo_msg_grp_.prj_(Name); + public static Gfo_usr_dlg usr_dlg_console_() { + Gfo_usr_dlg rv = new Gfo_usr_dlg_base(); + rv.Ui_wkr_(Gfo_usr_dlg_ui_.Console); + rv.Log_wtr_(new Gfo_log_wtr_base()); + rv.Log_wtr().Queue_enabled_(true); + return rv; + } + public static final byte Mode_console = 0, Mode_gui = 1; +} +class Xoa_app_boot_mgr { + private Gfo_usr_dlg usr_dlg; private Gfo_log_wtr log_wtr; private String chkpoint = "null"; + public void Run(String[] args) { + try { + if (!Init_env(args)) return; + App_cmd_mgr args_mgr = Init_args_mgr(args); if (args_mgr == null) return; + Run_app(args_mgr); + } + catch (Exception e) { + String err_str = Err_.Message_gplx(e); + log_wtr.Log_err(err_str); + ConsoleAdp._.WriteLine(err_str); + if (log_wtr.Log_dir() == null) log_wtr.Log_dir_(Env_.AppUrl().OwnerDir().GenSubFil("xowa.log")); + log_wtr.Queue_enabled_(false); + } + } + private boolean Init_env(String[] args) { + Gfo_usr_dlg_._ = usr_dlg = Xoa_app_.usr_dlg_console_(); + log_wtr = usr_dlg.Log_wtr(); log_wtr.Log_msg_to_session_fmt("env.init: version=~{0}", Xoa_app_.Version); + GfuiEnv_.Init_swt(args, Xoa_app_.class); + Io_url jar_url = Env_.AppUrl(); + Xoa_app_.Build_date = Io_mgr._.QueryFil(jar_url).ModifiedTime().XtoUtc().XtoStr_fmt("yyyy-MM-dd HH:mm"); + log_wtr.Log_msg_to_session_fmt("env.init: jar_url=~{0}; build_date=~{1}", jar_url.NameAndExt(), Xoa_app_.Build_date); + log_wtr.Log_msg_to_session_fmt("env.init: op_sys=~{0}", Op_sys.Cur().Xto_str()); + chkpoint = "init_env"; + return true; + } + private App_cmd_mgr Init_args_mgr(String[] args) { + App_cmd_mgr rv = new App_cmd_mgr(); + try { + String hdr = String_.Concat_lines_nl_skip_last + ( Env_.GenHdr(false, "XOWA", "XOWA: the XOWA Offline Wiki Application\n", "") + , String_.Repeat("-", 80) + , "" + , "version: " + Xoa_app_.Version + "; build date: " + Xoa_app_.Build_date + ); + rv + .Fmt_hdr_(hdr) + .Expd_add_many + ( App_cmd_arg.opt_("root_dir").Example_url_("C:\\xowa").Note_("root directory for xowa; defaults to current directory of xowa.jar") + , App_cmd_arg.opt_("user_dir").Example_url_("C:\\xowa\\user\\" + Xou_user.Key_xowa_user).Note_("directory for user_data; defaults to '/xowa/user/" + Xou_user.Key_xowa_user + "'") + , App_cmd_arg.opt_("wiki_dir").Example_url_("C:\\xowa\\wiki\\").Note_("directory for wikis; defaults to '/xowa/wiki/'") + , App_cmd_arg.opt_("bin_dir_name").Example_("windows").Note_("platform-dependent directory name inside /xowa/bin/; valid values are 'linux', 'macosx', 'windows', 'linux_64', 'macosx_64', 'windows_64'; defaults to detected version") + , App_cmd_arg.opt_("app_mode").Example_("gui").Note_("type of app to run; valid values are 'gui', 'cmd', 'server', 'http_server'; defaults to 'gui'") + , App_cmd_arg.opt_("cmd_file").Example_url_("C:\\xowa\\xowa.gfs").Note_("file_path of script to execute; defaults to 'xowa.gfs'") + , App_cmd_arg.opt_("cmd_text").Example_("\"app.shell.fetch_page('en.wikipedia.org/wiki/Earth', 'html');\"").Note_("script to run; runs after cmd_file; does nothing if empty; default is empty.\nCurrently a useful cmd is to do 'java -jar xowa_your_platform.jar --app_mode cmd --show_license n --show_args n --cmd_text \"app.shell.fetch_page('en.wikipedia.org/wiki/Earth' 'html');\"'. This will output the page's html to the console. You can also change 'html' to 'wiki' to get the wikitext.") + , App_cmd_arg.opt_("url").Example_("en.wikipedia.org/wiki/Earth").Note_("url to be shown when xowa first launches; default is home/wiki/Main_Page") + , App_cmd_arg.opt_("server_port_recv").Example_("55000").Note_("applies to --app_mode server; port where xowa server will receive messages; clients should send messages to this port") + , App_cmd_arg.opt_("server_port_send").Example_("55001").Note_("applies to --app_mode server; port where xowa server will send messages; clients should listen for messages from this port") + , App_cmd_arg.opt_("http_server_port").Example_("8080").Note_("applies to --app_mode http_server; port used by http_server; default is 8080") + , App_cmd_arg.sys_header_("show_license").Dflt_(true) + , App_cmd_arg.sys_args_("show_args").Dflt_(true) + , App_cmd_arg.sys_help_() + ) + ; chkpoint = "fmt_hdr"; + boolean pass = rv.Args_process(args); chkpoint = "args_process"; + rv.Print_header(usr_dlg); // always call print_header + if (!pass) { + rv.Print_args(usr_dlg); + rv.Print_fail(usr_dlg); + rv.Print_help(usr_dlg, Xoa_app_.Name); + return null; + } + if (rv.Args_has_help()) { + rv.Print_help(usr_dlg, Xoa_app_.Name); + return null; + } + if (rv.Args_get("show_args").Val_as_bool()) + rv.Print_args(usr_dlg); + } catch (Exception e) {usr_dlg.Warn_many("", "", "args failed: ~{0} ~{1}", chkpoint, Err_.Message_gplx(e)); return null;} + return rv; + } + private void Run_app(App_cmd_mgr args_mgr) { + boolean app_mode_gui = false; + Xoa_app app = null; + try { + // init vars + Io_url jar_dir = Env_.AppUrl().OwnerDir(); + Io_url root_dir = args_mgr.Args_get("root_dir").Val_as_url_rel_dir_or(jar_dir, jar_dir); + Io_url user_dir = args_mgr.Args_get("user_dir").Val_as_url_rel_dir_or(root_dir.GenSubDir("user"), root_dir.GenSubDir_nest("user", Xou_user.Key_xowa_user)); + Io_url wiki_dir = args_mgr.Args_get("wiki_dir").Val_as_url_rel_dir_or(root_dir.GenSubDir("wiki"), root_dir.GenSubDir("wiki")); + Io_url cmd_file = args_mgr.Args_get("cmd_file").Val_as_url_rel_fil_or(jar_dir, root_dir.GenSubFil("xowa.gfs")); + String app_mode = args_mgr.Args_get("app_mode").Val_as_str_or("gui"); + String launch_url = args_mgr.Args_get("url").Val_as_str_or(Xoa_sys_cfg.Launch_url_dflt); + int server_port_recv = args_mgr.Args_get("server_port_recv").Val_as_int_or(55000); + int server_port_send = args_mgr.Args_get("server_port_send").Val_as_int_or(55001); + int http_server_port = args_mgr.Args_get("http_server_port").Val_as_int_or(8080); + Xoa_app_.Op_sys = args_mgr.Args_get("bin_dir_name").Val_as_str_or(Bin_dir_name()); + Xoa_app_.User_agent = String_.Format("XOWA/{0} ({1}) [gnosygnu@gmail.com]", Xoa_app_.Version, Xoa_app_.Op_sys); + String cmd_text = args_mgr.Args_get("cmd_text").Val_as_str_or(null); + app_mode_gui = String_.Eq(app_mode, "gui"); + + // init app + app = new Xoa_app(usr_dlg, root_dir, user_dir, Xoa_app_.Op_sys); usr_dlg.Log_wtr().Queue_enabled_(false); log_wtr.Log_msg_to_session_fmt("app.init"); + app.Fsys_mgr().Wiki_dir_(wiki_dir); + try { + app.Sys_cfg().Lang_(System_lang()); chkpoint = "lang"; + app.Sys_cfg().Launch_url_(launch_url); chkpoint = "url"; + app.Tcp_server().Rdr_port_(server_port_recv).Wtr_port_(server_port_send); + app.Http_server().Port_(http_server_port); + app.Init(); chkpoint = "init_gfs"; + } + catch (Exception e) {usr_dlg.Warn_many("", "", "app init failed: ~{0} ~{1}", chkpoint, Err_.Message_gplx(e));} + app.Gui_wtr().Log_wtr_(app.Log_wtr()); // NOTE: log_wtr must be set for cmd-line (else process will fail); + + // run gfs + try {app.Gfs_mgr().Run_url(cmd_file); chkpoint = "run_url";} + catch (Exception e) { + usr_dlg.Warn_many("", "", "script file failed: ~{0} ~{1} ~{2}", chkpoint, cmd_file.Raw(), Err_.Message_gplx(e)); + if (app_mode_gui) + GfuiEnv_.ShowMsg(Err_.Message_gplx_brief(e)); + } + + // launch + app.Launch(); chkpoint = "launch"; + if (String_.Eq(app_mode, "server")) + app.Tcp_server().Run(); + else if (String_.Eq(app_mode, "http_server")) + app.Http_server().Run(); + else { + if (cmd_text != null) + ConsoleAdp._.WriteLine_utf8(Object_.XtoStr_OrEmpty(app.Gfs_mgr().Run_str(cmd_text))); + if (app_mode_gui) { + app.Mode_(Xoa_app_.Mode_gui); + app.Gui_mgr().Run(); chkpoint = "run"; + } + else // teardown app, else lua will keep process running + if (gplx.xowa.xtns.scribunto.Scrib_core.Core() != null) gplx.xowa.xtns.scribunto.Scrib_core.Core().Term(); + } + } + catch (Exception e) {usr_dlg.Warn_many("", "", "app launch failed: ~{0} ~{1}", chkpoint, Err_.Message_gplx(e));} + finally { + if (app != null && app_mode_gui) // only cancel if app_mode_gui is true; (force cmd_line to end process) + app.Setup_mgr().Cmd_mgr().Canceled_y_(); + } + } + private static String Bin_dir_name() { + String rv = ""; + Op_sys op_sys = Op_sys.Cur(); + switch (op_sys.Tid()) { + case Op_sys.Tid_lnx: rv = "linux"; break; + case Op_sys.Tid_wnt: rv = "windows"; break; + case Op_sys.Tid_osx: rv = "macosx"; break; + default: throw Err_mgr._.unhandled_("unknown platform " + Op_sys.Cur()); + } + if (op_sys.Bitness() == Op_sys.Bitness_64) rv += "_64"; + return rv; + } + private static byte[] System_lang() { + String lang_code = Env_.Env_prop__user_language(); + byte[] lang_code_bry = Bry_.new_ascii_(lang_code); + Xol_lang_itm lang_itm = Xol_lang_itm_.Get_by_key(lang_code_bry); + return lang_itm == null ? Xol_lang_.Key_en : lang_itm.Key(); + } +} diff --git a/400_xowa/src/gplx/xowa/Xoa_app_fxt.java b/400_xowa/src/gplx/xowa/Xoa_app_fxt.java new file mode 100644 index 000000000..7e7e1a8fc --- /dev/null +++ b/400_xowa/src/gplx/xowa/Xoa_app_fxt.java @@ -0,0 +1,56 @@ +/* +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.xowa; import gplx.*; +import gplx.xowa.apps.*; +public class Xoa_app_fxt { + public static Xoa_app app_() { + Io_mgr._.InitEngine_mem(); + return app_(Io_url_.mem_dir_("mem/xowa/"), "linux"); + } + public static Xoa_app app_(Io_url root_dir, String op_sys) { + Io_url user_dir = root_dir.GenSubDir_nest("user", "test_user"); + Gfo_log_wtr_base._.Log_dir_(user_dir.GenSubDir_nest("tmp", "current")); + Xoa_app app = new Xoa_app(Gfo_usr_dlg_base.test_(), root_dir, user_dir, op_sys); + app.Setup_mgr().Dump_mgr().Data_storage_format_(gplx.ios.Io_stream_.Tid_file); // TEST: set data_storage_format to file, else bldr tests will fails (expects plain text) + GfsCore._.Clear(); // NOTE: must clear + GfsCore._.AddCmd(app, Xoa_app.Invk_app); // NOTE: must add app to GfsCore; app.Gfs_mgr() always adds current app to GfsCore; note this causes old test to leave behind GfsCore for new test + GfsCore._.AddCmd(app, Xoa_app.Invk_xowa); // add alias for app; DATE:2014-06-09 + return app; + } + public static Xow_wiki wiki_tst_(Xoa_app app) {return wiki_(app, "en.wikipedia.org");} + public static Xow_wiki wiki_(Xoa_app app, String key) {return wiki_(app, key, app.Lang_mgr().Lang_en());} + public static Xow_wiki wiki_(Xoa_app app, String key, Xol_lang lang) { + Io_url wiki_dir = app.Fsys_mgr().Wiki_dir().GenSubDir(key); + Xow_wiki rv = new Xow_wiki(app, wiki_dir, Xow_ns_mgr_.default_(), lang); + rv.File_mgr().Meta_mgr().Depth_(2); // TEST: written for 2 depth + rv.Props().Main_page_(Xoa_page_.Main_page_bry); // TEST: default to Main Page (nothing tests loading Main Page from wiki.gfs) + rv.Ns_mgr().Ids_get_or_null(Xow_ns_.Id_main).Subpages_enabled_(true); + app.Wiki_mgr().Add(rv); + return rv; + } + public static void Init_gui(Xoa_app app) { + app.Gui_mgr().Browser_win().Init_by_kit(gplx.gfui.Mem_kit._); + app.Gui_mgr().Browser_win().Tab_mgr().Tabs_new_init(); + } + public static Xob_bldr bldr_(Xoa_app app) { + Xob_bldr rv = new Xob_bldr(app); + rv.Sort_mem_len_(Io_mgr.Len_kb).Dump_fil_len_(Io_mgr.Len_kb).Make_fil_len_(Io_mgr.Len_kb); + return rv; + } + public static final Io_url Root_dir = Op_sys.Cur().Tid_is_lnx() ? Io_url_.new_dir_("/xowa/") : Io_url_.new_dir_("C:\\xowa\\"); +} diff --git a/400_xowa/src/gplx/xowa/Xoa_consts.java b/400_xowa/src/gplx/xowa/Xoa_consts.java new file mode 100644 index 000000000..ec22e42c6 --- /dev/null +++ b/400_xowa/src/gplx/xowa/Xoa_consts.java @@ -0,0 +1,30 @@ +/* +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.xowa; import gplx.*; +public class Xoa_consts { + public static final byte[] + Nl_bry = new byte[] {Byte_ascii.NewLine} + , Slash_bry = new byte[] {Byte_ascii.Slash} + , Pipe_bry = new byte[] {Byte_ascii.Pipe} + , Invk_bgn = new byte[] {Byte_ascii.Curly_bgn, Byte_ascii.Curly_bgn} // "{{" + , Invk_end = new byte[] {Byte_ascii.Curly_end, Byte_ascii.Curly_end} // "}}" + , Transclude_bgn = new byte[] {Byte_ascii.Curly_bgn, Byte_ascii.Curly_bgn, Byte_ascii.Colon} // "{{:" + , Url_relative_prefix = new byte[] {Byte_ascii.Slash, Byte_ascii.Slash} // "//" + , Url_wiki_intermediary = Bry_.new_ascii_("/wiki/") + ; +} diff --git a/400_xowa/src/gplx/xowa/Xoa_test_.java b/400_xowa/src/gplx/xowa/Xoa_test_.java new file mode 100644 index 000000000..7c7876ceb --- /dev/null +++ b/400_xowa/src/gplx/xowa/Xoa_test_.java @@ -0,0 +1,25 @@ +/* +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.xowa; import gplx.*; +public class Xoa_test_ { + public static boolean Db_skip() {return false;} + public static boolean Fsdb_is_mem = false; + public static Io_url Url_root() {return Io_url_.Usr().GenSubDir_nest("xowa", "dev", "tst", "400_xowa");} + public static Io_url Url_wiki_enwiki() {return Url_root().GenSubDir_nest("root", "wiki", "en.wikipedia.org");} + public static Io_url Url_file_enwiki() {return Url_root().GenSubDir_nest("root", "file", "en.wikipedia.org");} +} diff --git a/400_xowa/src/gplx/xowa/apis/Xoapi_root.java b/400_xowa/src/gplx/xowa/apis/Xoapi_root.java new file mode 100644 index 000000000..15e8e3dc6 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/Xoapi_root.java @@ -0,0 +1,60 @@ +/* +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.xowa.apis; import gplx.*; import gplx.xowa.*; +import gplx.xowa.apis.xowa.*; import gplx.xowa.gui.cmds.*; +public class Xoapi_root implements GfoInvkAble { + public Xoa_app app; + public Xoapi_root(Xoa_app app) {usr_api.Ctor_by_app(app);} + public void Init_by_kit(Xoa_app app) { + this.app = app; + app_api.Init_by_kit(app); + nav.Init_by_kit(app); + gui_api.Init_by_kit(app); + html_api.Init_by_kit(app); + net_api.Init_by_kit(app); + usr_api.Init_by_kit(app); + xtns_api.Init_by_kit(app); + } + public Xoapi_app App() {return app_api;} private Xoapi_app app_api = new Xoapi_app(); + public Xoapi_nav Nav() {return nav;} private Xoapi_nav nav = new Xoapi_nav(); + public Xoapi_gui Gui() {return gui_api;} private Xoapi_gui gui_api = new Xoapi_gui(); + public Xoapi_html Html() {return html_api;} private Xoapi_html html_api = new Xoapi_html(); + public Xoapi_net Net() {return net_api;} private Xoapi_net net_api = new Xoapi_net(); + public Xoapi_usr Usr() {return usr_api;} private Xoapi_usr usr_api = new Xoapi_usr(); + public Xoapi_xtns Xtns() {return xtns_api;} private Xoapi_xtns xtns_api = new Xoapi_xtns(); + private void Exec(String key) { + Xog_cmd_itm cmd_itm = app.Gui_mgr().Cmd_mgr().Get_or_null(key); + if (cmd_itm == null) app.Usr_dlg().Warn_many("", "", "could not find cmd; key=~{0}", key); + app.Gfs_mgr().Run_str_for(app, cmd_itm.Cmd()); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_app)) return app_api; + else if (ctx.Match(k, Invk_nav)) return nav; + else if (ctx.Match(k, Invk_gui)) return gui_api; + else if (ctx.Match(k, Invk_html)) return html_api; + else if (ctx.Match(k, Invk_net)) return net_api; + else if (ctx.Match(k, Invk_usr)) return usr_api; + else if (ctx.Match(k, Invk_xtns)) return xtns_api; + else if (ctx.Match(k, Invk_exec)) Exec(m.ReadStr("v")); + return this; + } + private static final String + Invk_exec = "exec" + , Invk_app = "app", Invk_nav = "nav", Invk_gui = "gui", Invk_html = "html", Invk_net = "net", Invk_usr = "usr", Invk_xtns = "xtns" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_app.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_app.java new file mode 100644 index 000000000..a5a08255d --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_app.java @@ -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 . +*/ +package gplx.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +import gplx.xowa.gui.views.*; +public class Xoapi_app implements GfoInvkAble { + private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) { + win = app.Gui_mgr().Browser_win(); + } + public void Exit() {win.App__exit();} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_exit)) this.Exit(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_exit = "exit"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_gui.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_gui.java new file mode 100644 index 000000000..6b07777c9 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_gui.java @@ -0,0 +1,36 @@ +/* +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.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +import gplx.xowa.apis.xowa.gui.*; +public class Xoapi_gui implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + browser.Init_by_kit(app); + font.Init_by_kit(app); + page.Init_by_kit(app); + } + public Xoapi_browser Browser() {return browser;} private Xoapi_browser browser = new Xoapi_browser(); + public Xoapi_font Font() {return font;} private Xoapi_font font = new Xoapi_font(); + public Xoapi_page Page() {return page;} private Xoapi_page page = new Xoapi_page(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_browser)) return browser; + else if (ctx.Match(k, Invk_font)) return font; + else if (ctx.Match(k, Invk_page)) return page; + return this; + } + private static final String Invk_browser = "browser", Invk_font = "font", Invk_page = "page"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_html.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_html.java new file mode 100644 index 000000000..12e1906fa --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_html.java @@ -0,0 +1,33 @@ +/* +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.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +import gplx.xowa.apis.xowa.html.*; +public class Xoapi_html implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + tidy.Init_by_kit(app); + modules.Init_by_kit(app); + } + public Xoapi_tidy Tidy() {return tidy;} private Xoapi_tidy tidy = new Xoapi_tidy(); + public Xoapi_modules Modules() {return modules;} private Xoapi_modules modules = new Xoapi_modules(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_tidy)) return tidy; + else if (ctx.Match(k, Invk_modules)) return modules; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String Invk_tidy = "tidy", Invk_modules = "modules"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_nav.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_nav.java new file mode 100644 index 000000000..3fc5698fa --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_nav.java @@ -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 . +*/ +package gplx.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +import gplx.xowa.gui.views.*; +import gplx.xowa.apis.xowa.navs.*; +public class Xoapi_nav implements GfoInvkAble { + private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) { + win = app.Gui_mgr().Browser_win(); + wiki.Init_by_kit(app); + } + public Xoapi_wiki Wiki() {return wiki;} private Xoapi_wiki wiki = new Xoapi_wiki(); + public void Goto(String page) {win.Page__navigate_by_url_bar(page);} + public void Go_bwd() {win.Page__navigate_by_history(Bool_.N);} + public void Go_fwd() {win.Page__navigate_by_history(Bool_.Y);} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_go_bwd)) this.Go_bwd(); + else if (ctx.Match(k, Invk_go_fwd)) this.Go_fwd(); + else if (ctx.Match(k, Invk_goto)) this.Goto(m.ReadStr("v")); + else if (ctx.Match(k, Invk_wiki)) return wiki; + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_go_bwd = "go_bwd", Invk_go_fwd = "go_fwd", Invk_goto = "goto", Invk_wiki = "wiki"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_net.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_net.java new file mode 100644 index 000000000..548aa6977 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_net.java @@ -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 . +*/ +package gplx.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +public class Xoapi_net implements GfoInvkAble, GfoEvObj { + public Xoapi_net() {this.ev_mgr = GfoEvMgr.new_(this);} + public GfoEvMgr EvMgr() {return ev_mgr;} private GfoEvMgr ev_mgr; + public void Init_by_kit(Xoa_app app) { + } + public boolean Enabled() {return enabled;} private boolean enabled; + public void Enabled_(boolean v) { + this.enabled = v; + gplx.ios.IoEngine_system.Web_access_enabled = v; + GfoEvMgr_.PubVal(this, gplx.xowa.gui.menus.dom.Xog_mnu_evt_mgr.Evt_selected_changed, v); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return this.Enabled(); + else if (ctx.Match(k, Invk_enabled_n_)) this.Enabled_(Bool_.N); + else if (ctx.Match(k, Invk_enabled_y_)) this.Enabled_(Bool_.Y); + else if (ctx.Match(k, Invk_enabled_x_)) this.Enabled_(this.Enabled()); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_enabled = "enabled", Invk_enabled_n_ = "enabled_n_", Invk_enabled_y_ = "enabled_y_", Invk_enabled_x_ = "enabled_x_" + ; + public static final String Evt_enabled_changed = "enabled_changed"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_usr.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_usr.java new file mode 100644 index 000000000..f07ea48d6 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_usr.java @@ -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 . +*/ +package gplx.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +import gplx.xowa.apis.xowa.usrs.*; +public class Xoapi_usr implements GfoInvkAble { + public void Ctor_by_app(Xoa_app app) { + bookmarks.Ctor_by_app(app); + history.Ctor_by_app(app); + logs.Ctor_by_app(app); + } + public void Init_by_kit(Xoa_app app) { + bookmarks.Init_by_kit(app); + history.Init_by_kit(app); + } + public Xoapi_bookmarks Bookmarks() {return bookmarks;} private Xoapi_bookmarks bookmarks = new Xoapi_bookmarks(); + public Xoapi_history History() {return history;} private Xoapi_history history = new Xoapi_history(); + public Xoapi_logs Logs() {return logs;} private Xoapi_logs logs = new Xoapi_logs(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_bookmarks)) return bookmarks; + else if (ctx.Match(k, Invk_history)) return history; + else if (ctx.Match(k, Invk_logs)) return logs; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String Invk_bookmarks = "bookmarks", Invk_history = "history", Invk_logs = "logs"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_xtns.java b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_xtns.java new file mode 100644 index 000000000..728115b74 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/Xoapi_xtns.java @@ -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 . +*/ +package gplx.xowa.apis.xowa; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; +import gplx.xowa.apis.xowa.xtns.*; +public class Xoapi_xtns implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + scribunto.Init_by_kit(app); + } + public Xoapi_scribunto Scribunto() {return scribunto;} private Xoapi_scribunto scribunto = new Xoapi_scribunto(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_scribunto)) return scribunto; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String + Invk_scribunto = "scribunto" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_browser.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_browser.java new file mode 100644 index 000000000..099c2226a --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_browser.java @@ -0,0 +1,53 @@ +/* +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.xowa.apis.xowa.gui; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.apis.xowa.gui.browsers.*; +public class Xoapi_browser implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + url.Init_by_kit(app); + search.Init_by_kit(app); + tabs.Init_by_kit(app); + html.Init_by_kit(app); + find.Init_by_kit(app); + prog.Init_by_kit(app); + info.Init_by_kit(app); + prog_log.Init_by_kit(app); + } + public Xoapi_url Url() {return url;} private Xoapi_url url = new Xoapi_url(); + public Xoapi_search Search() {return search;} private Xoapi_search search = new Xoapi_search(); + public Xoapi_tabs Tabs() {return tabs;} private Xoapi_tabs tabs = new Xoapi_tabs(); + public Xoapi_html_box Html() {return html;} private Xoapi_html_box html = new Xoapi_html_box(); + public Xoapi_find Find() {return find;} private Xoapi_find find = new Xoapi_find(); + public Xoapi_prog Prog() {return prog;} private Xoapi_prog prog = new Xoapi_prog(); + public Xoapi_info Info() {return info;} private Xoapi_info info = new Xoapi_info(); + public Xoapi_prog_log Prog_log() {return prog_log;} private Xoapi_prog_log prog_log = new Xoapi_prog_log(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_url)) return url; + else if (ctx.Match(k, Invk_search)) return search; + else if (ctx.Match(k, Invk_tabs)) return tabs; + else if (ctx.Match(k, Invk_html)) return html; + else if (ctx.Match(k, Invk_find)) return find; + else if (ctx.Match(k, Invk_prog)) return prog; + else if (ctx.Match(k, Invk_info)) return info; + else if (ctx.Match(k, Invk_prog_log)) return prog_log; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String + Invk_url = "url", Invk_search = "search", Invk_tabs = "tabs", Invk_html = "html" + , Invk_find = "find", Invk_prog = "prog", Invk_info = "info", Invk_prog_log = "prog_log"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_font.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_font.java new file mode 100644 index 000000000..e3421bedd --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_font.java @@ -0,0 +1,55 @@ +/* +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.xowa.apis.xowa.gui; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.cfgs.gui.*; import gplx.xowa.html.*; +public class Xoapi_font implements GfoInvkAble { + private Xoa_app app; + public void Init_by_kit(Xoa_app app) { + this.app = app; + } + public void Increase() {Adj(1);} + public void Decrease() {Adj(-1);} + public void Reset() {Set(false, Xoh_page_mgr.Font_size_default, Xocfg_win.Font_size_default);} + private void Adj(int adj) { + float html_font_size = app.Html_mgr().Page_mgr().Font_size() + adj; + float gui_font_size = app.Gui_mgr().Win_cfg().Font().Size() + adj; // (html_font_size * .75f) - 4; // .75f b/c 16px = 12 pt; -4 b/c gui font is currently 4 pt smaller + if (html_font_size < 1 || gui_font_size < 1) return; + Set(true, html_font_size, gui_font_size); + } + private void Set(boolean enabled, float html_font_size, float gui_font_size) { + if (html_font_size <= 0) return; // font must be positive + app.Html_mgr().Page_mgr().Font_enabled_(enabled); + app.Html_mgr().Page_mgr().Font_size_(html_font_size); + app.Cfg_mgr().Set_by_app("app.html.page.font_enabled", "y"); + app.Cfg_mgr().Set_by_app("app.html.page.font_size", Float_.XtoStr(app.Html_mgr().Page_mgr().Font_size())); + app.Gui_mgr().Win_cfg().Font().Size_(gui_font_size); + app.Cfg_mgr().Set_by_app("app.gui.win_opts.font.size", Float_.XtoStr(gui_font_size)); + app.Cfg_mgr().Db_save_txt(); + app.Gui_mgr().Browser_win().Page__reload(); // NOTE: force reload; needed if viewing Help:Options/HTML, else Font size won't update + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_increase)) this.Increase(); + else if (ctx.Match(k, Invk_decrease)) this.Decrease(); + else if (ctx.Match(k, Invk_reset)) this.Reset(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_increase = "increase", Invk_decrease = "decrease", Invk_reset = "reset" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_page.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_page.java new file mode 100644 index 000000000..2126e0ba7 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/Xoapi_page.java @@ -0,0 +1,36 @@ +/* +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.xowa.apis.xowa.gui; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.apis.xowa.gui.pages.*; +public class Xoapi_page implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + view.Init_by_kit(app); + selection.Init_by_kit(app); + edit.Init_by_kit(app); + } + public Xoapi_view View() {return view;} private Xoapi_view view = new Xoapi_view(); + public Xoapi_edit Edit() {return edit;} private Xoapi_edit edit = new Xoapi_edit(); + public Xoapi_selection Selection() {return selection;} private Xoapi_selection selection = new Xoapi_selection(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_view)) return view; + else if (ctx.Match(k, Invk_selection)) return selection; + else if (ctx.Match(k, Invk_edit)) return edit; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String Invk_view = "view", Invk_selection = "selection", Invk_edit = "edit"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_find.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_find.java new file mode 100644 index 000000000..9b3d16bbc --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_find.java @@ -0,0 +1,80 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.*; import gplx.xowa.gui.views.*; +public class Xoapi_find implements GfoInvkAble { + private Xog_find_box find_box; + public void Init_by_kit(Xoa_app app) { + find_box = new Xog_find_box(app); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_show)) find_box.Show(); + else if (ctx.Match(k, Invk_show_by_paste)) find_box.Show_by_paste(); + else if (ctx.Match(k, Invk_exec)) find_box.Exec(); + else if (ctx.Match(k, Invk_type)) find_box.Exec(); // NOTE: same as exec; provided so that exec doesn't accidentally overwrite auto-type for find + else if (ctx.Match(k, Invk_find_fwd)) find_box.Exec_by_dir(Bool_.Y); + else if (ctx.Match(k, Invk_find_bwd)) find_box.Exec_by_dir(Bool_.N); + else if (ctx.Match(k, Invk_case_toggle)) find_box.Case_toggle(); + else if (ctx.Match(k, Invk_wrap_toggle)) find_box.Wrap_toggle(); + else if (ctx.Match(k, Invk_hide)) find_box.Hide(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_show = "show", Invk_show_by_paste = "show_by_paste", Invk_hide = "hide", Invk_exec = "exec", Invk_type = "type" + , Invk_find_bwd = "find_bwd", Invk_find_fwd = "find_fwd", Invk_case_toggle = "case_toggle", Invk_wrap_toggle = "wrap_toggle"; +} +class Xog_find_box { + private Xoa_app app; private Xog_win_itm win; private GfuiTextBox find_box; + private boolean dir_fwd = true, case_match = false, wrap_search = true; + public Xog_find_box(Xoa_app app) { + this.app = app; + this.win = app.Gui_mgr().Browser_win(); + this.find_box = win.Find_box(); + } + public void Show() {app.Gui_mgr().Layout().Find_show();} + public void Hide() {app.Gui_mgr().Layout().Find_close();} + public void Show_by_paste() { + this.Show(); + if (win.Tab_mgr().Active_tab_is_null()) return; // if no active_tab, just show box; don't try to copy what's on tab; + find_box.Text_(win.Active_html_itm().Html_selected_get_text_or_href()); + } + public void Exec_by_dir(boolean fwd) { + boolean changed = dir_fwd != fwd; + dir_fwd = fwd; + Exec(); + if (changed) // clicking on buttons calls Exec_by_dir; in case of repeated clicks on same button, don't flash changed message again + win.Usr_dlg().Prog_direct("Find direction changed to " + (fwd ? "forward" : "backward")); + } + public void Exec() { + Xog_tab_itm tab = win.Tab_mgr().Active_tab(); if (tab == Xog_tab_itm_.Null) return; + String elem_id = tab.View_mode() == Xog_page_mode.Tid_read + ? Gfui_html.Elem_id_body + : Xog_html_itm.Elem_id__xowa_edit_data_box + ; + tab.Html_box().Html_doc_find(elem_id, find_box.Text(), dir_fwd, case_match, wrap_search); + win.Usr_dlg().Prog_direct(""); + } + public void Case_toggle() { + case_match = !case_match; + win.Usr_dlg().Prog_direct("Case match " + (case_match ? "enabled" : "disabled")); + } + public void Wrap_toggle() { + wrap_search = !wrap_search; + win.Usr_dlg().Prog_direct("Wrap search " + (wrap_search ? "enabled" : "disabled")); + } +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_html_box.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_html_box.java new file mode 100644 index 000000000..30866d09e --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_html_box.java @@ -0,0 +1,36 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.*; import gplx.xowa.gui.views.*; +public class Xoapi_html_box implements GfoInvkAble { + private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) {this.win = app.Gui_mgr().Browser_win();} + public void Focus() { + Xog_tab_itm tab = win.Active_tab(); if (tab == Xog_tab_itm_.Null) return; + Gfui_html html_box = tab.Html_itm().Html_box(); + html_box.Focus(); + if (tab.View_mode() != Xog_page_mode.Tid_read) // if edit / html, place focus in edit box + html_box.Html_elem_focus(Xog_html_itm.Elem_id__xowa_edit_data_box); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_focus)) this.Focus(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_focus = "focus"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_info.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_info.java new file mode 100644 index 000000000..d8c2b299a --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_info.java @@ -0,0 +1,37 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.views.*; +public class Xoapi_info implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) {this.app = app;} private Xoa_app app; + private Xog_win_itm Win() {return app.Gui_mgr().Browser_win();} + public void Focus() {this.Win().Info_box().Focus();} + public void Clear() {app.Usr_dlg().Ui_wkr().Clear();} + public void Launch() { + Io_url session_fil = app.Log_wtr().Session_fil(); + app.Fsys_mgr().App_mgr().App_view_text().Run(session_fil); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_focus)) this.Focus(); + else if (ctx.Match(k, Invk_clear)) this.Clear(); + else if (ctx.Match(k, Invk_launch)) this.Launch(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_focus = "focus", Invk_clear = "clear", Invk_launch = "launch"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_prog.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_prog.java new file mode 100644 index 000000000..75e72f0c6 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_prog.java @@ -0,0 +1,30 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.views.*; +public class Xoapi_prog implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) {this.app = app;} private Xoa_app app; + private Xog_win_itm Win() {return app.Gui_mgr().Browser_win();} + public void Focus() {this.Win().Prog_box().Focus();} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_focus)) this.Focus(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_focus = "focus"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_prog_log.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_prog_log.java new file mode 100644 index 000000000..418d81f5a --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_prog_log.java @@ -0,0 +1,29 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.views.*; +public class Xoapi_prog_log implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) {this.app = app;} private Xoa_app app; + public void Show() {app.Gui_mgr().Show_prog();} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_show)) this.Show(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_show = "show"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_search.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_search.java new file mode 100644 index 000000000..d021575a6 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_search.java @@ -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 . +*/ +package gplx.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.views.*; +public class Xoapi_search implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) {this.app = app;} private Xoa_app app; + private GfuiTextBox Search_box() {return app.Gui_mgr().Browser_win().Search_box();} + private Xog_win_itm Win() {return app.Gui_mgr().Browser_win();} + public void Focus() {this.Search_box().Focus_select_all();} + public void Exec() {this.Win().Page__navigate_by_search();} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_focus)) this.Focus(); + else if (ctx.Match(k, Invk_exec)) this.Exec(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_focus = "focus"; + public static final String Invk_exec = "exec"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_tabs.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_tabs.java new file mode 100644 index 000000000..811d13400 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_tabs.java @@ -0,0 +1,90 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.xowa.gui.views.*; +public class Xoapi_tabs implements GfoInvkAble { + private Xog_tab_mgr tab_mgr; + public void Init_by_kit(Xoa_app app) { + this.tab_mgr = app.Gui_mgr().Browser_win().Tab_mgr(); + } + private boolean Active_tab_is_null() {return tab_mgr.Active_tab_is_null();} + public void New_dupe(boolean focus) {tab_mgr.Tabs_new_dupe(focus);} + public void New_dflt(boolean focus) {tab_mgr.Tabs_new_dflt(focus);} + public void New_link(boolean focus, String link) {tab_mgr.Tabs_new_link(focus, link);} + public void New_href(boolean focus) { + Xog_tab_itm tab = tab_mgr.Active_tab(); if (tab == Xog_tab_itm_.Null) return; + String link = tab.Html_itm().Html_selected_get_href_or_text(); + if (String_.Len_eq_0(link)) {tab_mgr.Win().Usr_dlg().Prog_many("", "", "no link or text selected"); return;} + tab_mgr.Tabs_new_dflt(true); + tab_mgr.Win().Page__navigate_by_url_bar(link); + } + public void Close_cur() {tab_mgr.Tabs_close_cur();} + public void Select_bwd() {tab_mgr.Tabs_select(Bool_.N);} + public void Select_fwd() {tab_mgr.Tabs_select(Bool_.Y);} + public void Select_by_idx(int v) {tab_mgr.Tabs_select_by_idx(v - ListAdp_.Base1);} + public void Move_bwd() {tab_mgr.Tabs_move(Bool_.N);} + public void Move_fwd() {tab_mgr.Tabs_move(Bool_.Y);} + public void Close_others() {tab_mgr.Tabs_close_others();} + public void Close_to_bgn() {tab_mgr.Tabs_close_to_bgn();} + public void Close_to_end() {tab_mgr.Tabs_close_to_end();} + public void Close_undo() {tab_mgr.Tabs_close_undo();} + public void Pin_toggle() {if (this.Active_tab_is_null()) return; tab_mgr.Active_tab().Pin_toggle();} + public void Tab_bookmark_all() {}//app.Gui_mgr().Browser_win().Tab_mgr().Tabs_close_others();} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_new_dflt__at_dflt__focus_y)) this.New_dflt(Bool_.Y); + else if (ctx.Match(k, Invk_new_link__at_dflt__focus_n)) this.New_link(Bool_.N, m.ReadStrOr("v", null)); + else if (ctx.Match(k, Invk_new_link__at_dflt__focus_y)) this.New_link(Bool_.Y, m.ReadStrOr("v", null)); + else if (ctx.Match(k, Invk_new_href__at_dflt__focus_y)) this.New_href(Bool_.Y); + else if (ctx.Match(k, Invk_new_dupe__at_dflt__focus_y)) this.New_dupe(Bool_.Y); + else if (ctx.Match(k, Invk_close_cur)) this.Close_cur(); + else if (ctx.Match(k, Invk_select_bwd)) this.Select_bwd(); + else if (ctx.Match(k, Invk_select_fwd)) this.Select_fwd(); + else if (ctx.Match(k, Invk_move_bwd)) this.Move_bwd(); + else if (ctx.Match(k, Invk_move_fwd)) this.Move_fwd(); + else if (ctx.Match(k, Invk_close_others)) this.Close_others(); + else if (ctx.Match(k, Invk_close_to_bgn)) this.Close_to_bgn(); + else if (ctx.Match(k, Invk_close_to_end)) this.Close_to_end(); + else if (ctx.Match(k, Invk_close_undo)) this.Close_undo(); + else if (ctx.Match(k, Invk_pin_toggle)) this.Pin_toggle(); + else if (ctx.Match(k, Invk_select_by_idx_1)) this.Select_by_idx(1); + else if (ctx.Match(k, Invk_select_by_idx_2)) this.Select_by_idx(2); + else if (ctx.Match(k, Invk_select_by_idx_3)) this.Select_by_idx(3); + else if (ctx.Match(k, Invk_select_by_idx_4)) this.Select_by_idx(4); + else if (ctx.Match(k, Invk_select_by_idx_5)) this.Select_by_idx(5); + else if (ctx.Match(k, Invk_select_by_idx_6)) this.Select_by_idx(6); + else if (ctx.Match(k, Invk_select_by_idx_7)) this.Select_by_idx(7); + else if (ctx.Match(k, Invk_select_by_idx_8)) this.Select_by_idx(8); + else if (ctx.Match(k, Invk_select_by_idx_9)) this.Select_by_idx(9); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_new_dflt__at_dflt__focus_y = "new_dflt__at_dflt__focus_y" + , Invk_new_link__at_dflt__focus_n = "new_link__at_dflt__focus_n" + , Invk_new_link__at_dflt__focus_y = "new_link__at_dflt__focus_y" + , Invk_new_href__at_dflt__focus_y = "new_href__at_dflt__focus_y" + , Invk_new_dupe__at_dflt__focus_y = "new_dupe__at_dflt__focus_y" + , Invk_close_cur = "close_cur" + , Invk_select_bwd = "select_bwd", Invk_select_fwd = "select_fwd" + , Invk_move_bwd = "move_bwd", Invk_move_fwd = "move_fwd" + , Invk_close_others = "close_others", Invk_close_to_bgn = "close_to_bgn", Invk_close_to_end = "close_to_end", Invk_close_undo = "close_undo" + , Invk_pin_toggle = "pin_toggle" + , Invk_select_by_idx_1 = "select_by_idx_1", Invk_select_by_idx_2 = "select_by_idx_2", Invk_select_by_idx_3 = "select_by_idx_3", Invk_select_by_idx_4 = "select_by_idx_4", Invk_select_by_idx_5 = "select_by_idx_5" + , Invk_select_by_idx_6 = "select_by_idx_6", Invk_select_by_idx_7 = "select_by_idx_7", Invk_select_by_idx_8 = "select_by_idx_8", Invk_select_by_idx_9 = "select_by_idx_9" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_url.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_url.java new file mode 100644 index 000000000..957ed9534 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/browsers/Xoapi_url.java @@ -0,0 +1,57 @@ +/* +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.xowa.apis.xowa.gui.browsers; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.views.*; +public class Xoapi_url implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) {this.app = app;} private Xoa_app app; + private GfuiTextBox Url_box() {return app.Gui_mgr().Browser_win().Url_box();} + public void Focus() {this.Url_box().Focus_select_all();} + public void Exec() {Exec_wkr(Bool_.N, this.Url_box().Text());} + public void Exec_by_paste() {Exec_wkr(Bool_.N, ClipboardAdp_.GetText());} + public void Exec_new_tab_by_paste() {Exec_wkr(Bool_.Y, ClipboardAdp_.GetText());} + private void Exec_wkr(boolean new_tab, String urls_text) { + if (Op_sys.Cur().Tid_is_wnt()) + urls_text = String_.Replace(urls_text, Op_sys.Wnt.Nl_str(), Op_sys.Lnx.Nl_str()); + String[] urls = String_.Split(String_.Trim(urls_text), Op_sys.Lnx.Nl_str()); + int urls_len = urls.length; + if (urls_len == 0) return; + if (urls_len == 1) { + String url = urls[0]; + this.Url_box().Text_(url); + app.Gui_mgr().Browser_win().Page__navigate_by_url_bar(url); + } + else { + for (int i = 0; i < urls_len; i++) { + String url = urls[i]; + if (String_.HasAtBgn(url, "\"") && String_.HasAtBgn(url, "\"")) + url = String_.Mid(url, 1, String_.Len(url) - 1); + app.Gui_mgr().Browser_win().Tab_mgr().Tabs_new_link(url, false); + } + } + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_focus)) this.Focus(); + else if (ctx.Match(k, Invk_exec)) this.Exec(); + else if (ctx.Match(k, Invk_exec_by_paste)) this.Exec_by_paste(); + else if (ctx.Match(k, Invk_exec_new_tab_by_paste)) this.Exec_new_tab_by_paste(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_focus = "focus", Invk_exec_by_paste = "exec_by_paste", Invk_exec_new_tab_by_paste = "exec_new_tab_by_paste"; + public static final String Invk_exec = "exec"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_edit.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_edit.java new file mode 100644 index 000000000..827c00b1d --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_edit.java @@ -0,0 +1,56 @@ +/* +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.xowa.apis.xowa.gui.pages; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.xowa.gui.*; import gplx.xowa.gui.views.*; +public class Xoapi_edit implements GfoInvkAble { + private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) { + win = app.Gui_mgr().Browser_win(); + } + private boolean Active_tab_is_null() {return win.Tab_mgr().Active_tab_is_null();} + public void Copy() {if (Active_tab_is_null()) return; win.Kit().Clipboard().Copy(win.Active_html_itm().Html_selected_get_text_or_href());} + public void Select_all() {if (Active_tab_is_null()) return; GfoInvkAble_.InvkCmd(win.Win_box().Kit().Clipboard(), gplx.gfui.Gfui_clipboard_.Invk_select_all);} + public void Save() {if (Active_tab_is_null()) return; Xog_tab_itm_edit_mgr.Save(win.Active_tab(), false);} + public void Save_draft() {if (Active_tab_is_null()) return; Xog_tab_itm_edit_mgr.Save(win.Active_tab(), true);} + public void Preview() {if (Active_tab_is_null()) return; Xog_tab_itm_edit_mgr.Preview(win.Active_tab());} + public void Dbg_tmpl() {if (Active_tab_is_null()) return; Xog_tab_itm_edit_mgr.Debug(win, Xog_page_mode.Tid_edit);} + public void Dbg_html() {if (Active_tab_is_null()) return; Xog_tab_itm_edit_mgr.Debug(win, Xog_page_mode.Tid_html);} + public void Focus_edit_box(){if (Active_tab_is_null()) return; Xog_tab_itm_edit_mgr.Focus(win, Xog_html_itm.Elem_id__xowa_edit_data_box);} + public void Exec() { + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_copy)) this.Copy(); + else if (ctx.Match(k, Invk_select_all)) this.Select_all(); + else if (ctx.Match(k, Invk_save)) this.Save(); + else if (ctx.Match(k, Invk_save_draft)) this.Save_draft(); + else if (ctx.Match(k, Invk_preview)) this.Preview(); + else if (ctx.Match(k, Invk_focus_edit_box)) this.Focus_edit_box(); + else if (ctx.Match(k, Invk_dbg_tmpl)) this.Dbg_tmpl(); + else if (ctx.Match(k, Invk_dbg_html)) this.Dbg_html(); + else if (ctx.Match(k, Invk_exec)) this.Exec(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_copy = "copy" + , Invk_select_all = "select_all" + , Invk_save = "save", Invk_save_draft = "save_draft", Invk_preview = "preview" + , Invk_focus_edit_box = "focus_edit_box" + , Invk_dbg_tmpl = "dbg_tmpl", Invk_dbg_html = "dbg_html", Invk_exec = "exec" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_selection.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_selection.java new file mode 100644 index 000000000..37efb5dd8 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_selection.java @@ -0,0 +1,52 @@ +/* +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.xowa.apis.xowa.gui.pages; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.*; import gplx.xowa.gui.views.*; +public class Xoapi_selection implements GfoInvkAble { + private Xoa_app app; private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) { + this.app = app; + win = app.Gui_mgr().Browser_win(); + } + private boolean Active_tab_is_null() {return win.Tab_mgr().Active_tab_is_null();} + public void Copy() {if (Active_tab_is_null()) return; win.Kit().Clipboard().Copy(win.Active_html_itm().Html_selected_get_text_or_href());} + public void Select_all() {if (Active_tab_is_null()) return; GfoInvkAble_.InvkCmd(win.Win_box().Kit().Clipboard(), gplx.gfui.Gfui_clipboard_.Invk_select_all);} + public void Save_file_as() { + if (this.Active_tab_is_null()) return; + Xog_html_itm html_itm = win.Tab_mgr().Active_tab().Html_itm(); + String src = html_itm.Html_selected_get_src_or_empty(); + if (String_.Len_eq_0(src)) {app.Usr_dlg().Prog_many("", "", "no file selected: tab=~{0}", html_itm.Owner_tab().Page().Url().X_to_full_str()); return;} + Io_url src_url = Io_url_.http_any_(src, Op_sys.Cur().Tid_is_wnt()); + String trg_name = src_url.NameAndExt(); + if (String_.Has(src, "/thumb/")) trg_name = src_url.OwnerDir().NameOnly(); + String trg = app.Gui_mgr().Kit().New_dlg_file(Gfui_kit_.File_dlg_type_save, "Select file to save to:").Init_file_(trg_name).Ask(); + if (String_.Len_eq_0(trg)) return; + Io_url trg_url = Io_url_.new_fil_(trg); + Io_mgr._.CopyFil(src_url, trg_url, true); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_copy)) this.Copy(); + else if (ctx.Match(k, Invk_select_all)) this.Select_all(); + else if (ctx.Match(k, Invk_save_file_as)) this.Save_file_as(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_copy = "copy", Invk_select_all = "select_all", Invk_save_file_as = "save_file_as" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_view.java b/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_view.java new file mode 100644 index 000000000..3f6331185 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/gui/pages/Xoapi_view.java @@ -0,0 +1,61 @@ +/* +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.xowa.apis.xowa.gui.pages; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.gui.*; +import gplx.gfui.*; import gplx.xowa.gui.*; import gplx.xowa.gui.views.*; +public class Xoapi_view implements GfoInvkAble { + private Xoa_app app; private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) { + this.app = app; this.win = app.Gui_mgr().Browser_win(); + } + private boolean Active_tab_is_null() {return win.Tab_mgr().Active_tab_is_null();} + public void Mode_read() {Mode(Xog_page_mode.Tid_read);} + public void Mode_edit() {Mode(Xog_page_mode.Tid_edit);} + public void Mode_html() {Mode(Xog_page_mode.Tid_html);} + private void Mode(byte v) {if (Active_tab_is_null()) return; win.Page__mode_(v);} + public void Reload() {if (Active_tab_is_null()) return; win.Page__reload();} + public void Refresh() {if (Active_tab_is_null()) return; win.Page__refresh();} + public void Print() { + if (this.Active_tab_is_null()) return; + win.Active_html_box().Html_window_print_preview(); + } + public void Save_as() { + if (this.Active_tab_is_null()) return; + Xog_tab_itm tab = win.Tab_mgr().Active_tab(); + String file_name = app.Url_converter_fsys_safe().Encode_str(String_.new_utf8_(tab.Page().Ttl().Full_url())) + ".html"; + String file_url = app.Gui_mgr().Kit().New_dlg_file(Gfui_kit_.File_dlg_type_save, "Select file to save to:").Init_file_(file_name).Ask(); + if (String_.Len_eq_0(file_url)) return; + Io_mgr._.SaveFilStr(file_url, tab.Html_box().Text()); + app.Usr_dlg().Prog_many("", "", "saved page: file=~{0}", file_url); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_mode_read)) this.Mode_read(); + else if (ctx.Match(k, Invk_mode_edit)) this.Mode_edit(); + else if (ctx.Match(k, Invk_mode_html)) this.Mode_html(); + else if (ctx.Match(k, Invk_reload)) this.Reload(); + else if (ctx.Match(k, Invk_refresh)) this.Refresh(); + else if (ctx.Match(k, Invk_print)) this.Print(); + else if (ctx.Match(k, Invk_save_as)) this.Save_as(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_mode_read = "mode_read", Invk_mode_edit = "mode_edit", Invk_mode_html = "mode_html" + , Invk_reload = "reload", Invk_refresh = "refresh" + , Invk_print = "print", Invk_save_as = "save_as" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/html/Xoapi_modules.java b/400_xowa/src/gplx/xowa/apis/xowa/html/Xoapi_modules.java new file mode 100644 index 000000000..2b9d8dfeb --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/html/Xoapi_modules.java @@ -0,0 +1,36 @@ +/* +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.xowa.apis.xowa.html; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.apis.xowa.html.modules.*; +public class Xoapi_modules implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + popups.Init_by_app(app); + } + public Xoapi_collapsible Collapsible() {return collapsible;} private Xoapi_collapsible collapsible = new Xoapi_collapsible(); + public Xoapi_navframe Navframe() {return navframe;} private Xoapi_navframe navframe = new Xoapi_navframe(); + public Xoapi_toc Toc() {return toc;} private Xoapi_toc toc = new Xoapi_toc(); + public Xoapi_popups Popups() {return popups;} private Xoapi_popups popups = new Xoapi_popups(); + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_collapsible)) return collapsible; + else if (ctx.Match(k, Invk_navframe)) return navframe; + else if (ctx.Match(k, Invk_toc)) return toc; + else if (ctx.Match(k, Invk_popups)) return popups; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String Invk_collapsible = "collapsible", Invk_navframe = "navframe", Invk_toc = "toc", Invk_popups = "popups"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/html/Xoapi_tidy.java b/400_xowa/src/gplx/xowa/apis/xowa/html/Xoapi_tidy.java new file mode 100644 index 000000000..d9ee5e475 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/html/Xoapi_tidy.java @@ -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 . +*/ +package gplx.xowa.apis.xowa.html; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.html.tidy.*; +public class Xoapi_tidy implements GfoInvkAble { + private Xoa_app app; + public void Init_by_kit(Xoa_app app) { + this.app = app; + } + public void Toggle() { + app.Html_mgr().Tidy_mgr().Enabled_toggle(); + app.Gui_mgr().Browser_win().Page__refresh(); + } + public void Engine_(byte v) { + app.Html_mgr().Tidy_mgr().Wkr_tid_(v); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_toggle)) this.Toggle(); + else if (ctx.Match(k, Invk_engine_tidy_)) Engine_(Xoh_tidy_wkr_.Tid_tidy); + else if (ctx.Match(k, Invk_engine_jtidy_)) Engine_(Xoh_tidy_wkr_.Tid_jtidy); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_toggle = "toggle", Invk_engine_tidy_ = "engine_tidy_", Invk_engine_jtidy_ = "engine_jtidy_"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_collapsible.java b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_collapsible.java new file mode 100644 index 000000000..649c7f7b3 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_collapsible.java @@ -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 . +*/ +package gplx.xowa.apis.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.html.*; +public class Xoapi_collapsible implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + } + public boolean Collapsed() {return collapsed;} private boolean collapsed; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_collapsed)) return Yn.X_to_str(collapsed); + else if (ctx.Match(k, Invk_collapsed_)) collapsed = m.ReadYn("v"); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_collapsed = "collapsed", Invk_collapsed_ = "collapsed_" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_navframe.java b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_navframe.java new file mode 100644 index 000000000..afcf04968 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_navframe.java @@ -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 . +*/ +package gplx.xowa.apis.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.html.*; +public class Xoapi_navframe implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + } + public boolean Collapsed() {return collapsed;} private boolean collapsed; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_collapsed)) return Yn.X_to_str(collapsed); + else if (ctx.Match(k, Invk_collapsed_)) collapsed = m.ReadYn("v"); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_collapsed = "collapsed", Invk_collapsed_ = "collapsed_" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_popups.java b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_popups.java new file mode 100644 index 000000000..418a81e2d --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_popups.java @@ -0,0 +1,194 @@ +/* +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.xowa.apis.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.html.*; +import gplx.xowa.html.modules.popups.*; +public class Xoapi_popups implements GfoInvkAble, GfoEvMgrOwner { + private Xoa_app app; + public Xoapi_popups() { + evMgr = GfoEvMgr.new_(this); + } + public GfoEvMgr EvMgr() {return evMgr;} private GfoEvMgr evMgr; + public void Init_by_app(Xoa_app app) {this.app = app;} + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled = false; + public int Show_init_word_count() {return show_init_word_count;} private int show_init_word_count = Dflt_show_init_word_count; + public int Show_more_word_count() {return show_more_word_count;} private int show_more_word_count = Dflt_show_more_word_count; + public int Show_all_if_less_than() {return show_all_if_less_than;} public void Show_all_if_less_than_(int v) {show_all_if_less_than = v;} private int show_all_if_less_than = Dflt_show_all_if_less_than; + public byte[] Html_fmt() {return html_fmt;} private byte[] html_fmt = Dflt_html_fmt; + public byte[] Html_fmt_dflt() {return html_fmt_dflt;} private byte[] html_fmt_dflt = Dflt_html_fmt; + public int Win_show_delay() {return win_show_delay;} private int win_show_delay = Dflt_win_show_delay; + public int Win_hide_delay() {return win_hide_delay;} private int win_hide_delay = Dflt_win_hide_delay; + public int Win_max_w() {return win_max_w;} private int win_max_w = Dflt_win_max_w; + public int Win_max_h() {return win_max_h;} private int win_max_h = Dflt_win_max_h; + public int Win_show_all_max_w() {return win_show_all_max_w;} private int win_show_all_max_w = Dflt_win_show_all_max_w; + public boolean Win_bind_focus_blur() {return win_bind_focus_blur;} private boolean win_bind_focus_blur = Dflt_win_bind_focus_blur; + public byte[] Xnde_ignore_ids() {return xnde_ignore_ids;} private byte[] xnde_ignore_ids = Dflt_coordinates; + public int Scan_len() {return scan_len;} private int scan_len = Dflt_scan_len; + public int Scan_max() {return scan_max;} private int scan_max = Dflt_scan_max; + public byte[] Ns_allowed() {return ns_allowed;} private byte[] ns_allowed = Dflt_ns_allowed; + public int Read_til_stop_fwd() {return read_til_stop_fwd;} private int read_til_stop_fwd = Dflt_read_til_stop_fwd; + public int Read_til_stop_bwd() {return read_til_stop_bwd;} private int read_til_stop_bwd = Dflt_read_til_stop_bwd; + public int Tmpl_tkn_max() {return tmpl_tkn_max;} private int tmpl_tkn_max = Dflt_tmpl_tkn_max; + public void Show_more(String popup_id) { + Xow_wiki wiki = app.Gui_mgr().Browser_win().Active_tab().Page().Wiki(); + wiki.Html_mgr().Module_mgr().Popup_mgr().Show_more(popup_id); + } + public void Show_all(String popup_id) { + Xow_wiki wiki = app.Gui_mgr().Browser_win().Active_tab().Page().Wiki(); + wiki.Html_mgr().Module_mgr().Popup_mgr().Show_all(popup_id); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return Yn.X_to_str(enabled); + else if (ctx.Match(k, Invk_enabled_)) enabled = m.ReadYn("v"); + else if (ctx.Match(k, Invk_show_more)) Show_more(m.ReadStr("popup_id")); + else if (ctx.Match(k, Invk_show_all)) Show_all (m.ReadStr("popup_id")); + else if (ctx.Match(k, Invk_show_init_word_count)) return show_init_word_count; + else if (ctx.Match(k, Invk_show_init_word_count_)) {show_init_word_count = Set_int_gt_0(m, show_init_word_count, Evt_show_init_word_count_changed);} + else if (ctx.Match(k, Invk_show_more_word_count)) return show_more_word_count; + else if (ctx.Match(k, Invk_show_more_word_count_)) {show_more_word_count = Set_int_gt_0(m, show_more_word_count, Evt_show_more_word_count_changed);} + else if (ctx.Match(k, Invk_show_all_if_less_than)) return show_all_if_less_than; + else if (ctx.Match(k, Invk_show_all_if_less_than_)) {show_all_if_less_than = Set_int(m, show_all_if_less_than, Evt_show_all_if_less_than_changed);} + else if (ctx.Match(k, Invk_win_show_delay)) return win_show_delay; + else if (ctx.Match(k, Invk_win_show_delay_)) {win_show_delay = Set_int(m, win_show_delay, Evt_win_show_delay_changed);} + else if (ctx.Match(k, Invk_win_hide_delay)) return win_hide_delay; + else if (ctx.Match(k, Invk_win_hide_delay_)) {win_hide_delay = Set_int(m, win_hide_delay, Evt_win_hide_delay_changed);} + else if (ctx.Match(k, Invk_win_max_w)) return win_max_w; + else if (ctx.Match(k, Invk_win_max_w_)) {win_max_w = Set_int(m, win_max_w, Evt_win_max_w_changed);} + else if (ctx.Match(k, Invk_win_max_h)) return win_max_h; + else if (ctx.Match(k, Invk_win_max_h_)) {win_max_h = Set_int(m, win_max_h, Evt_win_max_h_changed);} + else if (ctx.Match(k, Invk_win_show_all_max_w)) return win_show_all_max_w; + else if (ctx.Match(k, Invk_win_show_all_max_w_)) {win_show_all_max_w = m.ReadInt("v");} + else if (ctx.Match(k, Invk_win_bind_focus_blur)) return Yn.X_to_str(win_bind_focus_blur); + else if (ctx.Match(k, Invk_win_bind_focus_blur_)) win_bind_focus_blur = m.ReadYn("v"); + else if (ctx.Match(k, Invk_xnde_ignore_ids)) return String_.new_utf8_(xnde_ignore_ids); + else if (ctx.Match(k, Invk_xnde_ignore_ids_)) {xnde_ignore_ids = m.ReadBry("v"); GfoEvMgr_.PubVal(this, Evt_xnde_ignore_ids_changed, xnde_ignore_ids);} + else if (ctx.Match(k, Invk_scan_len)) return scan_len; + else if (ctx.Match(k, Invk_scan_len_)) {scan_len = Set_int_gt_0(m, scan_len, Evt_scan_len_changed);} + else if (ctx.Match(k, Invk_scan_max)) return scan_max; + else if (ctx.Match(k, Invk_scan_max_)) {scan_max = Set_int_gt_0(m, scan_max, Evt_scan_max_changed);} + else if (ctx.Match(k, Invk_html_fmt)) return String_.new_utf8_(html_fmt); + else if (ctx.Match(k, Invk_html_fmt_)) {html_fmt = m.ReadBry("v"); GfoEvMgr_.PubVal(this, Evt_html_fmt_changed, html_fmt);} + else if (ctx.Match(k, Invk_html_fmt_dflt)) return String_.new_utf8_(html_fmt_dflt); + else if (ctx.Match(k, Invk_html_fmt_dflt_)) {html_fmt_dflt = m.ReadBry("v");} + else if (ctx.Match(k, Invk_read_til_stop_fwd)) return read_til_stop_fwd; + else if (ctx.Match(k, Invk_read_til_stop_fwd_)) {read_til_stop_fwd = m.ReadInt("v"); GfoEvMgr_.PubVal(this, Evt_read_til_stop_fwd_changed, read_til_stop_fwd);} + else if (ctx.Match(k, Invk_read_til_stop_bwd)) return read_til_stop_bwd; + else if (ctx.Match(k, Invk_read_til_stop_bwd_)) {read_til_stop_bwd = m.ReadInt("v"); GfoEvMgr_.PubVal(this, Evt_read_til_stop_bwd_changed, read_til_stop_bwd);} + else if (ctx.Match(k, Invk_ns_allowed)) return String_.new_utf8_(ns_allowed); + else if (ctx.Match(k, Invk_ns_allowed_)) {ns_allowed = m.ReadBry("v"); GfoEvMgr_.PubVal(this, Evt_ns_allowed_changed, ns_allowed);} + else if (ctx.Match(k, Invk_tmpl_tkn_max)) return tmpl_tkn_max; + else if (ctx.Match(k, Invk_tmpl_tkn_max_)) {tmpl_tkn_max = m.ReadInt("v"); GfoEvMgr_.PubVal(this, Evt_tmpl_tkn_max_changed, tmpl_tkn_max);} + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private int Set_int_gt_0(GfoMsg m, int cur_val, String evt) { + int tmp = m.ReadInt("v"); + if (tmp < 1) return cur_val; + GfoEvMgr_.PubVal(this, evt, tmp); + return tmp; + } + private int Set_int(GfoMsg m, int cur_val, String evt) { + int tmp = m.ReadInt("v"); + GfoEvMgr_.PubVal(this, evt, tmp); + return tmp; + } + private static final String + Invk_enabled = "enabled", Invk_enabled_ = "enabled_" + , Invk_show_init_word_count = "show_init_word_count", Invk_show_init_word_count_ = "show_init_word_count_" + , Invk_show_more_word_count = "show_more_word_count", Invk_show_more_word_count_ = "show_more_word_count_" + , Invk_show_all_if_less_than = "show_all_if_less_than", Invk_show_all_if_less_than_ = "show_all_if_less_than_" + , Invk_xnde_ignore_ids = "xnde_ignore_ids", Invk_xnde_ignore_ids_ = "xnde_ignore_ids_" + , Invk_scan_len = "scan_len", Invk_scan_len_ = "scan_len_" + , Invk_scan_max = "scan_max", Invk_scan_max_ = "scan_max_" + , Invk_show_more = "show_more", Invk_show_all = "show_all" + , Invk_html_fmt = "html_fmt", Invk_html_fmt_ = "html_fmt_" + , Invk_html_fmt_dflt = "html_fmt_dflt", Invk_html_fmt_dflt_ = "html_fmt_dflt_" + , Invk_win_show_delay = "win_show_delay", Invk_win_show_delay_ = "win_show_delay_" + , Invk_win_hide_delay = "win_hide_delay", Invk_win_hide_delay_ = "win_hide_delay_" + , Invk_win_bind_focus_blur = "win_bind_focus_blur", Invk_win_bind_focus_blur_ = "win_bind_focus_blur_" + , Invk_win_max_w = "win_max_w", Invk_win_max_w_ = "win_max_w_" + , Invk_win_max_h = "win_max_h", Invk_win_max_h_ = "win_max_h_" + , Invk_win_show_all_max_w = "win_show_all_max_w", Invk_win_show_all_max_w_ = "win_show_all_max_w_" + , Invk_read_til_stop_fwd = "read_til_stop_fwd", Invk_read_til_stop_fwd_ = "read_til_stop_fwd_" + , Invk_read_til_stop_bwd = "read_til_stop_bwd", Invk_read_til_stop_bwd_ = "read_til_stop_bwd_" + , Invk_ns_allowed = "ns_allowed", Invk_ns_allowed_ = "ns_allowed_" + , Invk_tmpl_tkn_max = "tmpl_tkn_max", Invk_tmpl_tkn_max_ = "tmpl_tkn_max_" + ; + public static final String + Evt_show_init_word_count_changed = "show_init_word_count_changed" + , Evt_show_more_word_count_changed = "show_more_word_count_changed" + , Evt_show_all_if_less_than_changed = "show_all_if_less_than_changed" + , Evt_win_show_delay_changed = "win_show_delay_changed" + , Evt_win_hide_delay_changed = "win_hide_delay_changed" + , Evt_win_max_w_changed = "win_max_w_changed" + , Evt_win_max_h_changed = "win_max_h_changed" + , Evt_xnde_ignore_ids_changed = "xnde_ignore_ids_changed" + , Evt_scan_len_changed = "scan_len_changed" + , Evt_scan_max_changed = "scan_max_changed" + , Evt_html_fmt_changed = "html_fmt_changed" + , Evt_read_til_stop_fwd_changed = "read_til_stop_fwd_changed" + , Evt_read_til_stop_bwd_changed = "read_til_stop_bwd_changed" + , Evt_ns_allowed_changed = "ns_allowed_changed" + , Evt_tmpl_tkn_max_changed = "tmpl_tkn_max_changed" + ; + public static final byte[] + Dflt_coordinates = Bry_.new_ascii_("coordinates") + , Dflt_html_fmt = Bry_.new_ascii_(String_.Concat_lines_nl_skip_last + ( "" + )) + ; + public static final String[] Dflt_html_fmt_keys = String_.Ary("content", "page_lang_ltr", "page_url", "page_title", "wiki_domain", "wiki_item", "page_size", "edit_time", "view_time_item", "xowa_root_dir", "popup_id"); + public static final byte[] + Dflt_ns_allowed = Bry_.Empty + ; + public static final int + Dflt_show_init_word_count = 64 + , Dflt_show_more_word_count = 64 + , Dflt_show_all_if_less_than = -1 + , Dflt_show_all_win_max_w = -1 + , Dflt_win_show_delay = 400, Dflt_win_hide_delay = 400 + , Dflt_win_max_w = -1, Dflt_win_max_h = -1 + , Dflt_win_show_all_max_w = -1 + , Dflt_scan_len = 1 * Io_mgr.Len_kb + , Dflt_scan_max = 32 * Io_mgr.Len_kb + , Dflt_read_til_stop_fwd = -1 + , Dflt_read_til_stop_bwd = -1 + , Dflt_tmpl_tkn_max = 8192 + ; + public static final boolean + Dflt_win_bind_focus_blur = false + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_toc.java b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_toc.java new file mode 100644 index 000000000..155b36003 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/html/modules/Xoapi_toc.java @@ -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 . +*/ +package gplx.xowa.apis.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; import gplx.xowa.apis.xowa.html.*; +public class Xoapi_toc implements GfoInvkAble { + public void Init_by_kit(Xoa_app app) { + } + public boolean Collapsed() {return collapsed;} private boolean collapsed = false; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_collapsed)) return Yn.X_to_str(collapsed); + else if (ctx.Match(k, Invk_collapsed_)) collapsed = m.ReadYn("v"); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_collapsed = "collapsed", Invk_collapsed_ = "collapsed_" + ; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/navs/Xoapi_wiki.java b/400_xowa/src/gplx/xowa/apis/xowa/navs/Xoapi_wiki.java new file mode 100644 index 000000000..5f7ab619c --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/navs/Xoapi_wiki.java @@ -0,0 +1,36 @@ +/* +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.xowa.apis.xowa.navs; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.gui.views.*; +public class Xoapi_wiki implements GfoInvkAble { + private Xog_win_itm win; + public void Init_by_kit(Xoa_app app) { + win = app.Gui_mgr().Browser_win(); + } + public void Main_page() {win.Page__navigate_by_url_bar(win.Active_page().Wiki().Domain_str() + Xoh_href_parser.Href_wiki_str);} // NOTE: add "/wiki/" to generate non-page like url; EX: "home" -> "home/wiki/" which will be interpreted as a url, as opposed to "home" which will be intrepretted as page; DATE:2014-04-14 + public void Random() {win.Page__navigate_by_url_bar("Special:Random");} + public void Sandbox() {win.Page__navigate_by_url_bar("Project:Sandbox");} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_main_page)) this.Main_page(); + else if (ctx.Match(k, Invk_random)) this.Random(); + else if (ctx.Match(k, Invk_sandbox)) this.Sandbox(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_main_page = "main_page", Invk_random = "random", Invk_sandbox = "sandbox"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/startup/Xoapi_startup_tabs.java b/400_xowa/src/gplx/xowa/apis/xowa/startup/Xoapi_startup_tabs.java new file mode 100644 index 000000000..2ebd62c86 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/startup/Xoapi_startup_tabs.java @@ -0,0 +1,78 @@ +/* +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.xowa.apis.xowa.startup; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +class Xoapi_startup_tabs implements GfoInvkAble { + public String[] Calc_startup_strs() { + switch (type) { + case Xoapi_startup_tabs_type_.Tid_blank: return new String[] {gplx.xowa.specials.xowa.default_tab.Default_tab_page.Ttl_full_str}; + case Xoapi_startup_tabs_type_.Tid_xowa: return new String[] {gplx.xowa.users.Xouc_pages_mgr.Page_xowa}; + case Xoapi_startup_tabs_type_.Tid_custom: return String_.SplitLines_nl(custom); + case Xoapi_startup_tabs_type_.Tid_previous: return String_.SplitLines_nl(previous); + default: throw Err_.unhandled(type); + } + } + public String Custom() {return custom;} private String custom; + public boolean Custom_is_expr() {return custom_is_expr;} private boolean custom_is_expr; + public String Previous() {return previous;} public Xoapi_startup_tabs Previous_(String v) {previous = v; return this;} private String previous = ""; + public byte Type() {return type;} private byte type = Xoapi_startup_tabs_type_.Tid_xowa; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_type)) return Xoapi_startup_tabs_type_.X_to_key(type); + else if (ctx.Match(k, Invk_type_)) type = Xoapi_startup_tabs_type_.X_to_tid(m.ReadStr("v")); + else if (ctx.Match(k, Invk_custom)) return custom; + else if (ctx.Match(k, Invk_custom_)) custom = m.ReadStr("v"); + else if (ctx.Match(k, Invk_custom_is_expr)) return Yn.X_to_str(custom_is_expr); + else if (ctx.Match(k, Invk_custom_is_expr_)) custom_is_expr = m.ReadYn("v"); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_type = "type", Invk_type_ = "type_" + , Invk_custom = "custom", Invk_custom_ = "custom_" + , Invk_custom_is_expr = "custom_is_expr", Invk_custom_is_expr_ = "custom_is_expr_" + ; +} +class Xoapi_startup_tabs_type_ { + public static final byte Tid_blank = 0, Tid_xowa = 1, Tid_previous = 2, Tid_custom = 3; + public static final String Key_blank = "blank", Key_xowa = "xowa", Key_previous = "previous", Key_custom = "custom"; + public static String X_to_key(byte v) { + switch (v) { + case Tid_blank: return Key_blank; + case Tid_xowa: return Key_xowa; + case Tid_previous: return Key_previous; + case Tid_custom: return Key_custom; + default: throw Err_.not_implemented_(); + } + } + public static byte X_to_tid(String s) { + if (String_.Eq(s, Key_blank)) return Tid_blank; + else if (String_.Eq(s, Key_xowa)) return Tid_xowa; + else if (String_.Eq(s, Key_previous)) return Tid_previous; + else if (String_.Eq(s, Key_custom)) return Tid_custom; + else throw Err_.not_implemented_(); + } + public static KeyVal[] Options__list = KeyVal_.Ary(KeyVal_.new_(Key_blank), KeyVal_.new_(Key_xowa), KeyVal_.new_(Key_previous), KeyVal_.new_(Key_custom)); +} +/* +startup { +tabs { + type = 'previous|blank|custom|xowa_home'; + custom = 'en.wikipedia.org/wiki/{{{MONTHNAME}}} {{{DAY}}'; + custom_has_wikitext = 'n'; +} +} +*/ diff --git a/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_bookmarks.java b/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_bookmarks.java new file mode 100644 index 000000000..3972430c1 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_bookmarks.java @@ -0,0 +1,53 @@ +/* +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.xowa.apis.xowa.usrs; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.gui.history.*; import gplx.xowa.gui.views.*; +public class Xoapi_bookmarks implements GfoInvkAble { + private Xoa_app app; private Xog_win_itm win; + public void Ctor_by_app(Xoa_app app) {this.app = app;} + public void Init_by_kit(Xoa_app app) {this.win = app.Gui_mgr().Browser_win();} + public boolean Enabled() {return enabled;} private boolean enabled = true; + public void Enabled_(boolean v) {enabled = v;} + public void Add(String url_str) { + if (!enabled) return; + Xog_tab_itm tab = win.Active_tab(); if (tab == Xog_tab_itm_.Null) return; + Xoa_page page = tab.Page(); + byte[] wiki_domain = null, ttl_full_txt = null; + if (url_str == null) { + wiki_domain = page.Wiki().Domain_bry(); + ttl_full_txt = page.Ttl().Full_txt(); + } + else { + Xoa_url url = Xoa_url_parser.Parse_from_url_bar(app, page.Wiki(), url_str); + wiki_domain = url.Wiki_bry(); + ttl_full_txt = url.Page_bry(); + } + app.User().Bookmarks_add(wiki_domain, ttl_full_txt); + app.Usr_dlg().Prog_many("", "", "bookmark added: ~{0}", String_.new_utf8_(ttl_full_txt)); + } + public void Show() {win.Page__navigate_by_url_bar("home/wiki/Data:Bookmarks");} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return Yn.X_to_str(this.Enabled()); + else if (ctx.Match(k, Invk_enabled_)) Enabled_(m.ReadYn("v")); + else if (ctx.Match(k, Invk_add)) this.Add(m.ReadStrOr("v", null)); + else if (ctx.Match(k, Invk_show)) this.Show(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_enabled = "enabled", Invk_enabled_ = "enabled_", Invk_add = "add", Invk_show = "show"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_history.java b/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_history.java new file mode 100644 index 000000000..1ee28840d --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_history.java @@ -0,0 +1,37 @@ +/* +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.xowa.apis.xowa.usrs; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.gui.views.*; +public class Xoapi_history implements GfoInvkAble { + private Xoa_app app; private Xog_win_itm win; + public void Ctor_by_app(Xoa_app app) {this.app = app;} + public void Init_by_kit(Xoa_app app) {this.win = app.Gui_mgr().Browser_win();} + public boolean Enabled() {return enabled;} private boolean enabled = true; + public void Enabled_(boolean v) {enabled = v;} + public void Goto_recent() {win.Page__navigate_by_url_bar(app.User().History_mgr().Get_at_last(app));} + public void Show() {win.Page__navigate_by_url_bar("home/wiki/Special:XowaPageHistory");} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return Yn.X_to_str(this.Enabled()); + else if (ctx.Match(k, Invk_enabled_)) Enabled_(m.ReadYn("v")); + else if (ctx.Match(k, Invk_goto_recent)) this.Goto_recent(); + else if (ctx.Match(k, Invk_show)) this.Show(); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_enabled = "enabled", Invk_enabled_ = "enabled_", Invk_goto_recent = "goto_recent", Invk_show = "show"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_logs.java b/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_logs.java new file mode 100644 index 000000000..39ab4cfe4 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/usrs/Xoapi_logs.java @@ -0,0 +1,37 @@ +/* +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.xowa.apis.xowa.usrs; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.gui.views.*; +public class Xoapi_logs implements GfoInvkAble { + private Xoa_app app; + public void Ctor_by_app(Xoa_app app) {this.app = app;} + public void Init_by_kit(Xoa_app app) {} + public boolean Enabled() {return app.Log_wtr().Enabled();} + public void Enabled_(boolean v) { + app.Log_wtr().Enabled_(v); + if (!v) + Io_mgr._.DeleteFil_args(app.Log_wtr().Session_fil()).MissingFails_off().Exec(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return Yn.X_to_str(this.Enabled()); + else if (ctx.Match(k, Invk_enabled_)) Enabled_(m.ReadYn("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_enabled = "enabled", Invk_enabled_ = "enabled_"; +} diff --git a/400_xowa/src/gplx/xowa/apis/xowa/xtns/Xoapi_scribunto.java b/400_xowa/src/gplx/xowa/apis/xowa/xtns/Xoapi_scribunto.java new file mode 100644 index 000000000..57eeedd38 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apis/xowa/xtns/Xoapi_scribunto.java @@ -0,0 +1,38 @@ +/* +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.xowa.apis.xowa.xtns; import gplx.*; import gplx.xowa.*; import gplx.xowa.apis.*; import gplx.xowa.apis.xowa.*; +import gplx.xowa.xtns.scribunto.*; import gplx.xowa.xtns.scribunto.engines.*; +public class Xoapi_scribunto implements GfoInvkAble { + private Xoa_app app; + public void Init_by_kit(Xoa_app app) { + this.app = app; + } + public void Engine_(byte v) { + Scrib_xtn_mgr scrib_xtn = (Scrib_xtn_mgr)app.Xtn_mgr().Get_or_fail(Scrib_xtn_mgr.XTN_KEY); + scrib_xtn.Engine_type_(v); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_engine_lua_)) Engine_(Scrib_engine_type.Type_lua); + else if (ctx.Match(k, Invk_engine_luaj_)) Engine_(Scrib_engine_type.Type_luaj); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_engine_lua_ = "engine_lua_", Invk_engine_luaj_ = "engine_luaj_" + ; +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_gfs_mgr.java b/400_xowa/src/gplx/xowa/apps/Xoa_gfs_mgr.java new file mode 100644 index 000000000..25a056aa7 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_gfs_mgr.java @@ -0,0 +1,109 @@ +/* +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.xowa.apps; import gplx.*; import gplx.xowa.*; +import gplx.gfs.*; +import gplx.xowa.users.*; +public class Xoa_gfs_mgr implements GfoInvkAble, GfoInvkRootWkr { + public Xoa_gfs_mgr(Xoa_app app) { + this.app = app; + GfsCore._.AddCmd(app, Xoa_app.Invk_app); + GfsCore._.AddCmd(app, Xoa_app.Invk_xowa); + eval_mgr = new Xoa_app_eval(app); + } private Xoa_app app; + public Xoa_app_eval Eval_mgr() {return eval_mgr;} private Xoa_app_eval eval_mgr; + public static boolean Fail_if_unhandled = false; + public static final String Cfg_user_file = "xowa_user_cfg.gfs", Cfg_user_custom_file = "user_custom_cfg.gfs"; + private void Run_url_by_type(String type) { + Xou_fsys_mgr fsys_mgr = app.User().Fsys_mgr(); + Io_url app_data_dir = fsys_mgr.App_data_dir(); + Io_url url = null; + if (String_.Eq(type, "user_system_cfg")) url = app_data_dir.GenSubFil_nest("cfg", "user_system_cfg.gfs"); + else if (String_.Eq(type, "user_custom_cfg")) url = fsys_mgr.App_data_cfg_custom_fil(); + else if (String_.Eq(type, "xowa_user_cfg")) url = fsys_mgr.App_data_cfg_user_fil(); + else if (String_.Eq(type, "xowa")) url = app.Fsys_mgr().Root_dir().GenSubFil("xowa.gfs"); + else throw Err_mgr._.fmt_(GRP_KEY, "invalid_gfs_type", "invalid gfs type: ~{0}", type); + Run_url(url); + } + public GfoMsg Parse_root_msg(String v) {return gplx.gfs.Gfs_msg_bldr._.ParseToMsg(v);} + public Gfs_wtr Wtr() {return wtr;} private Gfs_wtr wtr = new Gfs_wtr(); + public void Run_url(Io_url url) { + Run_url_for(GfsCore._.Root(), url); + app.Log_wtr().Log_msg_to_session_fmt("gfs.done: ~{0}", url.Raw()); + } + public void Run_url_for(GfoInvkAble invk, Io_url url) { + String raw = Io_mgr._.LoadFilStr_args(url).MissingIgnored_().Exec(); if (String_.Len_eq_0(raw)) return; + Run_str_for(invk, raw); + } + public Object Run_str(String raw) {return Run_str_for(GfsCore._.Root(), raw);} + public Object Run_str_for(GfoInvkAble invk, String raw) {return Run_str_for(invk, Parse_root_msg(raw));} + public Object Run_str_for(GfoInvkAble invk, GfoMsg root_msg) { + try { + int sub_msgs_len = root_msg.Subs_count(); + GfsCtx ctx = GfsCtx.new_().Fail_if_unhandled_(Fail_if_unhandled).Usr_dlg_(app.Usr_dlg()); + Object rv = null; + for (int i = 0; i < sub_msgs_len; i++) { + GfoMsg sub_msg = root_msg.Subs_getAt(i); + rv = GfsCore._.ExecOne_to(ctx, invk, sub_msg); + } + return rv; + } catch (Exception e) { + app.Usr_dlg().Warn_many("", "", "error while executing script: err=~{0}", Err_.Message_gplx(e)); + return GfoInvkAble_.Rv_error; + } + } + public GfoEvObj Get_owner_as_event_obj(String cmd) { + GfoMsg cur_msg = Parse_root_msg(cmd).Subs_getAt(0); // ignore root_msg which is "" + GfoInvkAble cur_invk = app; + while (true) { + if (cur_msg.Subs_count() == 0) return (GfoEvObj)cur_invk; // terminal msg; return cur_invk + cur_invk = (GfoInvkAble)GfoInvkAble_.InvkCmd(cur_invk, cur_msg.Key()); + cur_msg = cur_msg.Subs_getAt(0); + } + } + public byte[] Build_prop_set_to_bry(Bry_bfr bfr, byte[] prop, byte[] val) {Build_prop_set(bfr, prop, val); return bfr.XtoAryAndClear();} + public void Build_prop_set(Bry_bfr bfr, byte[] prop, byte[] val) { + fmtr_eval_set.Bld_bfr_many(bfr, prop, Xoa_gfs_mgr.Cfg_save_escape(val)); + } private Bry_fmtr fmtr_eval_set = Bry_fmtr.new_("~{prop}_('~{val}');\n", "prop", "val"); + public byte[] Build_prop_get_to_bry(Bry_bfr bfr, byte[] prop) {Build_prop_get(bfr, prop); return bfr.XtoAryAndClear();} + public void Build_prop_get(Bry_bfr bfr, byte[] prop) { + fmtr_eval_get.Bld_bfr_many(bfr, prop); + } private Bry_fmtr fmtr_eval_get = Bry_fmtr.new_("~{prop};\n", "prop"); + static final String GRP_KEY = "Xoa_gfs_mgr"; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_run_file_by_type)) Run_url_by_type(m.ReadStr("v")); + else if (ctx.Match(k, Invk_fail_if_unhandled_)) {Fail_if_unhandled = m.ReadYn("v"); ctx.Fail_if_unhandled_(Fail_if_unhandled);} + else if (ctx.Match(k, Invk_txns)) {return GfoInvkAble_.Null;} // FUTURE: handle version for upgrades + else return GfoInvkAble_.Rv_unhandled; + return this; + } private static final String Invk_run_file_by_type = "run_file_by_type", Invk_fail_if_unhandled_ = "fail_if_unhandled_", Invk_txns = "txns"; + public static void Msg_parser_init() { + GfsCore._.MsgParser_(gplx.gfs.Gfs_msg_bldr._); + } + public static byte[] Cfg_save_escape(String v) {return Cfg_save_escape(Bry_.new_ascii_(v));} + public static byte[] Cfg_save_escape(byte[] v) { + return Bry_finder.Find_fwd(v, Byte_ascii.Apos) == Bry_.NotFound ? v : Bry_.Replace(v, Bry_apos_1, Bry_apos_2); + } static final byte[] Bry_apos_1 = Bry_.new_ascii_("'"), Bry_apos_2 = Bry_.new_ascii_("''"); + public static String Build_code(String... ary) { + int len = ary.length; + for (int i = 0; i < len; i++) { + if (i != 0) Build_code_bfr.Add_byte(Byte_ascii.Dot); + Build_code_bfr.Add_str(ary[i]); + } + return Build_code_bfr.XtoStrAndClear(); + } static final Bry_bfr Build_code_bfr = Bry_bfr.new_(); +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_gfs_php_mgr.java b/400_xowa/src/gplx/xowa/apps/Xoa_gfs_php_mgr.java new file mode 100644 index 000000000..a82d401c9 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_gfs_php_mgr.java @@ -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 . +*/ +package gplx.xowa.apps; import gplx.*; import gplx.xowa.*; +import gplx.php.*; +public class Xoa_gfs_php_mgr { + public static byte[] Xto_php(Bry_bfr bfr, boolean escape_backslash, byte[] src) { + int len = src.length; + int pos = 0; + boolean dirty = false; + while (pos < len) { + byte b = src[pos]; + switch (b) { + case Byte_ascii.Tilde: + if (!dirty) { + bfr.Add_mid(src, 0, pos); + dirty = true; + } + pos = Xto_php_swap(bfr, src, len, pos + 1); + break; + case Byte_ascii.Backslash: case Byte_ascii.Dollar: + case Byte_ascii.Apos: case Byte_ascii.Quote: + if (escape_backslash) { + if (!dirty) { + bfr.Add_mid(src, 0, pos); + dirty = true; + } + bfr.Add_byte(Byte_ascii.Backslash); + bfr.Add_byte(b); + } + else { + if (dirty) + bfr.Add_byte(b); + } + ++pos; + break; + default: + if (dirty) + bfr.Add_byte(b); + ++pos; + break; + } + } + return dirty ? bfr.XtoAryAndClear() : src; + } + private static int Xto_php_swap(Bry_bfr bfr, byte[] src, int len, int pos) { + if (pos >= len) throw Err_.new_("invalid gfs: tilde is last char; src={0}", String_.new_utf8_(src)); + byte b = src[pos]; + switch (b) { + case Byte_ascii.Tilde: { // ~~ -> ~ + bfr.Add_byte(Byte_ascii.Tilde); + return pos + 1; + } + case Byte_ascii.Curly_bgn: { + int num_bgn = pos + 1; + int num_end = Bry_finder.Find_fwd_while_num(src, num_bgn, len); // +1 to position after { + if ( num_end == Bry_finder.Not_found + || num_end == len + || src[num_end] != Byte_ascii.Curly_end + ) + throw Err_.new_("invalid gfs; num_end not found={0}", String_.new_utf8_(src)); + bfr.Add_byte(Byte_ascii.Dollar); + int arg_idx = Bry_.X_to_int_or(src, num_bgn, num_end, -1); + if (arg_idx == -1) { + throw Err_.new_("invalid int"); + } + bfr.Add_int_variable(arg_idx + 1); + return num_end + 1; + } + default: { + throw Err_.new_("invalid gfs; next char after tilde must be either tilde or open brace; src={0}", String_.new_utf8_(src)); + } + } + } + public static byte[] Xto_gfs(Bry_bfr bfr, byte[] raw) { + int raw_len = raw.length; + for (int i = 0; i < raw_len; i++) { + byte b = raw[i]; + switch (b) { + case Byte_ascii.Backslash: + ++i; + if (i < raw_len) + bfr.Add_byte(raw[i]); + else + bfr.Add_byte(Byte_ascii.Backslash); + break; + case Byte_ascii.Tilde: + bfr.Add_byte_repeat(Bry_fmtr.char_escape, 2); // escape tilde; EX: ~u -> ~~u; DATE:2013-11-11 + break; + case Byte_ascii.Dollar: + int end_pos = Php_text_itm_parser.Find_fwd_non_int(raw, i + 1, raw_len); + int int_val = Bry_.X_to_int_or(raw, i + 1, end_pos, -1); + bfr.Add_byte(Bry_fmtr.char_escape).Add_byte(Bry_fmtr.char_arg_bgn).Add_int_variable(int_val - 1).Add_byte(Bry_fmtr.char_arg_end); + i = end_pos - 1; + break; + default: + bfr.Add_byte(b); + break; + } + } + return bfr.XtoAryAndClear(); + } +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_gfs_php_mgr_tst.java b/400_xowa/src/gplx/xowa/apps/Xoa_gfs_php_mgr_tst.java new file mode 100644 index 000000000..c46e00fac --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_gfs_php_mgr_tst.java @@ -0,0 +1,50 @@ +/* +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.xowa.apps; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xoa_gfs_php_mgr_tst { + @Before public void init() {fxt.Clear();} private Xoa_gfs_php_mgr_fxt fxt = new Xoa_gfs_php_mgr_fxt(); + @Test public void Xto_gfs() { + fxt.Test_Xto_gfs("a\\\\b\\'c\\\"d\\$e" , "a\\b'c\"d$e"); // backslash.escape + fxt.Test_Xto_gfs("\\" , "\\"); // backslash.eos; eos, but nothing to escape; render self but dont fail + fxt.Test_Xto_gfs("a~b" , "a~~b"); // tilde.escape + fxt.Test_Xto_gfs("a$1b" , "a~{0}b"); // dollar + } + @Test public void Xto_php() { + fxt.Test_Xto_php_escape_y("a~{0}b" , "a$1b"); // tilde.arg.one + fxt.Test_Xto_php_escape_y("a~{0}b~{1}c~{2}d" , "a$1b$2c$3d"); // tilde.arg.many + fxt.Test_Xto_php_escape_y("a~{9}" , "a$10"); // tilde.arg.9 -> 10 + fxt.Test_Xto_php_escape_y("a~~b" , "a~b"); // tilde.escape + fxt.Test_Xto_php_escape_y("a\\b'c\"d$e" , "a\\\\b\\'c\\\"d\\$e"); // backslash.escape_y + fxt.Test_Xto_php_escape_n("a\\b'c\"d$e" , "a\\b'c\"d$e"); // backslash.escape_n + } +} +class Xoa_gfs_php_mgr_fxt { + private Bry_bfr bfr = Bry_bfr.new_(); + public void Clear() {} + public void Test_Xto_gfs(String raw, String expd) { + byte[] actl = Xoa_gfs_php_mgr.Xto_gfs(bfr, Bry_.new_utf8_(raw)); + Tfds.Eq(expd, String_.new_utf8_(actl)); + } + public void Test_Xto_php_escape_y(String raw, String expd) {Test_Xto_php(raw, Bool_.Y, expd);} + public void Test_Xto_php_escape_n(String raw, String expd) {Test_Xto_php(raw, Bool_.N, expd);} + public void Test_Xto_php(String raw, boolean escape_backslash, String expd) { + byte[] actl = Xoa_gfs_php_mgr.Xto_php(bfr, escape_backslash, Bry_.new_utf8_(raw)); + Tfds.Eq(expd, String_.new_utf8_(actl)); + } +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_shell.java b/400_xowa/src/gplx/xowa/apps/Xoa_shell.java new file mode 100644 index 000000000..0dabb1e11 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_shell.java @@ -0,0 +1,36 @@ +/* +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.xowa.apps; import gplx.*; import gplx.xowa.*; +public class Xoa_shell implements GfoInvkAble { + public Xoa_shell(Xoa_app app) {this.app = app;} private Xoa_app app; + public boolean Fetch_page_exec_async() {return fetch_page_exec_async;} private boolean fetch_page_exec_async = true; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_fetch_page)) return Fetch_page(m); + else if (ctx.Match(k, Invk_chars_per_line_max_)) ConsoleAdp._.CharsPerLineMax_set(m.ReadInt("v")); + else if (ctx.Match(k, Invk_backspace_by_bytes_)) ConsoleAdp._.Backspace_by_bytes_(m.ReadYn("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private String Fetch_page(GfoMsg m) { + return String_.new_utf8_(app.Gui_mgr().Browser_win().App__retrieve_by_url(m.ReadStr("url"), m.ReadStrOr("output_type", "html"))); + } + private static final String Invk_fetch_page = "fetch_page" + , Invk_chars_per_line_max_ = "chars_per_line_max_" + , Invk_backspace_by_bytes_ = "backspace_by_bytes_" + ; +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_shell_tst.java b/400_xowa/src/gplx/xowa/apps/Xoa_shell_tst.java new file mode 100644 index 000000000..658d17072 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_shell_tst.java @@ -0,0 +1,30 @@ +/* +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.xowa.apps; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xoa_shell_tst { + @Test public void Fetch_page() { // PURPOSE.fix: fetch_page failed with nullRef; DATE:2013-04-12 + Xop_fxt parser_fxt = new Xop_fxt(); + Xoa_app app = parser_fxt.App(); Xow_wiki wiki = parser_fxt.Wiki(); + parser_fxt.Init_page_create("A", "test"); // need to create page in order for html output to gen + wiki.Html_mgr().Page_wtr_mgr().Html_capable_(true); // need to set html_capable in order for div_home to load + Xoa_gfs_mgr.Msg_parser_init(); + wiki.Html_mgr().Portal_mgr().Div_home_fmtr().Fmt_("~{<>app.user.msgs.get('mainpage-description');<>}"); + app.Gfs_mgr().Run_str("app.shell.fetch_page('en.wikipedia.org/wiki/A' 'html');"); // this causes a nullRef error b/c app.user.lang is null + } +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_stage_.java b/400_xowa/src/gplx/xowa/apps/Xoa_stage_.java new file mode 100644 index 000000000..52bf458e3 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_stage_.java @@ -0,0 +1,21 @@ +/* +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.xowa.apps; import gplx.*; import gplx.xowa.*; +public class Xoa_stage_ { + public static final byte Tid_ctor = 0, Tid_init = 1, Tid_launch = 2; +} diff --git a/400_xowa/src/gplx/xowa/apps/Xoa_thread_mgr.java b/400_xowa/src/gplx/xowa/apps/Xoa_thread_mgr.java new file mode 100644 index 000000000..025cd6f10 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/Xoa_thread_mgr.java @@ -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 . +*/ +package gplx.xowa.apps; import gplx.*; import gplx.xowa.*; +import gplx.threads.*; +public class Xoa_thread_mgr { + public Gfo_thread_pool Page_load_mgr() {return page_load_mgr;} private Gfo_thread_pool page_load_mgr = new Gfo_thread_pool(); + public Gfo_thread_pool File_load_mgr() {return file_load_mgr;} private Gfo_thread_pool file_load_mgr = new Gfo_thread_pool(); + public void Usr_dlg_(Gfo_usr_dlg usr_dlg) { + page_load_mgr.Usr_dlg_(usr_dlg); + file_load_mgr.Usr_dlg_(usr_dlg); + } +} diff --git a/400_xowa/src/gplx/xowa/apps/caches/Wdata_doc_cache.java b/400_xowa/src/gplx/xowa/apps/caches/Wdata_doc_cache.java new file mode 100644 index 000000000..6bd669045 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/caches/Wdata_doc_cache.java @@ -0,0 +1,26 @@ +/* +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.xowa.apps.caches; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +import gplx.xowa.xtns.wdatas.*; +public class Wdata_doc_cache { + private Hash_adp_bry hash = Hash_adp_bry.cs_(); + public void Add(byte[] qid, Wdata_doc doc) {hash.Add(qid, doc);} + public Wdata_doc Get_or_null(byte[] qid) {return (Wdata_doc)hash.Get_by_bry(qid);} + public void Free_mem_all() {this.Clear();} + public void Clear() {hash.Clear();} +} diff --git a/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java b/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java new file mode 100644 index 000000000..e73e3e869 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/caches/Xoa_cache_mgr.java @@ -0,0 +1,24 @@ +/* +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.xowa.apps.caches; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +public class Xoa_cache_mgr { + public Wdata_doc_cache Doc_cache() {return doc_cache;} private Wdata_doc_cache doc_cache = new Wdata_doc_cache(); + public void Free_mem_all() { + doc_cache.Free_mem_all(); + } +} diff --git a/400_xowa/src/gplx/xowa/apps/fsys/Launcher_app_mgr.java b/400_xowa/src/gplx/xowa/apps/fsys/Launcher_app_mgr.java new file mode 100644 index 000000000..38ad5ad2b --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/fsys/Launcher_app_mgr.java @@ -0,0 +1,128 @@ +/* +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.xowa.apps.fsys; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +public class Launcher_app_mgr implements GfoInvkAble { + public Launcher_app_mgr(Xoa_app app) {this.app = app;} private Xoa_app app; + public void Init() { + Xoa_fsys_eval cmd_eval = app.Url_cmd_eval(); + ProcessAdp.ini_(this, app.Gui_wtr(), app_query_img_size , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 10 * 60, "~{<>bin_plat_dir<>}imagemagick\\identify", "-ping -format \"<{%w,%h}>\" \"~{file}\"", "file"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_resize_img , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 10 * 60, "~{<>bin_plat_dir<>}imagemagick\\convert", "\"~{source}\" -coalesce -resize ~{width}x~{height} \"~{target}\"", "source", "target", "width", "height"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_convert_svg_to_png , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 10 * 60, "~{<>bin_plat_dir<>}inkscape\\inkscape", "-z -w ~{width} -f \"~{source}\" -e \"~{target}\"", "source", "target", "width").Thread_kill_name_("inkscape.exe"); // // -z=without-gui; -w=width; -f=file -e=export-png + ProcessAdp.ini_(this, app.Gui_wtr(), app_convert_tex_to_dvi , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 2 * 60, "~{<>bin_plat_dir<>}miktex\\miktex\\bin\\latex", "-quiet -output-directory=~{temp_dir} -job-name=xowa_temp ~{tex_file}", "tex_file", "temp_dir"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_convert_dvi_to_png , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 2 * 60, "~{<>bin_plat_dir<>}miktex\\miktex\\bin\\dvipng", "~{dvi_file} -o ~{png_file} -q* -T tight -bg Transparent", "dvi_file", "png_file", "temp_dir"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_convert_djvu_to_tiff , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 1 * 60, "~{<>bin_plat_dir<>}djvulibre\\ddjvu", "-format=tiff -page=1 \"~{source}\" \"~{target}\"", "source", "target"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_decompress_bz2 , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 0 , "~{<>bin_plat_dir<>}7-zip\\7za", "x -y \"~{src}\" -o\"~{trg_dir}\"", "src", "trg", "trg_dir"); // x=extract; -y=yes on all queries; -o=output_dir + ProcessAdp.ini_(this, app.Gui_wtr(), app_decompress_bz2_by_stdout, cmd_eval, ProcessAdp.Run_mode_sync_timeout , 0 , "~{<>bin_plat_dir<>}7-zip\\7za", "x -so \"~{src}\"", "src"); // x=extract; -so=stdout + ProcessAdp.ini_(this, app.Gui_wtr(), app_decompress_zip , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 0 , "~{<>bin_plat_dir<>}7-zip\\7za", "x -y \"~{src}\" -o\"~{trg_dir}\"", "src", "trg", "trg_dir"); // x=extract; -y=yes on all queries; -o=output_dir + ProcessAdp.ini_(this, app.Gui_wtr(), app_decompress_gz , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 0 , "~{<>bin_plat_dir<>}7-zip\\7za", "x -y \"~{src}\" -o\"~{trg_dir}\"", "src", "trg", "trg_dir"); // x=extract; -y=yes on all queries; -o=output_dir + ProcessAdp.ini_(this, app.Gui_wtr(), app_lua , cmd_eval, ProcessAdp.Run_mode_async , 0 , "~{<>bin_plat_dir<>}lua\\lua", "", ""); + ProcessAdp.ini_(this, app.Gui_wtr(), app_lilypond , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 1 * 60, "~{<>bin_plat_dir<>}lilypond\\usr\\bin\\lilypond.exe", "\"-dsafe=#t\" -dbackend=ps --png --header=texidoc -dmidi-extension=midi \"~{file}\"", "file"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_abc2ly , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 1 * 60, "~{<>bin_plat_dir<>}lilypond\\usr\\bin\\python.exe", "abc2ly.py -s \"--output=~{target}\" \"~{source}\"", "source", "target"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_trim_img , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 1 * 60, "~{<>bin_plat_dir<>}imagemagick\\convert", "-trim \"~{source}\" \"~{target}\"", "source", "target"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_convert_midi_to_ogg , cmd_eval, ProcessAdp.Run_mode_sync_timeout , 1 * 60, "~{<>bin_plat_dir<>}timidity\\timidity", "-Ov \"--output-file=~{target}\" \"~{source}\"", "source", "target"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_view_web , cmd_eval, ProcessAdp.Run_mode_async , 0 , "cmd", "/c start \"~{url}\"", "url"); + ProcessAdp.ini_(this, app.Gui_wtr(), app_view_text , cmd_eval, ProcessAdp.Run_mode_async , 0 , "cmd", "/c start \"~{url}\"", "url"); + int cmds_view_file_by_ext_len = cmds_view_file_by_ext.length; + for (int i= 0; i < cmds_view_file_by_ext_len; i++) { + ProcessAdp cmd = new ProcessAdp(); + cmds_view_file_by_ext [i] = cmd; + ProcessAdp.ini_(this, app.Gui_wtr(), cmd , cmd_eval, ProcessAdp.Run_mode_async , 0 , "cmd", "/c start \"~{file}\"", "file"); + } + } + public ProcessAdp App_query_img_size() {return app_query_img_size;} private ProcessAdp app_query_img_size = new ProcessAdp(); + public ProcessAdp App_resize_img() {return app_resize_img;} private ProcessAdp app_resize_img = new ProcessAdp(); + public ProcessAdp App_convert_svg_to_png() {return app_convert_svg_to_png;} private ProcessAdp app_convert_svg_to_png = new ProcessAdp(); + public ProcessAdp App_convert_tex_to_dvi() {return app_convert_tex_to_dvi;} private ProcessAdp app_convert_tex_to_dvi = new ProcessAdp(); + public ProcessAdp App_convert_dvi_to_png() {return app_convert_dvi_to_png;} private ProcessAdp app_convert_dvi_to_png = new ProcessAdp(); + public ProcessAdp App_convert_djvu_to_tiff() {return app_convert_djvu_to_tiff;} private ProcessAdp app_convert_djvu_to_tiff = new ProcessAdp(); + public ProcessAdp App_view_web() {return app_view_web;} private ProcessAdp app_view_web = new ProcessAdp(); + public ProcessAdp App_view_text() {return app_view_text;} private ProcessAdp app_view_text = new ProcessAdp(); + public ProcessAdp App_decompress_bz2() {return app_decompress_bz2;} private ProcessAdp app_decompress_bz2 = new ProcessAdp(); + public ProcessAdp App_decompress_zip() {return app_decompress_zip;} private ProcessAdp app_decompress_zip = new ProcessAdp(); + public ProcessAdp App_decompress_gz() {return app_decompress_gz;} private ProcessAdp app_decompress_gz = new ProcessAdp(); + public ProcessAdp App_decompress_bz2_by_stdout() {return app_decompress_bz2_by_stdout;} private ProcessAdp app_decompress_bz2_by_stdout = new ProcessAdp(); + public ProcessAdp App_lua() {return app_lua;} private ProcessAdp app_lua = new ProcessAdp(); + public ProcessAdp App_lilypond() {return app_lilypond;} private ProcessAdp app_lilypond = new ProcessAdp(); + public ProcessAdp App_abc2ly() {return app_abc2ly;} private ProcessAdp app_abc2ly = new ProcessAdp(); + public ProcessAdp App_trim_img() {return app_trim_img;} private ProcessAdp app_trim_img = new ProcessAdp(); + public ProcessAdp App_convert_midi_to_ogg() {return app_convert_midi_to_ogg;} private ProcessAdp app_convert_midi_to_ogg = new ProcessAdp(); + public ProcessAdp App_by_ext(String ext) {return App_by_ext_key(String_.Mid(ext, 1));} // ignore 1st . in ext; EX: ".png" -> "png" + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_query_img_size)) return app_query_img_size; + else if (ctx.Match(k, Invk_resize_img)) return app_resize_img; + else if (ctx.Match(k, Invk_convert_svg_to_png)) return app_convert_svg_to_png; + else if (ctx.Match(k, Invk_convert_tex_to_dvi)) return app_convert_tex_to_dvi; + else if (ctx.Match(k, Invk_convert_dvi_to_png)) return app_convert_dvi_to_png; + else if (ctx.Match(k, Invk_convert_djvu_to_tiff)) return app_convert_djvu_to_tiff; + else if (ctx.Match(k, Invk_view_web)) return app_view_web; + else if (ctx.Match(k, Invk_decompress_bz2)) return app_decompress_bz2; + else if (ctx.Match(k, Invk_decompress_zip)) return app_decompress_zip; + else if (ctx.Match(k, Invk_decompress_gz)) return app_decompress_gz; + else if (ctx.Match(k, Invk_decompress_bz2_by_stdout)) return app_decompress_bz2_by_stdout; + else if (ctx.Match(k, Invk_lua)) return app_lua; + else if (ctx.Match(k, Invk_lilypond)) return app_lilypond; + else if (ctx.Match(k, Invk_abc2ly)) return app_abc2ly; + else if (ctx.Match(k, Invk_convert_midi_to_ogg)) return app_convert_midi_to_ogg; + else if (ctx.Match(k, Invk_trim_img)) return app_trim_img; + else if (ctx.Match(k, Invk_web)) return app_view_web; + else if (ctx.Match(k, Invk_text)) return app_view_text; + else if (ctx.Match(k, Invk_image)) return Init_by_exts("png", "jpg", "jpeg", "gif", "tif", "tiff", "svg"); + else if (ctx.Match(k, Invk_media)) return Init_by_exts("mid", "ogg", "oga", "ogv", "webm"); + else if (ctx.Match(k, Invk_svg)) return Init_by_exts("svg"); + else if (ctx.Match(k, Invk_pdf)) return Init_by_exts("pdf"); + else if (ctx.Match(k, Invk_djvu)) return Init_by_exts("djvu"); + else if (ctx.Match(k, Invk_view_by_ext)) Exec_view_by_ext(m.ReadStr("exts"), m.ReadStr("cmd"), m.ReadStr("args")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + public void Exec_view_web(byte[] url) { + url = Bry_.Replace(url, Quote_normal, Quote_escape); // escape quotes; DATE:2013-03-31 + String url_str = String_.new_utf8_(url); + url_str = ProcessAdp.Escape_ampersands_if_process_is_cmd(Op_sys.Cur().Tid_is_wnt(), app_view_web.Exe_url().Raw(), url_str); // escape ampersands; DATE:2014-05-20 + app_view_web.Run(url_str); + } private static final byte[] Quote_normal = new byte[] {Byte_ascii.Quote}, Quote_escape = new byte[] {Byte_ascii.Quote, Byte_ascii.Quote}; + private ProcessAdp App_by_ext_key(String ext) {return cmds_view_file_by_ext[Xof_ext_.Get_id_by_ext_(Bry_.new_ascii_(ext))];} + public void Exec_view_by_ext(String exts_raw, String cmd, String args) { + String[] exts_ary = String_.Split(exts_raw, '|'); + int exts_ary_len = exts_ary.length; + for (int i = 0; i < exts_ary_len; i++) + App_by_ext_key(exts_ary[i]).Cmd_args(cmd, args); + } ProcessAdp[] cmds_view_file_by_ext = new ProcessAdp[Xof_ext_.Id__max]; + private ProcessAdp Init_by_exts(String... exts) { + ProcessAdp rv = App_by_ext_key(exts[0]); + int len = exts.length; + for (int i = 0; i < len; i++) { + cmds_view_file_by_ext[Xof_ext_.Get_id_by_ext_(Bry_.new_ascii_(exts[i]))] = rv; + } + return rv; + } + private static final String Invk_query_img_size = "query_img_size", Invk_resize_img = "resize_img", Invk_convert_svg_to_png = "convert_svg_to_png", Invk_convert_tex_to_dvi = "convert_tex_to_dvi", Invk_convert_dvi_to_png = "convert_dvi_to_png" + , Invk_convert_djvu_to_tiff = "convert_djvu_to_tiff", Invk_view_web = "view_web" + , Invk_decompress_bz2 = "decompress_bz2", Invk_decompress_zip = "decompress_zip", Invk_decompress_gz = "decompress_gz", Invk_decompress_bz2_by_stdout = "decompress_bz2_by_stdout" + , Invk_view_by_ext = "view_by_ext" + , Invk_lua = "lua", Invk_lilypond = "lilypond", Invk_abc2ly = "abc2ly", Invk_trim_img = "trim_img", Invk_convert_midi_to_ogg = "convert_midi_to_ogg" + , Invk_web = "web" + , Invk_media = "media" + , Invk_image = "image" + , Invk_svg = "svg" + , Invk_pdf = "pdf" + , Invk_djvu = "djvu" + , Invk_text = "text" + ; + public static final int Len_dlm_fld = 1, Adj_next_char = 1; +} diff --git a/400_xowa/src/gplx/xowa/apps/fsys/Xoa_fsys_eval.java b/400_xowa/src/gplx/xowa/apps/fsys/Xoa_fsys_eval.java new file mode 100644 index 000000000..f8dab39d8 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/fsys/Xoa_fsys_eval.java @@ -0,0 +1,37 @@ +/* +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.xowa.apps.fsys; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +public class Xoa_fsys_eval implements Bry_fmtr_eval_mgr { + public Xoa_fsys_eval(Xoa_app app) {this.app = app;} private Xoa_app app; + public boolean Enabled() {return enabled;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled = true; + public byte[] Eval(byte[] cmd) { + Object o = hash.Get_by_bry(cmd); + if (o == null) return null; + byte val = ((Byte_obj_val)o).Val(); + switch (val) { + case Tid_bin_plat_dir: return app.Fsys_mgr().Bin_plat_dir().RawBry(); + case Tid_user_temp_dir: return app.User().Fsys_mgr().App_temp_dir().RawBry(); + case Tid_xowa_root_dir: return app.Fsys_mgr().Root_dir().RawBry(); + case Tid_user_cfg_dir: return app.User().Fsys_mgr().App_data_cfg_dir().RawBry(); + default: throw Err_mgr._.unhandled_(val); + } + } + Hash_adp_bry hash = Hash_adp_bry.ci_().Add_bry_byte(Bry_bin_plat_dir, Tid_bin_plat_dir).Add_bry_byte(Bry_user_temp_dir, Tid_user_temp_dir).Add_bry_byte(Bry_xowa_root_dir, Tid_xowa_root_dir).Add_bry_byte(Bry_user_cfg_dir, Tid_user_cfg_dir); + private static final byte[] Bry_bin_plat_dir = Bry_.new_ascii_("bin_plat_dir"), Bry_user_temp_dir = Bry_.new_ascii_("user_temp_dir"), Bry_xowa_root_dir = Bry_.new_ascii_("xowa_root_dir"), Bry_user_cfg_dir = Bry_.new_ascii_("user_cfg_dir"); + static final byte Tid_bin_plat_dir = 0, Tid_user_temp_dir = 1, Tid_xowa_root_dir = 2, Tid_user_cfg_dir = 3; +} diff --git a/400_xowa/src/gplx/xowa/apps/fsys/Xoa_fsys_mgr.java b/400_xowa/src/gplx/xowa/apps/fsys/Xoa_fsys_mgr.java new file mode 100644 index 000000000..c97e9c3d7 --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/fsys/Xoa_fsys_mgr.java @@ -0,0 +1,49 @@ +/* +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.xowa.apps.fsys; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +public class Xoa_fsys_mgr implements GfoInvkAble { + public Xoa_fsys_mgr(Xoa_app app, Io_url root_dir, String bin_dir_name) { + this.root_dir = root_dir; + bin_any_dir = root_dir.GenSubDir("bin").GenSubDir("any"); + bin_plat_dir = root_dir.GenSubDir("bin").GenSubDir(bin_dir_name); + bin_extensions_dir = bin_any_dir.GenSubDir_nest("xowa", "xtns"); + file_dir = root_dir.GenSubDir("file"); + wiki_dir = root_dir.GenSubDir("wiki"); + temp_dir = root_dir.GenSubDir("tmp"); + app_mgr = new Launcher_app_mgr(app); + } + public Io_url Root_dir() {return root_dir;} private Io_url root_dir; + public Io_url File_dir() {return file_dir;} private Io_url file_dir; + public Io_url Wiki_dir() {return wiki_dir;} public Xoa_fsys_mgr Wiki_dir_(Io_url v) {wiki_dir = v; return this;} private Io_url wiki_dir; + public Io_url Temp_dir() {return temp_dir;} public Xoa_fsys_mgr Temp_dir_(Io_url v) {temp_dir = v; return this;} private Io_url temp_dir; // set to /xowa/user//temp + public Io_url Bin_any_dir() {return bin_any_dir;} private Io_url bin_any_dir; + public Io_url Bin_extensions_dir() {return bin_extensions_dir;} private Io_url bin_extensions_dir; + public Io_url Bin_plat_dir() {return bin_plat_dir;} private Io_url bin_plat_dir; + public Io_url Bin_db_dir() {return bin_any_dir.GenSubDir_nest("sql", "xowa");} + public Launcher_app_mgr App_mgr() {return app_mgr;} Launcher_app_mgr app_mgr; + public void Init() { + app_mgr.Init(); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_apps)) return app_mgr; + else if (ctx.Match(k, Invk_root_dir)) return root_dir; + else return GfoInvkAble_.Rv_unhandled; + } + private static final String Invk_apps = "apps", Invk_root_dir = "root_dir"; + public static final String DirName_wiki = "wiki", DirName_user = "user"; +} diff --git a/400_xowa/src/gplx/xowa/apps/ttls/Xoa_ttl__anchor_tst.java b/400_xowa/src/gplx/xowa/apps/ttls/Xoa_ttl__anchor_tst.java new file mode 100644 index 000000000..aea4e899b --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/ttls/Xoa_ttl__anchor_tst.java @@ -0,0 +1,37 @@ +/* +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.xowa.apps.ttls; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +import org.junit.*; +public class Xoa_ttl__anchor_tst { + @Before public void init() {fxt.Reset();} private Xoa_ttl_fxt fxt = new Xoa_ttl_fxt(); + @Test public void Anch_y() {fxt.Init_ttl("a#b") .Expd_full_txt("A").Expd_page_txt("A").Expd_anch_txt("b").Test();} + @Test public void Anch_only() {fxt.Init_ttl("#a") .Expd_full_txt("").Expd_page_txt("").Expd_anch_txt("a").Test();} + @Test public void Anchor_fails() { // PURPOSE: :#batch:Main Page causes ttl to fail b/c # is treated as anchor; DATE:2013-01-02 + fxt.Wiki().Xwiki_mgr().Add_full(Bry_.new_ascii_("#batch"), Bry_.new_ascii_("none")); + fxt.Init_ttl(":#batch:Main Page").Expd_full_txt("Main Page").Test(); + } + @Test public void Anchor_angles() {// PURPOSE: angles in anchor should be encoded; DATE: 2013-01-23 + fxt.Init_ttl("A#bd").Expd_full_txt("A").Expd_anch_txt("b.3Cc.3Ed").Test(); + } + @Test public void Anchor_arg_in_non_main_ns() { + fxt.Init_ttl("Help:A#B").Expd_full_txt("Help:A").Expd_anch_txt("B").Test(); + } + @Test public void Anchor_and_slash() { // PURPOSE: slash in anchor was being treated as a subpage; DATE:2014-01-14 + fxt.Init_ttl("A#b/c").Expd_full_txt("A").Expd_anch_txt("b/c").Expd_leaf_txt("A").Test(); // NOTE: Leaf_txt should be Page_txt; used to fail + } +} \ No newline at end of file diff --git a/400_xowa/src/gplx/xowa/apps/ttls/Xoa_ttl__basic_tst.java b/400_xowa/src/gplx/xowa/apps/ttls/Xoa_ttl__basic_tst.java new file mode 100644 index 000000000..ee9c7108b --- /dev/null +++ b/400_xowa/src/gplx/xowa/apps/ttls/Xoa_ttl__basic_tst.java @@ -0,0 +1,61 @@ +/* +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.xowa.apps.ttls; import gplx.*; import gplx.xowa.*; import gplx.xowa.apps.*; +import org.junit.*; +public class Xoa_ttl__basic_tst { + @Before public void init() {fxt.Reset();} private Xoa_ttl_fxt fxt = new Xoa_ttl_fxt(); + @Test public void Ns() {fxt.Init_ttl("Help:Test") .Expd_ns_id(Xow_ns_.Id_help).Expd_page_txt("Test").Test();} + @Test public void Ns_false() {fxt.Init_ttl("Helpx:Test") .Expd_ns_id(Xow_ns_.Id_main).Expd_page_txt("Helpx:Test").Test();} + @Test public void Ns_multiple() {fxt.Init_ttl("Help:Talk:test") .Expd_page_txt("Talk:test").Test();} + @Test public void Full_txt() {fxt.Init_ttl("Help:a & b") .Expd_full_txt("Help:A & b").Test();} // preserve + @Test public void Full_url() {fxt.Init_ttl("Help:a & b") .Expd_full_url("Help:A_%26_b").Test();} // escape + @Test public void Page_txt() {fxt.Init_ttl("a & b") .Expd_page_txt("A & b").Test();} // preserve; + @Test public void Page_txt_underline() {fxt.Init_ttl("a_b") .Expd_page_txt("A b").Expd_page_url("A_b").Test();} // NOTE: raw is "a_b" but txt is "a b" + @Test public void Page_url() {fxt.Init_ttl("a & b") .Expd_page_url("A_%26_b").Test();} // escape + @Test public void Page_url_w_ns() {fxt.Init_ttl("Help:a & b") .Expd_page_url("A_%26_b").Test();} // remove ns + @Test public void Page_url_symbols() {fxt.Init_ttl(";:@&=$ -_.+!*'(),/").Expd_page_url(";:@%26%3D$_-_.%2B!*%27(),/").Test();}// symbols + space + @Test public void Leaf_txt() {fxt.Init_ttl("root/mid/leaf") .Expd_leaf_txt("leaf").Test();} + @Test public void Leaf_txt_slash_is_last() {fxt.Init_ttl("root/mid/leaf/") .Expd_leaf_txt("").Test();} + @Test public void Leaf_txt_no_slash() {fxt.Init_ttl("root") .Expd_leaf_txt("Root").Test();} + @Test public void Leaf_url() {fxt.Init_ttl("root/mid/a & b") .Expd_leaf_url("a_%26_b").Test();} // NOTE: a not capitalized ("root" would be) + @Test public void Base_txt() {fxt.Init_ttl("a & b/mid/leaf") .Expd_base_txt("A & b/mid").Test();} + @Test public void Base_url() {fxt.Init_ttl("a & b/mid/leaf") .Expd_base_url("A_%26_b/mid").Test();} + @Test public void Root_txt() {fxt.Init_ttl("root/mid/leaf") .Expd_root_txt("Root").Test();} + @Test public void Rest_txt() {fxt.Init_ttl("root/mid/leaf") .Expd_rest_txt("mid/leaf").Test();} + @Test public void Talk_txt() {fxt.Init_ttl("Help:test") .Expd_talk_txt("Help talk:Test").Test();} + @Test public void Talk_txt_identity() {fxt.Init_ttl("Help talk:test") .Expd_talk_txt("Help talk:Test").Test();} + @Test public void Talk_url() {fxt.Init_ttl("Help:a & b") .Expd_talk_url("Help_talk:A_%26_b").Test();} + @Test public void Talk_txt_main() {fxt.Init_ttl("test") .Expd_talk_txt("Talk:Test").Test();} + @Test public void Subj_txt() {fxt.Init_ttl("Help talk:test") .Expd_subj_txt("Help:Test").Test();} + @Test public void Subj_txt_identity() {fxt.Init_ttl("Help:test") .Expd_subj_txt("Help:Test").Test();} + @Test public void Subj_url() {fxt.Init_ttl("Help talk:a & b").Expd_subj_url("Help:A_%26_b").Test();} + @Test public void Subj_txt_main() {fxt.Init_ttl("Talk:test") .Expd_subj_txt("Test").Test();} + @Test public void Force_literal_link_y() {fxt.Init_ttl(":Help:test") .Expd_force_literal_link(1).Expd_page_txt("Test").Test();} + @Test public void Force_literal_link_n() {fxt.Init_ttl( "Help:test") .Expd_force_literal_link(0).Expd_page_txt("Test").Test();} + @Test public void Force_literal_link_y_2() {fxt.Init_ttl("::Help:test") .Expd_force_literal_link(1).Expd_page_txt("Test").Test();} // PURPOSE: 2nd initial colon should be ignored; EX:mw:MediaWiki; [[::MediaWiki]]; DATE:2013-12-14 + @Test public void All_page() {fxt.Init_ttl("test") .Expd_xwik_txt("").Expd_ns_id(Xow_ns_.Id_main).Expd_page_txt("Test").Expd_leaf_txt("Test").Expd_anch_txt("").Test();} + @Test public void All_ns() {fxt.Init_ttl("Help:test") .Expd_xwik_txt("").Expd_ns_id(Xow_ns_.Id_help).Expd_page_txt("Test").Expd_leaf_txt("Test").Expd_anch_txt("").Test();} + @Test public void Special() {fxt.Init_ttl("Special:Random").Expd_ns_id(Xow_ns_.Id_special).Expd_page_txt("Random").Test();} + @Test public void Special_xowa() {fxt.Init_ttl("Special:xowa/Search/Ttl").Expd_ns_id(Xow_ns_.Id_special).Expd_page_txt("Xowa/Search/Ttl").Test();} + @Test public void Comment() {fxt.Init_ttl("Ab").Expd_page_txt("Ab").Test();} + @Test public void Comment_eos() {fxt.Init_ttl("Ab") + , Br = Bry_.new_ascii_("
") + + , B_bgn = Bry_.new_ascii_(""), B_end = Bry_.new_ascii_("") + , I_bgn = Bry_.new_ascii_(""), I_end = Bry_.new_ascii_("") + , P_bgn = Bry_.new_ascii_("

"), P_end = Bry_.new_ascii_("

") + + , A_bgn = Bry_.new_ascii_("") + , A_bgn_lnke_0_xowa = Bry_.new_ascii_("\">") + , A_end = Bry_.new_ascii_("") + + , Div_bgn_open = Bry_.new_ascii_("
") + + , Img_bgn = Bry_.new_ascii_("") + , Span_bgn = Bry_.new_ascii_("") + + , Pre_bgn = Bry_.new_ascii_("
"), Pre_end = Bry_.new_ascii_("
") + , Pre_bgn_open = Bry_.new_ascii_("") + + , Code_bgn_closed = Bry_.new_ascii_("") + , Code_bgn_open = Bry_.new_ascii_("") + , Title_atr = Bry_.new_ascii_("\" title=\"") + , Id_atr = Bry_.new_ascii_(" id=\"") + , Style_atr = Bry_.new_ascii_(" style=\"") + ; + public static final int Nbsp_int = 160; +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_mgr.java b/400_xowa/src/gplx/xowa/html/Xoh_html_mgr.java new file mode 100644 index 000000000..aea0d7f7e --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_mgr.java @@ -0,0 +1,36 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.html.tidy.*; import gplx.xowa.html.utils.*; +public class Xoh_html_mgr implements GfoInvkAble { + public Xoh_html_mgr(Xoa_app app) { + js_cleaner = new Xoh_js_cleaner(app); + } + public void Init_by_app(Xoa_app app) { + tidy_mgr.Init_by_app(app); + } + public Xop_xatr_whitelist_mgr Whitelist_mgr() {return whitelist_mgr;} private Xop_xatr_whitelist_mgr whitelist_mgr = new Xop_xatr_whitelist_mgr().Ini(); + public Xoh_page_mgr Page_mgr() {return page_mgr;} private Xoh_page_mgr page_mgr = new Xoh_page_mgr(); + public Xoh_tidy_mgr Tidy_mgr() {return tidy_mgr;} private Xoh_tidy_mgr tidy_mgr = new Xoh_tidy_mgr(); + public Xoh_js_cleaner Js_cleaner() {return js_cleaner;} private Xoh_js_cleaner js_cleaner; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_page)) return page_mgr; + else if (ctx.Match(k, Invk_tidy)) return tidy_mgr; + else return GfoInvkAble_.Rv_unhandled; + } private static final String Invk_page = "page", Invk_tidy = "tidy"; +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_tag.java b/400_xowa/src/gplx/xowa/html/Xoh_html_tag.java new file mode 100644 index 000000000..f25781d7d --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_tag.java @@ -0,0 +1,30 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xoh_html_tag { + public static final String + Nde_xowa_title_str = "xowa_title" + , Const_anchor = "#" + ; + + public static final byte[] + Nde_a_bry = Bry_.new_ascii_("a") + , Nde_href_bry = Bry_.new_ascii_("href") + , Nde_xowa_title_bry = Bry_.new_ascii_(Nde_xowa_title_str) + ; +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_wtr.java b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr.java new file mode 100644 index 000000000..733c9cc21 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr.java @@ -0,0 +1,697 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.wikis.*; import gplx.xowa.net.*; +import gplx.xowa.parsers.apos.*; import gplx.xowa.parsers.amps.*; import gplx.xowa.parsers.lnkes.*; +import gplx.xowa.xtns.*; import gplx.xowa.xtns.dynamicPageList.*; import gplx.xowa.xtns.math.*; import gplx.xowa.langs.vnts.*; import gplx.xowa.xtns.refs.*; +public class Xoh_html_wtr { + private Xow_wiki wiki; private Xoa_app app; private Xoa_page page; private Xop_xatr_whitelist_mgr whitelist_mgr; + public Xoh_html_wtr(Xow_wiki wiki, Xow_html_mgr html_mgr) { + this.wiki = wiki; this.app = wiki.App(); this.whitelist_mgr = app.Html_mgr().Whitelist_mgr(); + lnki_wtr = new Xoh_lnki_wtr(this, wiki, html_mgr, cfg); + ref_wtr = new Ref_html_wtr(wiki); + } + public void Init_by_wiki(Xow_wiki wiki) { + cfg.Toc_show_(true).Lnki_title_(true).Lnki_visited_(true).Lnki_id_(true); // NOTE: set during Init_by_wiki, b/c all tests assume these are false + ref_wtr.Init_by_wiki(wiki); + } + public Xoh_html_wtr_cfg Cfg() {return cfg;} private Xoh_html_wtr_cfg cfg = new Xoh_html_wtr_cfg(); + public Xoh_lnki_wtr Lnki_wtr() {return lnki_wtr;} private Xoh_lnki_wtr lnki_wtr; + public Ref_html_wtr Ref_wtr() {return ref_wtr;} private Ref_html_wtr ref_wtr; + public void Init_by_page(Xop_ctx ctx, Xoa_page page) {this.page = page; lnki_wtr.Init_by_page(ctx, page);} + public void Write_all(Bry_bfr bfr, Xop_ctx ctx, byte[] src, Xop_root_tkn root) {Write_all(bfr, ctx, Xoh_html_wtr_ctx.Basic, src, root);} + public void Write_all(Bry_bfr bfr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_root_tkn root) { + try { + indent_level = 0; this.page = ctx.Cur_page(); + page.Xwiki_langs().Clear(); // HACK: always clear langs; necessary for reload + lnki_wtr.Init_by_page(ctx, ctx.Cur_page()); + Write_tkn(bfr, ctx, hctx, src, null, -1, root); + } + finally { + page.Category_list_(page.Html_data().Ctgs_to_ary()); + } + } + public void Write_tkn_ary(Bry_bfr bfr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_tkn_itm[] ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) { + Xop_tkn_itm itm = ary[i]; + Write_tkn(bfr, ctx, hctx, src, itm, i, itm); + } + } + public void Write_tkn(Bry_bfr bfr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_tkn_grp grp, int sub_idx, Xop_tkn_itm tkn) { + if (tkn.Ignore()) return; + switch (tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_arg_itm: + case Xop_tkn_itm_.Tid_root: + int subs_len = tkn.Subs_len(); + for (int i = 0; i < subs_len; i++) + Write_tkn(bfr, ctx, hctx, src, tkn, i, tkn.Subs_get(i)); + break; + case Xop_tkn_itm_.Tid_ignore: break; + case Xop_tkn_itm_.Tid_html_ncr: Html_ncr(ctx, hctx, bfr, src, (Xop_amp_tkn_num)tkn); break; + case Xop_tkn_itm_.Tid_html_ref: Html_ref(ctx, hctx, bfr, src, (Xop_amp_tkn_txt)tkn); break; + case Xop_tkn_itm_.Tid_hr: Hr(ctx, hctx, bfr, src, (Xop_hr_tkn)tkn); break; + case Xop_tkn_itm_.Tid_hdr: Hdr(ctx, hctx, bfr, src, (Xop_hdr_tkn)tkn); break; + case Xop_tkn_itm_.Tid_apos: Apos(ctx, hctx, bfr, src, (Xop_apos_tkn)tkn); break; + case Xop_tkn_itm_.Tid_lnke: Lnke(ctx, hctx, bfr, src, (Xop_lnke_tkn)tkn); break; + case Xop_tkn_itm_.Tid_lnki: lnki_wtr.Write(bfr, hctx, src, (Xop_lnki_tkn)tkn); break; + case Xop_tkn_itm_.Tid_list: List(ctx, hctx, bfr, src, (Xop_list_tkn)tkn); break; + case Xop_tkn_itm_.Tid_xnde: Xnde(ctx, hctx, bfr, src, (Xop_xnde_tkn)tkn); break; + case Xop_tkn_itm_.Tid_under: Under(ctx, hctx, bfr, src, (Xop_under_tkn)tkn); break; + case Xop_tkn_itm_.Tid_tblw_tb: Tblw(ctx, hctx, bfr, src, (Xop_tblw_tkn)tkn, Tag_tblw_tb_bgn_atr, Tag_tblw_tb_end, true); break; + case Xop_tkn_itm_.Tid_tblw_tr: Tblw(ctx, hctx, bfr, src, (Xop_tblw_tkn)tkn, Tag_tblw_tr_bgn_atr, Tag_tblw_tr_end, false); break; + case Xop_tkn_itm_.Tid_tblw_td: Tblw(ctx, hctx, bfr, src, (Xop_tblw_tkn)tkn, Tag_tblw_td_bgn_atr, Tag_tblw_td_end, false); break; + case Xop_tkn_itm_.Tid_tblw_th: Tblw(ctx, hctx, bfr, src, (Xop_tblw_tkn)tkn, Tag_tblw_th_bgn_atr, Tag_tblw_th_end, false); break; + case Xop_tkn_itm_.Tid_tblw_tc: Tblw(ctx, hctx, bfr, src, (Xop_tblw_tkn)tkn, Tag_tblw_tc_bgn_atr, Tag_tblw_tc_end, false); break; + case Xop_tkn_itm_.Tid_para: Para(ctx, hctx, bfr, src, (Xop_para_tkn)tkn); break; + case Xop_tkn_itm_.Tid_space: Space(ctx, hctx, bfr, src, grp, sub_idx, (Xop_space_tkn)tkn); break; + case Xop_tkn_itm_.Tid_pre: Pre(ctx, hctx, bfr, src, (Xop_pre_tkn)tkn); break; + case Xop_tkn_itm_.Tid_newLine: NewLine(ctx, hctx, bfr, src, (Xop_nl_tkn)tkn); break; + case Xop_tkn_itm_.Tid_bry: Bry(ctx, hctx, bfr, src, (Xop_bry_tkn)tkn); break; + case Xop_tkn_itm_.Tid_vnt: Vnt(ctx, hctx, bfr, src, (Xop_vnt_tkn)tkn); break; +// case Xop_tkn_itm_.Tid_tab: bfr.Add_byte_repeat(Byte_ascii.Tab, tkn.Src_end() - tkn.Src_bgn()); break; + default: + Xoh_html_wtr_escaper.Escape(app, bfr, src, tkn.Src_bgn(), tkn.Src_end(), true, false); // NOTE: always escape text including (a) lnki_alt text; and (b) any other text, especially failed xndes; DATE:2013-06-18 + break; + } + } + @gplx.Virtual public void Html_ncr(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_amp_tkn_num tkn) { + if (tkn.Val() < Byte_ascii.Max_7_bit) // NOTE: must "literalize"; converts wiki chars to ncrs, which must be "literalized: EX: [[A]] -> \\A]] which must be converted back to [[A]] + bfr.Add(tkn.Str_as_bry()); + else + bfr.Add_byte(Byte_ascii.Amp).Add_byte(Byte_ascii.Hash).Add_int_variable(tkn.Val()).Add_byte(Byte_ascii.Semic); // NOTE: do not literalize, else browser may not display multi-char bytes properly; EX:   gets added as   not as {192,160}; DATE:2013-12-09 + } + @gplx.Virtual public void Html_ref(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_amp_tkn_txt tkn) {tkn.Print_to_html(bfr);} + private static final byte[] Bry_hdr_bgn = Bry_.new_ascii_(" 0) + bfr.Add_byte_repeat(Byte_ascii.Apos, literal_apos); + switch (apos.Apos_cmd()) { + case Xop_apos_tkn_.Cmd_b_bgn: bfr.Add(Xoh_consts.B_bgn); break; + case Xop_apos_tkn_.Cmd_b_end: bfr.Add(Xoh_consts.B_end); break; + case Xop_apos_tkn_.Cmd_i_bgn: bfr.Add(Xoh_consts.I_bgn); break; + case Xop_apos_tkn_.Cmd_i_end: bfr.Add(Xoh_consts.I_end); break; + case Xop_apos_tkn_.Cmd_bi_bgn: bfr.Add(Xoh_consts.B_bgn).Add(Xoh_consts.I_bgn); break; + case Xop_apos_tkn_.Cmd_ib_end: bfr.Add(Xoh_consts.I_end).Add(Xoh_consts.B_end); break; + case Xop_apos_tkn_.Cmd_ib_bgn: bfr.Add(Xoh_consts.I_bgn).Add(Xoh_consts.B_bgn); break; + case Xop_apos_tkn_.Cmd_bi_end: bfr.Add(Xoh_consts.B_end).Add(Xoh_consts.I_end);; break; + case Xop_apos_tkn_.Cmd_bi_end__b_bgn: bfr.Add(Xoh_consts.B_end).Add(Xoh_consts.I_end).Add(Xoh_consts.B_bgn); break; + case Xop_apos_tkn_.Cmd_ib_end__i_bgn: bfr.Add(Xoh_consts.I_end).Add(Xoh_consts.B_end).Add(Xoh_consts.I_bgn); break; + case Xop_apos_tkn_.Cmd_b_end__i_bgn: bfr.Add(Xoh_consts.B_end).Add(Xoh_consts.I_bgn); break; + case Xop_apos_tkn_.Cmd_i_end__b_bgn: bfr.Add(Xoh_consts.I_end).Add(Xoh_consts.B_bgn); break; + case Xop_apos_tkn_.Cmd_nil: break; + default: throw Err_.unhandled(apos.Apos_cmd()); + } + } + private void Lnke(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_lnke_tkn lnke) { + int lnke_bgn = lnke.Lnke_bgn(), lnke_end = lnke.Lnke_end(); + byte[] lnke_xwiki_wiki = lnke.Lnke_xwiki_wiki(); + boolean proto_is_xowa = lnke.Proto_tid() == Xoo_protocol_itm.Tid_xowa; + if (!hctx.Mode_is_alt()) { + if (lnke_xwiki_wiki == null) { + if (lnke.Lnke_relative()) // relative; EX: //a.org + bfr.Add(Xoh_consts.A_bgn).Add(app.Url_parser().Url_parser().Relative_url_protocol_bry()).Add_mid(src, lnke_bgn, lnke_end).Add(Xoh_consts.A_bgn_lnke_0); + else { // xowa or regular; EX: http://a.org + if (proto_is_xowa) { + bfr.Add(Xoh_consts.A_bgn).Add(Xop_lnke_wkr.Bry_xowa_protocol); + ctx.App().Url_converter_gfs().Encode(bfr, src, lnke_bgn, lnke_end); + bfr.Add(Xoh_consts.A_bgn_lnke_0_xowa); + } + else // regular; add href + bfr.Add(Xoh_consts.A_bgn).Add_mid(src, lnke_bgn, lnke_end).Add(Xoh_consts.A_bgn_lnke_0); + } + } + else { + bfr.Add(Xoh_consts.A_bgn).Add(Xoh_href_parser.Href_site_bry).Add(lnke_xwiki_wiki).Add(Xoh_href_parser.Href_wiki_bry).Add(lnke.Lnke_xwiki_page()); + if (lnke.Lnke_xwiki_qargs() != null) + Xoa_url_arg_hash.Concat_bfr(bfr, lnke.Lnke_xwiki_qargs()); + bfr.Add(Xoh_consts.__end_quote); + } + } + int subs_len = lnke.Subs_len(); + if (subs_len == 0) { // no text; auto-number; EX: "[1]" + if (lnke.Lnke_typ() == Xop_lnke_tkn.Lnke_typ_text) + bfr.Add_mid(src, lnke.Lnke_bgn(), lnke.Lnke_end()); + else + bfr.Add_byte(Byte_ascii.Brack_bgn).Add_int_variable(page.Html_data().Lnke_autonumber_next()).Add_byte(Byte_ascii.Brack_end); + } + else { // text available + for (int i = 0; i < subs_len; i++) + Write_tkn(bfr, ctx, hctx, src, lnke, i, lnke.Subs_get(i)); + } + if (!hctx.Mode_is_alt()) { + if (proto_is_xowa) // add + bfr.Add(Xoh_consts.Img_bgn).Add(wiki.Html_mgr().Img_xowa_protocol()).Add(Xoh_consts.__inline_quote); + bfr.Add(Xoh_consts.A_end); + } + } + public static byte[] Ttl_to_title(byte[] ttl) {return ttl;} // FUTURE: swap html chars? + public void List(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_list_tkn list) { + if (hctx.Mode_is_alt()) { // alt; add literally; EX: "*" for "\n*"; note that \n is added in NewLine() + if (list.List_bgn() == Bool_.Y_byte) { // bgn tag + bfr.Add_byte(list.List_itmTyp()); // add literal byte + } + else {} // end tag; ignore + } + else { + byte list_itm_type = list.List_itmTyp(); + if (list.List_bgn() == Bool_.Y_byte) { + if (list.List_sub_first()) List_grp_bgn(ctx, hctx, bfr, src, list_itm_type); + List_itm_bgn(ctx, hctx, bfr, src, list_itm_type); + } + else { + List_itm_end(ctx, hctx, bfr, src, list_itm_type); + if (list.List_sub_last() == Bool_.Y_byte) List_grp_end(ctx, hctx, bfr, src, list_itm_type); + } + } + } + @gplx.Virtual public void List_grp_bgn(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, byte type) { + byte[] tag = null; + switch (type) { + case Xop_list_tkn_.List_itmTyp_ol: tag = Tag_list_grp_ol_bgn; break; + case Xop_list_tkn_.List_itmTyp_ul: tag = Tag_list_grp_ul_bgn; break; + case Xop_list_tkn_.List_itmTyp_dd: + case Xop_list_tkn_.List_itmTyp_dt: tag = Tag_list_grp_dl_bgn; break; + default: throw Err_.unhandled(type); + } + if (bfr.Len() > 0) bfr.Add_byte_nl(); // NOTE: do not add newLine if start + if (indent_level > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent_level * 2); + bfr.Add(tag); + ++indent_level; + } + @gplx.Virtual public void List_itm_bgn(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, byte type) { + byte[] tag = null; + switch (type) { + case Xop_list_tkn_.List_itmTyp_ol: + case Xop_list_tkn_.List_itmTyp_ul: tag = Tag_list_itm_li_bgn; break; + case Xop_list_tkn_.List_itmTyp_dt: tag = Tag_list_itm_dt_bgn; break; + case Xop_list_tkn_.List_itmTyp_dd: tag = Tag_list_itm_dd_bgn; break; + default: throw Err_.unhandled(type); + } + bfr.Add_byte_nl(); + if (indent_level > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent_level * 2); + bfr.Add(tag); + ++indent_level; + } + @gplx.Virtual public void List_grp_end(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, byte type) { + --indent_level; + byte[] tag = null; + switch (type) { + case Xop_list_tkn_.List_itmTyp_ol: tag = Tag_list_grp_ol_end; break; + case Xop_list_tkn_.List_itmTyp_ul: tag = Tag_list_grp_ul_end; break; + case Xop_list_tkn_.List_itmTyp_dd: + case Xop_list_tkn_.List_itmTyp_dt: tag = Tag_list_grp_dl_end; break; + default: throw Err_.unhandled(type); + } + bfr.Add_byte_nl(); + if (indent_level > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent_level * 2); + bfr.Add(tag); + } + + @gplx.Virtual public void List_itm_end(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, byte type) { + --indent_level; + byte[] tag = null; + switch (type) { + case Xop_list_tkn_.List_itmTyp_ol: + case Xop_list_tkn_.List_itmTyp_ul: tag = Tag_list_itm_li_end; break; + case Xop_list_tkn_.List_itmTyp_dt: tag = Tag_list_itm_dt_end; break; + case Xop_list_tkn_.List_itmTyp_dd: tag = Tag_list_itm_dd_end; break; + default: throw Err_.unhandled(type); + } + bfr.Add_byte_if_not_last(Byte_ascii.NewLine); + if (indent_level > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent_level * 2); + bfr.Add(tag); + } + @gplx.Virtual public void NewLine(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_nl_tkn tkn) { + if (hctx.Mode_is_alt()) + bfr.Add_byte_space(); + else { + if (tkn.Nl_tid() == Xop_nl_tkn.Tid_char) { + bfr.Add_byte_if_not_last(Byte_ascii.NewLine); + } + } + } + public void Space(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_tkn_grp grp, int sub_idx, Xop_space_tkn space) { + bfr.Add_byte_repeat(Byte_ascii.Space, space.Src_end_grp(grp, sub_idx) - space.Src_bgn_grp(grp, sub_idx)); // NOTE: lnki.caption will convert \n to \s; see Xop_nl_lxr; EX.WP: Schwarzschild radius + } + @gplx.Virtual public void Para(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_para_tkn para) { + if (para.Nl_bgn() && bfr.Len() > 0) { + if (hctx.Mode_is_alt()) + bfr.Add_byte_space(); + else + bfr.Add_byte_if_not_last(Byte_ascii.NewLine); + } + switch (para.Para_end()) { + case Xop_para_tkn.Tid_none: break; + case Xop_para_tkn.Tid_para: bfr.Add(Tag_para_end).Add_byte_nl(); break; + case Xop_para_tkn.Tid_pre: bfr.Add(Tag_pre_end).Add_byte_nl(); break; + default: throw Err_.unhandled(para.Para_end()); + } + switch (para.Para_bgn()) { + case Xop_para_tkn.Tid_none: break; + case Xop_para_tkn.Tid_para: Para_assert_tag_starts_on_nl(bfr, para.Src_bgn()); bfr.Add(Tag_para_bgn); break; + case Xop_para_tkn.Tid_pre: Para_assert_tag_starts_on_nl(bfr, para.Src_bgn()); bfr.Add(Tag_pre_bgn); break; + default: throw Err_.unhandled(para.Para_bgn()); + } + if (para.Space_bgn() > 0) + bfr.Add_byte_repeat(Byte_ascii.Space, para.Space_bgn()); + } + private void Para_assert_tag_starts_on_nl(Bry_bfr bfr, int src_bgn) { + if (!bfr.Match_end_byt_nl_or_bos()) bfr.Add_byte_nl(); + if (src_bgn != 0) bfr.Add_byte_nl(); + } + @gplx.Virtual public void Pre(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_pre_tkn pre) { + switch (pre.Pre_tid()) { + case Xop_pre_tkn.Pre_tid_bgn: + bfr.Add(Tag_pre_bgn); + break; + case Xop_pre_tkn.Pre_tid_end: + bfr.Add_byte_nl().Add(Tag_pre_end).Add_byte_nl().Add_byte_nl(); + break; + } + } + @gplx.Virtual public void Bry(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_bry_tkn bry) { + bfr.Add(bry.Val()); + } + @gplx.Virtual public void Vnt(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_vnt_tkn vnt) { + Xop_vnt_html_wtr.Write(this, ctx, hctx, bfr, src, vnt); // NOTE: using wiki, b/c getting nullPointer with ctx during mass parse + } + @gplx.Virtual public void Under(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_under_tkn under) { + if (hctx.Mode_is_alt()) return; + switch (under.Under_tid()) { + case Xol_kwd_grp_.Id_toc: + wiki.Html_mgr().Toc_mgr().Html(page, src, bfr); + break; + case Xol_kwd_grp_.Id_notoc: case Xol_kwd_grp_.Id_forcetoc: // NOTE: skip output; changes flag on page only + break; + } + } + @gplx.Virtual public void Xnde(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_xnde_tkn xnde) { + if (hctx.Mode_is_alt()) { + if (xnde.Tag_close_bgn() > 0) // NOTE: some tags are not closed; WP.EX: France;

+ Xoh_html_wtr_escaper.Escape(app, bfr, src, xnde.Tag_open_end(), xnde.Tag_close_bgn(), true, false); + else + Xnde_subs(ctx, hctx, bfr, src, xnde); + return; + } + Xop_xnde_tag tag = xnde.Tag(); + int tag_id = tag.Id(); + switch (tag_id) { + case Xop_xnde_tag_.Tid_br: + if (xnde.Src_end() - xnde.Src_bgn() < 4 + || xnde.Src_bgn() == -1) + bfr.Add(Tag_br); else bfr.Add_mid(src, xnde.Src_bgn(), xnde.Src_end()); break; + case Xop_xnde_tag_.Tid_hr: bfr.Add(Tag_hr); break; + case Xop_xnde_tag_.Tid_includeonly: // NOTE: do not write tags or content + break; + case Xop_xnde_tag_.Tid_noinclude: // NOTE: do not write tags + case Xop_xnde_tag_.Tid_onlyinclude: + Xnde_subs_escape(ctx, hctx, bfr, src, xnde, false, false); + break; + case Xop_xnde_tag_.Tid_nowiki: + Xnde_subs_escape(ctx, hctx, bfr, src, xnde, false, false); + break; + case Xop_xnde_tag_.Tid_b: case Xop_xnde_tag_.Tid_strong: + case Xop_xnde_tag_.Tid_i: case Xop_xnde_tag_.Tid_em: case Xop_xnde_tag_.Tid_cite: case Xop_xnde_tag_.Tid_dfn: case Xop_xnde_tag_.Tid_var: + case Xop_xnde_tag_.Tid_u: case Xop_xnde_tag_.Tid_ins: case Xop_xnde_tag_.Tid_abbr: + case Xop_xnde_tag_.Tid_strike: case Xop_xnde_tag_.Tid_s: case Xop_xnde_tag_.Tid_del: + case Xop_xnde_tag_.Tid_sub: case Xop_xnde_tag_.Tid_sup: case Xop_xnde_tag_.Tid_big: case Xop_xnde_tag_.Tid_small: + case Xop_xnde_tag_.Tid_code: case Xop_xnde_tag_.Tid_tt: case Xop_xnde_tag_.Tid_kbd: case Xop_xnde_tag_.Tid_samp: case Xop_xnde_tag_.Tid_blockquote: + case Xop_xnde_tag_.Tid_font: case Xop_xnde_tag_.Tid_center: + case Xop_xnde_tag_.Tid_p: case Xop_xnde_tag_.Tid_span: case Xop_xnde_tag_.Tid_div: + case Xop_xnde_tag_.Tid_h1: case Xop_xnde_tag_.Tid_h2: case Xop_xnde_tag_.Tid_h3: case Xop_xnde_tag_.Tid_h4: case Xop_xnde_tag_.Tid_h5: case Xop_xnde_tag_.Tid_h6: + case Xop_xnde_tag_.Tid_dt: case Xop_xnde_tag_.Tid_dd: case Xop_xnde_tag_.Tid_ol: case Xop_xnde_tag_.Tid_ul: case Xop_xnde_tag_.Tid_dl: + case Xop_xnde_tag_.Tid_table: case Xop_xnde_tag_.Tid_tr: case Xop_xnde_tag_.Tid_td: case Xop_xnde_tag_.Tid_th: case Xop_xnde_tag_.Tid_caption: case Xop_xnde_tag_.Tid_tbody: + case Xop_xnde_tag_.Tid_ruby: case Xop_xnde_tag_.Tid_rt: case Xop_xnde_tag_.Tid_rb: case Xop_xnde_tag_.Tid_rp: + case Xop_xnde_tag_.Tid_time: case Xop_xnde_tag_.Tid_bdi: case Xop_xnde_tag_.Tid_data: case Xop_xnde_tag_.Tid_mark: case Xop_xnde_tag_.Tid_wbr: case Xop_xnde_tag_.Tid_bdo: // HTML 5: write literally and let browser handle them + { +// byte[] name = tag.Name_bry(); +// bfr.Add_byte(Tag__bgn).Add(name); +// if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr); +// bfr.Add_byte(Tag__end); +// Xnde_subs(hctx, bfr, src, xnde, depth); // NOTE: do not escape;

, etc may have nested nodes +// bfr.Add(Tag__end_bgn).Add(name).Add_byte(Tag__end); // NOTE: inline is never written as ; will be written as ; SEE: NOTE_1 + Write_xnde(bfr, ctx, hctx, xnde, tag, tag_id, src); + break; + } + case Xop_xnde_tag_.Tid_pre: { + if (xnde.Tag_open_end() == xnde.Tag_close_bgn()) return; // ignore empty tags, else blank pre line will be printed; DATE:2014-03-12 + byte[] name = tag.Name_bry(); + bfr.Add_byte(Tag__bgn).Add(name); + if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr); + bfr.Add_byte(Tag__end); + Xnde_subs_escape(ctx, hctx, bfr, src, xnde, false, true); + bfr.Add(Tag__end_bgn).Add(name).Add_byte(Tag__end); + break; + } + case Xop_xnde_tag_.Tid_li: { + byte[] name = tag.Name_bry(); + int bfr_len = bfr.Len(); + if (bfr_len > 0 && bfr.Bfr()[bfr_len - 1] != Byte_ascii.NewLine) bfr.Add_byte_nl(); // NOTE: always add nl before li else some lists will merge and force long horizontal bar; EX:w:Music + if (xnde.Tag_visible()) { + bfr.Add_byte(Tag__bgn).Add(name); + if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr); + bfr.Add_byte(Tag__end); + } + Xnde_subs(ctx, hctx, bfr, src, xnde); + if (xnde.Tag_visible()) + bfr.Add(Tag__end_bgn).Add(name).Add_byte(Tag__end); // NOTE: inline is never written as ; will be written as ; SEE: NOTE_1 + break; + } + case Xop_xnde_tag_.Tid_timeline: { + bfr.Add_str("
");
+				Xox_mgr_base.Xtn_write_escape(app, bfr, src, xnde.Tag_open_end(), xnde.Tag_close_bgn());	// NOTE: do not embed  tag inside pre, else timeline will render in black; EX:
a
will fail; DATE:2014-05-22 + bfr.Add_str("
"); + break; + } + case Xop_xnde_tag_.Tid_source: { // convert to
;
+				byte[] name = Xop_xnde_tag_.Tag_pre.Name_bry();
+				bfr.Add_byte(Tag__bgn).Add(name);
+				if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr);
+				bfr.Add_byte(Tag__end);
+				int tag_close_bgn = Bry_finder.Find_bwd_while(src, xnde.Tag_close_bgn(), -1, Byte_ascii.Space) + 1;	// trim space from end; PAGE:en.w:Comment_(computer_programming) DATE:2014-06-23
+				Xoh_html_wtr_escaper.Escape(app, bfr, src, xnde.Tag_open_end(), tag_close_bgn, false, false);	//  is a .Xtn(); render literally everything between > and <; DATE:2014-03-11
+				bfr.Add(Tag__end_bgn).Add(name).Add_byte(Tag__end);
+				break;
+			}
+			case Xop_xnde_tag_.Tid_gallery:
+			case Xop_xnde_tag_.Tid_poem:
+			case Xop_xnde_tag_.Tid_hiero:
+			case Xop_xnde_tag_.Tid_score:
+			case Xop_xnde_tag_.Tid_ref:
+			case Xop_xnde_tag_.Tid_references:
+			case Xop_xnde_tag_.Tid_inputBox:
+			case Xop_xnde_tag_.Tid_imageMap:
+			case Xop_xnde_tag_.Tid_pages:
+			case Xop_xnde_tag_.Tid_pagequality:
+			case Xop_xnde_tag_.Tid_pagelist:
+			case Xop_xnde_tag_.Tid_section:
+			case Xop_xnde_tag_.Tid_translate:
+			case Xop_xnde_tag_.Tid_dynamicPageList:
+			case Xop_xnde_tag_.Tid_languages:
+			case Xop_xnde_tag_.Tid_templateData:
+			case Xop_xnde_tag_.Tid_syntaxHighlight:
+			case Xop_xnde_tag_.Tid_listing_buy:
+			case Xop_xnde_tag_.Tid_listing_do:
+			case Xop_xnde_tag_.Tid_listing_drink:
+			case Xop_xnde_tag_.Tid_listing_eat:
+			case Xop_xnde_tag_.Tid_listing_listing:
+			case Xop_xnde_tag_.Tid_listing_see:
+			case Xop_xnde_tag_.Tid_listing_sleep:
+			case Xop_xnde_tag_.Tid_xowa_cmd:
+			case Xop_xnde_tag_.Tid_rss:
+			case Xop_xnde_tag_.Tid_math:
+			case Xop_xnde_tag_.Tid_xowa_html:
+				Xox_xnde xtn = xnde.Xnde_xtn();
+				xtn.Xtn_write(app, this, hctx, ctx, bfr, src, xnde);
+				break;
+			case Xop_xnde_tag_.Tid_xowa_tag_bgn:
+			case Xop_xnde_tag_.Tid_xowa_tag_end:
+				break;
+			default:	// unknown tag
+				if (tag.Restricted()) {	// a; img; script; etc..
+					if (	!page.Html_data().Restricted()								// page is not marked restricted (only [[Special:]])
+						||	page.Wiki().Domain_tid() == Xow_wiki_domain_.Tid_home) {	// page is in home wiki
+						bfr.Add_mid(src, xnde.Src_bgn(), xnde.Src_end());
+						return;
+					}
+				}
+				bfr.Add(Ary_escape_bgn).Add(tag.Name_bry());	// escape bgn
+				if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr);
+				switch (xnde.CloseMode()) {
+					case Xop_xnde_tkn.CloseMode_inline:
+						bfr.Add_byte(Byte_ascii.Slash).Add(Ary_escape_end);
+						break;
+					default:	// NOTE: close tag, even if dangling; EX: 
a ->
a
+ bfr.Add_byte(Byte_ascii.Gt); + Xnde_subs(ctx, hctx, bfr, src, xnde); + bfr.Add(Ary_escape_bgn).Add_byte(Byte_ascii.Slash).Add(tag.Name_bry()).Add(Ary_escape_end); + break; + } + break; + } + } + private void Write_xnde(Bry_bfr bfr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Xop_xnde_tkn xnde, Xop_xnde_tag tag, int tag_id, byte[] src) { + byte[] name = tag.Name_bry(); + boolean at_bgn = true; + Bry_bfr ws_bfr = wiki.Utl_bry_bfr_mkr().Get_b512(); // create separate ws_bfr to handle "a c d" -> "a c d" + int subs_len = xnde.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Xop_tkn_itm sub = xnde.Subs_get(i); + byte tkn_tid = sub.Tkn_tid(); + switch (tkn_tid) { + case Xop_tkn_itm_.Tid_space: // space; add to ws_bfr; + ws_bfr.Add_mid(src, sub.Src_bgn(), sub.Src_end()); + break; + default: + if (tkn_tid == Xop_tkn_itm_.Tid_html_ncr) { // html_entity needed for fr.wikipedia.org and many spans with ; DATE:2013-06-18 + Xop_amp_tkn_num ncr_tkn = (Xop_amp_tkn_num)sub; + if (ncr_tkn.Val() == Byte_ascii.Space + || ncr_tkn.Val() == 160 + ) { + + ws_bfr.Add_mid(src, ncr_tkn.Src_bgn(), ncr_tkn.Src_end()); + continue; // just add entity; don't process rest; + } + } + if (ws_bfr.Len() > 0) bfr.Add_bfr_and_clear(ws_bfr); // dump ws_bfr to real bfr + if (at_bgn) { // 1st non-ws tkn; add open tag; + at_bgn = false; + bfr.Add_byte(Tag__bgn).Add(name); + if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr); + bfr.Add_byte(Tag__end); + } + Write_tkn(bfr, ctx, hctx, src, xnde, i, sub); // NOTE: never escape;

,

,
etc may have nested nodes + break; + } + } + if (at_bgn) { // occurs when xnde is empty; EX: + bfr.Add_byte(Tag__bgn).Add(name); + if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(tag_id, hctx, src, xnde.Atrs_bgn(), xnde.Atrs_end(), xnde.Atrs_ary(), bfr); + bfr.Add_byte(Tag__end); + } + bfr.Add(Tag__end_bgn).Add(name).Add_byte(Tag__end); // NOTE: inline is never written as ; will be written as ; SEE: NOTE_1 + if (ws_bfr.Len() > 0) bfr.Add_bfr_and_clear(ws_bfr); // dump any leftover ws to bfr; handles "c " -> "c " + ws_bfr.Mkr_rls(); + } + public void Xnde_atrs(int tag_id, Xoh_html_wtr_ctx hctx, byte[] src, int bgn, int end, Xop_xatr_itm[] ary, Bry_bfr bfr) { + if (ary == null) return; // NOTE: some nodes will have null xatrs b/c of whitelist; EX:
a
; style is not on whitelist so not xatr generated, but xatr_bgn will != -1 + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) { + Xop_xatr_itm atr = ary[i]; + if (atr.Invalid()) continue; + if (!whitelist_mgr.Chk(tag_id, src, atr)) continue; + Xnde_atr_write(bfr, app, hctx, src, atr); + } + } + + public static void Xnde_atr_write(Bry_bfr bfr, Xoa_app app, Xoh_html_wtr_ctx hctx, byte[] src, Xop_xatr_itm atr) { + byte[] atr_key = atr.Key_bry(); + if ( hctx.Mode_is_display_title() + && Xoh_display_ttl_wtr._.Is_style_restricted(bfr, hctx, src, atr, atr_key)) + return; + + bfr.Add_byte(Byte_ascii.Space); // add space before every attribute + if (atr_key != null) { + bfr.Add(atr_key); + bfr.Add_byte(Byte_ascii.Eq); + } + byte quote_byte = atr.Quote_byte(); if (quote_byte == Byte_ascii.Nil) quote_byte = Byte_ascii.Quote; + bfr.Add_byte(quote_byte); + if (atr.Key_tid() == Xop_xatr_itm.Key_tid_id) { // ids should not have spaces; DATE:2013-04-01 + if (atr.Val_bry() == null) + Xnde_atr_write_id(bfr, app, src, atr.Val_bgn(), atr.Val_end()); + else { + byte[] atr_val = atr.Val_bry(); + Xnde_atr_write_id(bfr, app, atr_val, 0, atr_val.length); + } + } + else { + if (atr.Val_bry() == null) + bfr.Add_mid(src, atr.Val_bgn(), atr.Val_end()); + else + bfr.Add(atr.Val_bry()); + } + bfr.Add_byte(quote_byte); + } + private static void Xnde_atr_write_id(Bry_bfr bfr, Xoa_app app, byte[] bry, int bgn, int end) { + app.Url_converter_id().Encode(bfr, bry, bgn, end); + } + private void Xnde_subs(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_xnde_tkn xnde) { + int subs_len = xnde.Subs_len(); + for (int i = 0; i < subs_len; i++) + Write_tkn(bfr, ctx, hctx, src, xnde, i, xnde.Subs_get(i)); + } + private void Xnde_subs_escape(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_xnde_tkn xnde, boolean amp_enable, boolean nowiki) { + int xndesubs_len = xnde.Subs_len(); + for (int i = 0; i < xndesubs_len; i++) { + Xop_tkn_itm sub = xnde.Subs_get(i); + switch (sub.Tkn_tid()) { + case Xop_tkn_itm_.Tid_xnde: + Xop_xnde_tkn sub_xnde = (Xop_xnde_tkn)sub; + switch (sub_xnde.Tag().Id()) { + case Xop_xnde_tag_.Tid_noinclude: + case Xop_xnde_tag_.Tid_onlyinclude: + case Xop_xnde_tag_.Tid_includeonly: + break; + default: + byte[] tag_name = sub_xnde.Tag().Name_bry(); + bfr.Add(Xop_xnde_wkr.Bry_escape_lt).Add(tag_name); + if (xnde.Atrs_bgn() > Xop_tblw_wkr.Atrs_ignore_check) Xnde_atrs(sub_xnde.Tag().Id(), hctx, src, sub_xnde.Atrs_bgn(), sub_xnde.Atrs_end(), xnde.Atrs_ary(), bfr); + bfr.Add(Xop_xnde_wkr.Bry_escape_gt); + break; + } + Xnde_subs_escape(ctx, hctx, bfr, src, sub_xnde, amp_enable, false); + break; + case Xop_tkn_itm_.Tid_txt: + if (amp_enable) + bfr.Add_mid(src, sub.Src_bgn(), sub.Src_end()); + else + Xoh_html_wtr_escaper.Escape(app, bfr, src, sub.Src_bgn(), sub.Src_end(), true, nowiki); + break; + default: + Write_tkn(bfr, ctx, hctx, src, xnde, i, sub); + break; + } + } + } + public Bool_obj_ref Queue_add_ref() {return queue_add_ref;} Bool_obj_ref queue_add_ref = Bool_obj_ref.n_(); + public void Tblw(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_tblw_tkn tkn, byte[] bgn, byte[] end, boolean tblw_bgn) { + if (hctx.Mode_is_alt()) // add \s for each \n + bfr.Add_byte_space(); + else { + bfr.Add_byte_if_not_last(Byte_ascii.NewLine); + if (indent_level > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent_level * 2); +// boolean para_mode = tblw_bgn && tbl_para && depth == 1; // DELETE: old code; adding

to handle strange mozilla key down behavior on linux; DATE:2013-03-30 +// if (para_mode) {bfr.Add(Xoh_consts.P_bgn);} + + bfr.Add(bgn); + int atrs_bgn = tkn.Atrs_bgn(); + if (atrs_bgn != -1) Xnde_atrs(tkn.Tblw_tid(), hctx, src, atrs_bgn, tkn.Atrs_end(), tkn.Atrs_ary(), bfr); //bfr.Add_byte(Byte_ascii.Space).Add_mid(src, atrs_bgn, tkn.Atrs_end()); + bfr.Add_byte(Tag__end); + ++indent_level; + } + int subs_len = tkn.Subs_len(); + for (int i = 0; i < subs_len; i++) + Write_tkn(bfr, ctx, hctx, src, tkn, i, tkn.Subs_get(i)); + if (hctx.Mode_is_alt()) { + if (tblw_bgn) // only add \s for closing table; |} -> "\s" + bfr.Add_byte_space(); + } + else { + --indent_level; + bfr.Add_byte_if_not_last(Byte_ascii.NewLine); + if (indent_level > 0) bfr.Add_byte_repeat(Byte_ascii.Space, indent_level * 2); + bfr.Add(end); +// if (para_mode) {bfr.Add(Xoh_consts.P_end);} // DELETE: old code; adding

to handle strange mozilla key down behavior on linux; DATE:2013-03-30 + bfr.Add_byte_if_not_last(Byte_ascii.NewLine); +// bfr.Add_byte_nl(); + } + } + public static final byte[] Tag__end_quote = Bry_.new_ascii_("\">"), Tag__end_bgn = Bry_.new_ascii_(""), Tag_br = Bry_.new_ascii_("
") + , Tag_list_grp_ul_bgn = Bry_.new_ascii_("

    "), Tag_list_grp_ul_end = Bry_.new_ascii_("
") + , Tag_list_grp_ol_bgn = Bry_.new_ascii_("
    "), Tag_list_grp_ol_end = Bry_.new_ascii_("
") + , Tag_list_itm_li_bgn = Bry_.new_ascii_("
  • "), Tag_list_itm_li_end = Bry_.new_ascii_("
  • ") + , Tag_list_itm_dt_bgn = Bry_.new_ascii_("
    "), Tag_list_itm_dt_end = Bry_.new_ascii_("
    ") + , Tag_list_itm_dd_bgn = Bry_.new_ascii_("
    "), Tag_list_itm_dd_end = Bry_.new_ascii_("
    ") + , Tag_list_grp_dl_bgn = Bry_.new_ascii_("
    "), Tag_list_grp_dl_end = Bry_.new_ascii_("
    ") + , File_divider = Bry_.new_ascii_("---------------------------------") + , Tag_tblw_tb_bgn = Bry_.new_ascii_("
    "), Tag_tblw_tb_bgn_atr = Bry_.new_ascii_("") + , Tag_tblw_tr_bgn = Bry_.new_ascii_(""), Tag_tblw_tr_bgn_atr = Bry_.new_ascii_("") + , Tag_tblw_td_bgn = Bry_.new_ascii_("" + , "|}" + ) + , String_.Concat_lines_nl + ( "
    "), Tag_tblw_td_bgn_atr = Bry_.new_ascii_("") + , Tag_tblw_th_bgn = Bry_.new_ascii_(""), Tag_tblw_th_bgn_atr = Bry_.new_ascii_("") + , Tag_tblw_tc_bgn = Bry_.new_ascii_("
    "), Tag_tblw_tc_bgn_atr = Bry_.new_ascii_("") + , Ary_escape_bgn = Bry_.new_ascii_("<"), Ary_escape_end = Bry_.new_ascii_(">"), Ary_escape_end_bgn = Bry_.new_ascii_("</") + , Tag_para_bgn = Bry_.new_ascii_("

    "), Tag_para_end = Bry_.new_ascii_("

    "), Tag_para_mid = Bry_.new_ascii_("

    \n\n

    ") + , Tag_image_end = Bry_.new_ascii_("") + , Tag_pre_bgn = Bry_.new_ascii_("

    "), Tag_pre_end = Bry_.new_ascii_("
    ") + ; + public static final byte Tag__bgn = Byte_ascii.Lt, Tag__end = Byte_ascii.Gt; + public static final byte Dir_spr_http = Byte_ascii.Slash; + private int indent_level = 0; + public static final int Sub_idx_null = -1; // nonsense placeholder +} +class Xoh_display_ttl_wtr { + private static final byte[] + Atr_key_style = Bry_.new_ascii_("style") + , Msg_style_restricted = Bry_.new_ascii_(" style='/* attempt to bypass $wgRestrictDisplayTitle */'") + ; + private ByteTrieMgr_slim style_trie = ByteTrieMgr_slim.ci_ascii_() + .Add_str_byte__many(Byte_.int_(0), "display", "user-select", "visibility"); // if ( preg_match( '/(display|user-select|visibility)\s*:/i', $decoded['style'] ) ) { + public boolean Is_style_restricted(Bry_bfr bfr, Xoh_html_wtr_ctx hctx, byte[] src, Xop_xatr_itm atr, byte[] atr_key) { + if (atr_key != null + && Bry_.Eq(atr_key, Atr_key_style) + ) { + byte[] atr_val = atr.Val_as_bry(src); if (atr_val == null) return false; // bounds_chk + int atr_val_len = atr_val.length; + int atr_pos = 0; + while (atr_pos < atr_val_len) { + byte b = atr_val[atr_pos]; + Object o = style_trie.Match(b, atr_val, atr_pos, atr_val_len); + if (o != null) { + bfr.Add(Msg_style_restricted); + return true; + } + ++atr_pos; + } + } + return false; + } + public static final Xoh_display_ttl_wtr _ = new Xoh_display_ttl_wtr(); Xoh_display_ttl_wtr() {} +} +/* +NOTE_1:inline always written as , not +see WP:Permian�Triassic extinction event +this will cause firefox to swallow up rest of text +
    +this will not +
    +*/ \ No newline at end of file diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_cfg.java b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_cfg.java new file mode 100644 index 000000000..de0abe7f4 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_cfg.java @@ -0,0 +1,24 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xoh_html_wtr_cfg { + public boolean Toc_show() {return toc_show;} public Xoh_html_wtr_cfg Toc_show_(boolean v) {toc_show = v; return this;} private boolean toc_show; + public boolean Lnki_id() {return lnki_id;} public Xoh_html_wtr_cfg Lnki_id_(boolean v) {lnki_id = v; return this;} private boolean lnki_id; + public boolean Lnki_title() {return lnki_title;} public Xoh_html_wtr_cfg Lnki_title_(boolean v) {lnki_title = v; return this;} private boolean lnki_title; + public boolean Lnki_visited() {return lnki_visited;} public Xoh_html_wtr_cfg Lnki_visited_(boolean v) {lnki_visited = v; return this;} private boolean lnki_visited; +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_ctx.java b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_ctx.java new file mode 100644 index 000000000..0a83ab14a --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_ctx.java @@ -0,0 +1,31 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xoh_html_wtr_ctx { + Xoh_html_wtr_ctx(byte mode) {this.mode = mode;} private byte mode; + public boolean Mode_is_alt() {return mode == Mode_alt;} + public boolean Mode_is_display_title() {return mode == Mode_display_title;} + public boolean Mode_is_popup() {return mode == Mode_popup;} + public static final byte Mode_basic = 0, Mode_alt = 1, Mode_display_title = 2, Mode_popup = 3; + public static final Xoh_html_wtr_ctx + Basic = new Xoh_html_wtr_ctx(Mode_basic) + , Alt = new Xoh_html_wtr_ctx(Mode_alt) + , Display_title = new Xoh_html_wtr_ctx(Mode_display_title) + , Popup = new Xoh_html_wtr_ctx(Mode_popup) + ; +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_escaper.java b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_escaper.java new file mode 100644 index 000000000..e4e0c36f3 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_escaper.java @@ -0,0 +1,119 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.parsers.amps.*; +public class Xoh_html_wtr_escaper { + public static byte[] Escape(Xoa_app app, Bry_bfr tmp_bfr, byte[] src) { + Escape(app, tmp_bfr, src, 0, src.length, true, false); + return tmp_bfr.XtoAryAndClear(); + } + public static void Escape(Xoa_app app, Bry_bfr bfr, byte[] src, int bgn, int end, boolean interpret_amp, boolean nowiki_skip) { + Xop_amp_mgr amp_mgr = app.Parser_amp_mgr(); + ByteTrieMgr_slim amp_trie = amp_mgr.Amp_trie(); + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.Lt: + if (nowiki_skip) { + byte[] nowiki_name = Xop_xnde_tag_.Tag_nowiki.Name_bry(); + int nowiki_name_len = nowiki_name.length; + if (Bry_.Eq(nowiki_name, src, i + 1, i + 1 + nowiki_name_len)) { // > found + break; + case Byte_ascii.Lt: + if ( tag_is_bgn // end) // not enough chars for "/nowiki>" + || src[i + 1] != Byte_ascii.Slash // / + || !Bry_.Eq(nowiki_name, src, i + 2, i + 2 + nowiki_name_len) // nowiki + || src[i + 2 + nowiki_name_len] != Byte_ascii.Gt // > + ) return Bry_.NotFound; + end_lt = i; + end_gt = i + 2 + nowiki_name_len; + i = end; + break; + } + } + if (end_gt == -1) return Bry_.NotFound; // ">" of not found + bfr.Add_mid(src, bgn_gt + 1, end_lt); + return end_gt; + } + catch (Exception e) { + app.Usr_dlg().Warn_many("", "", "unknown error in escape.nowiki: ~{0} ~{1}", Err_.Message_gplx_brief(e), String_.new_utf8_(src, bgn, end)); + return Bry_.NotFound; + } + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_tst.java b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_tst.java new file mode 100644 index 000000000..08c4d1585 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_html_wtr_tst.java @@ -0,0 +1,369 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xoh_html_wtr_tst { + private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_(); fxt.Reset();} + @Test public void Hr_basic() {fxt.Test_parse_page_wiki_str("----" , "
    ");} + @Test public void Hr_extended() {fxt.Test_parse_page_wiki_str("--------" , "
    ");} + @Test public void Lnki_basic() {fxt.Test_parse_page_wiki_str("[[a]]" , "a");} + @Test public void Lnki_caption() {fxt.Test_parse_page_wiki_str("[[a|b]]" , "b");} + @Test public void Lnki_caption_fmt() {fxt.Test_parse_page_wiki_str("[[a|''b'']]" , "b");} + @Test public void Lnki_tail_trg() {fxt.Test_parse_page_wiki_str("[[a]]b" , "ab");} + @Test public void Lnki_tail_caption() {fxt.Test_parse_page_wiki_str("[[a|b]]c" , "bc");} + @Test public void Lnki_title() { + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_wiki_str("[[a|b]]", "b"); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Lnki_title_page_text() { + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_wiki_str("[[a_b]]", "a_b"); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Lnki_category() {fxt.Test_parse_page_wiki_str("[[Category:A]]" , "");} // NOTE: Category does not get written in main page bfr + @Test public void Lnki_category_force() {fxt.Test_parse_page_wiki_str("[[:Category:A]]" , "Category:A");} + @Test public void Lnki_matches_page() {fxt.Test_parse_page_wiki_str("[[test page|t1]]", "t1");} // NOTE: "Test page" is hardcoded to be the test page name + @Test public void Lnki_matches_page_but_has_anchor() {fxt.Test_parse_page_wiki_str("[[Test page#a|test 1]]", "test 1");} // NOTE: "Test page" is hardcoded to be the test page name + @Test public void Lnki_anchor() {fxt.Test_parse_page_wiki_str("[[A#b]]" , "A#b");} +// @Test public void Img_invalid_wnt_char() { +// fxt.Test_parse_page_wiki_str +// ( "[[File:A*b.png]]" +// , "
    \"\"
    " +// ); +// } +// @Test public void Img_alt() { // FUTURE: enable; WHEN: after fixing xnde to handle bad xnde; EX: France +// fxt.Test_parse_page_wiki_str("[[File:A.png|none|9x8px|alt=ab\"c\"d]]", Xop_fxt.html_img_none("File:A.png", "ab"c"d")); +// } + @Test public void Url_encode() {fxt.Test_parse_page_wiki_str("[[a;@$!*(),/ _^b|z]]" , "z");} + @Test public void Apos_i() {fxt.Test_parse_page_wiki_str("''a''" , "a");} + @Test public void Apos_b() {fxt.Test_parse_page_wiki_str("'''a'''" , "a");} + @Test public void Apos_ib() {fxt.Test_parse_page_wiki_str("'''''a'''''" , "a");} + @Test public void Html_ent() {fxt.Test_parse_page_wiki_str("!" , "!");} + @Test public void Html_ref() {fxt.Test_parse_page_wiki_str(">" , ">");} + @Test public void Lnke_basic() {fxt.Test_parse_page_wiki_str("[irc://a]" , "[1]");} + @Test public void Lnke_autonumber() {fxt.Test_parse_page_wiki_str("[irc://a] [irc://b]" , "[1] [2]");} + @Test public void Lnke_caption() {fxt.Test_parse_page_wiki_str("[irc://a b]" , "b");} + @Test public void Lnke_caption_fmt() {fxt.Test_parse_page_wiki_str("[irc://a ''b'']" , "b");} + @Test public void Lnke_xowa() { + String img = ""; + fxt.Wiki().Sys_cfg().Xowa_proto_enabled_(true); + fxt.Test_parse_page_wiki_str("[xowa-cmd:\"a\" z]" , "z" + img + ""); + fxt.Test_parse_page_wiki_str("[xowa-cmd:\"a.b('c_d');\" z]" , "z" + img + ""); + fxt.Test_parse_page_wiki_str("[xowa-cmd:*\"a\"b*c\"* z]" , "z" + img + ""); + fxt.Wiki().Sys_cfg().Xowa_proto_enabled_(false); + fxt.Test_parse_page_wiki_str("[xowa-cmd:\"a\" b]" , "[xowa-cmd:"a" b]"); // protocol is disabled: literalize String (i.e.: don't make it an anchor) + } + @Test public void List_1_itm() { + fxt.Test_parse_page_wiki_str("*a", String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a" + , "
    • " + , "
    " + )); + } + @Test public void List_2_itms() { + fxt.Test_parse_page_wiki_str("*a\n*b", String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a" + , "
    • " + , "
    • b" + , "
    • " + , "
    " + )); + } + @Test public void List_nest_ul() { + fxt.Test_parse_page_wiki_str("*a\n**b", String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a" + , "
        " + , "
      • b" + , "
      • " + , "
      " + , "
    • " + , "
    " + )); + } + @Test public void List_dt_dd() { + fxt.Test_parse_page_wiki_str(";a:b", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    a" + , "
    " + , "
    b" + , "
    " + , "
    " + )); + } + @Test public void List_dd_nest2() { + fxt.Test_parse_page_wiki_str("::a", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , "
    " + , "
    a" + , "
    " + , "
    " + , "
    " + , "
    " + )); + } + @Test public void Tblw_basic() { + fxt.Test_parse_page_wiki_str("{|\n|+a\n!b||c\n|-\n|d||e\n|}", String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , "
    b" + , " c" + , "
    d" + , " e" + , "
    " + )); + } + @Test public void Tblw_atrs() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|style='z'" + , "|+a" + , "!style='y'|b||style='x'|c" + , "|-style='w'" + , "|style='v'|d||style='u'|e" + , "|}" + ), String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , "
    b" + , " c" + , "
    d" + , " e" + , "
    " + )); + } + @Test public void Para_hdr_list() { + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "==a==" + , "" + , "*b" + , "*c" + ), String_.Concat_lines_nl_skip_last + ( "

    a

    " + , "" + , "
      " + , "
    • b" + , "
    • " + , "
    • c" + , "
    • " + , "
    " + )); + fxt.Init_para_n_(); + } + @Test public void Para_nl_is_space() { + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "a" + , "b" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "b" + , "

    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Para_nl_2_2() { + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "a" + , "" + , "b" + , "" + , "c" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + , "" + , "

    b" + , "

    " + , "" + , "

    c" + , "

    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Div_2() { // WP:[[Air]]#Density of air + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "
    a
    " + , "" + , "
    b
    " + ), String_.Concat_lines_nl_skip_last + ( "
    a
    " + , "
    b
    " + )); + fxt.Init_para_n_(); + } + @Test public void Tblw() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl + ( "{|" + , "|-" + , "|a" + , "|b" + , "|-" + , "|c" + , "|d" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , " b" + , "
    c" + , " d" + , "
    " + , "" + )); + } + @Test public void Tblw_lnki_bang() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl + ( "{|" + , "|-" + , "|[[a|!]]" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    !" + , "
    " + , "" + )); + } + @Test public void Tr_inside_tblw_td() { // WP:[[Earth]] + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl + ( "{|" + , "|-" + , "
    a
    " + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + } + @Test public void Tblw_tr_with_newlines() {// WP:[[John Adams]] Infobox Officeholder + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl + ( "{|" + , "|-" + , "" + , "" + , "" + , "|a" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + } + @Test public void Bang_doesnt_force_tbl() { + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str("a! b! c", "

    a! b! c\n

    \n"); + fxt.Init_para_n_(); + } + @Test public void Err_nlOnly() { + fxt.Test_parse_page_wiki_str("{{\n}}", "{{\n}}"); // NOTE: was {{}} + } + @Test public void Xnde_inline() { + fxt.Test_parse_page_wiki_str("
    ", "
    "); + } + @Test public void Xnde_id_encode() { // PURPOSE: id should be url-encoded; DATE: 2013-11-13; + fxt.Test_parse_page_wiki_str("
    ", "
    "); + fxt.Test_parse_page_wiki_str("
    ", "
    "); + } + @Test public void Math() { + fxt.App().File_mgr().Math_mgr().Renderer_is_mathjax_(false); + fxt.Test_parse_page_all_str("x + y", "x + y"); // latex has img + fxt.App().File_mgr().Math_mgr().Renderer_is_mathjax_(true); + fxt.Test_parse_page_all_str("x + y", "x + y"); // mathjax has no img + fxt.App().File_mgr().Math_mgr().Renderer_is_mathjax_(false); + } + @Test public void Timeline() {// PURPOSE: embed timeline contents in pre; DATE:2014-05-22 + fxt.Test_parse_page_wiki_str("a", "
    a
    "); + } + @Test public void Amp_ncr_should_not_be_rendered_as_bytes() { // PURPOSE:   should be rendered as   not as literal bytes {192,160}; DATE:2013-12-09 + fxt.Test_parse_page_wiki_str("a b", "a b"); + } + + // @Test public void Fix_PositionAbsolute_stripped() { +// fxt.Test_parse_page_wiki_str("", ""); +// } +// @Test public void Xnde_nl() { +// fxt.Test_parse_page_wiki_str("
    c
    ", String_.Concat_lines_nl_skip_last +// ( "
    c
    " +// )); +// } +// @Test public void Tblw() { +// fxt.Test_parse_page_wiki_str("{|\n|}", String_.Concat_lines_nl +// ( "" +// , " " +// , " " +// , " " +// , " " +// , "
    a" +// , " b" +// , "
    " +// )); +// } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_imgs_mgr.java b/400_xowa/src/gplx/xowa/html/Xoh_imgs_mgr.java new file mode 100644 index 000000000..75b77a3c4 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_imgs_mgr.java @@ -0,0 +1,35 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.wikis.*; +public class Xoh_imgs_mgr implements GfoInvkAble { + public Xoh_imgs_mgr(Xow_html_mgr html_mgr) {wiki_is_default = html_mgr.Wiki().Domain_tid() == Xow_wiki_domain_.Tid_home;} private boolean wiki_is_default; + public Bool_obj_ref Alt_in_caption() {return alt_in_caption;} Bool_obj_ref alt_in_caption = Bool_obj_ref.y_(); + public Bool_obj_ref Alt_defaults_to_caption() {return alt_defaults_to_caption;} Bool_obj_ref alt_defaults_to_caption = Bool_obj_ref.y_(); + public void Copy_cfg(Xoh_imgs_mgr copy) {this.alt_in_caption = copy.alt_in_caption;} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_alt_in_caption)) return Yn.X_to_str(alt_in_caption.Val()); + else if (ctx.Match(k, Invk_alt_in_caption_)) alt_in_caption = Modify(wiki_is_default, alt_in_caption, m.ReadYn("v")); + else if (ctx.Match(k, Invk_alt_defaults_to_caption)) return Yn.X_to_str(alt_defaults_to_caption.Val()); + else if (ctx.Match(k, Invk_alt_defaults_to_caption_)) alt_defaults_to_caption = Modify(wiki_is_default, alt_defaults_to_caption, m.ReadYn("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + public static final String Invk_alt_in_caption = "alt_in_caption", Invk_alt_in_caption_ = "alt_in_caption_", Invk_alt_defaults_to_caption = "alt_defaults_to_caption", Invk_alt_defaults_to_caption_ = "alt_defaults_to_caption_"; + private static Bool_obj_ref Modify(boolean orig, Bool_obj_ref cur, boolean v) {return orig ? cur.Val_(v) : Bool_obj_ref.new_(v);} +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr.java b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr.java new file mode 100644 index 000000000..8c8960468 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr.java @@ -0,0 +1,299 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.files.*; +import gplx.xowa.parsers.lnkis.*; +public class Xoh_lnki_file_wtr { + public Xoh_lnki_file_wtr(Xow_wiki wiki, Xow_html_mgr html_mgr, Xoh_html_wtr html_wtr) { + this.html_mgr = html_mgr; + this.wiki = wiki; this.html_wtr = html_wtr; bfr_mkr = wiki.Utl_bry_bfr_mkr(); + } private Xow_html_mgr html_mgr; private boolean lnki_title_enabled; + private Xow_wiki wiki; private Xoh_html_wtr html_wtr; + private Xoh_lnki_txt_fmtr media_alt_fmtr = new Xoh_lnki_txt_fmtr(), caption_fmtr = new Xoh_lnki_txt_fmtr(); private Bry_bfr_mkr bfr_mkr; + private Xoa_url tmp_url = new Xoa_url(); + public void Write_or_queue(Bry_bfr bfr, Xoa_page page, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki) { + Xof_xfer_itm xfer_itm = this.Lnki_eval(ctx, page, lnki, queue_add_ref); + this.Write_media(bfr, hctx, src, lnki, xfer_itm, Alt_text(src, lnki)); + } + public void Write_or_queue(Bry_bfr bfr, Xoa_page page, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, byte[] alt_text) { + Xof_xfer_itm xfer_itm = this.Lnki_eval(ctx, page, lnki, queue_add_ref); + this.Write_media(bfr, hctx, src, lnki, xfer_itm, alt_text); + } private Bool_obj_ref queue_add_ref = Bool_obj_ref.n_(); + public void Page_bgn(Xoa_page page) { + cfg_alt_defaults_to_caption = page.Wiki().App().User().Wiki().Html_mgr().Imgs_mgr().Alt_defaults_to_caption().Val(); + } private boolean cfg_alt_defaults_to_caption = true; + private Xop_ctx ctx; + public Xof_xfer_itm Lnki_eval(Xop_ctx ctx, Xoa_page page, Xop_lnki_tkn lnki, Bool_obj_ref queue_add_ref) {return Lnki_eval(ctx, page.File_queue(), lnki.Ttl().Page_url(), lnki.Lnki_type(), lnki.Lnki_w(), lnki.Lnki_h(), lnki.Upright(), lnki.Thumbtime(), lnki.Page(), lnki.Ns_id() == Xow_ns_.Id_media, queue_add_ref);} + public Xof_xfer_itm Lnki_eval(Xop_ctx ctx, Xof_xfer_queue queue, byte[] lnki_ttl, byte lnki_type, int lnki_w, int lnki_h, double lnki_upright, double lnki_thumbtime, int lnki_page, boolean lnki_is_media_ns, Bool_obj_ref queue_add_ref) { + this.ctx = ctx; + queue_add_ref.Val_n_(); + tmp_xfer_itm.Clear().Atrs_by_ttl(lnki_ttl, Bry_.Empty).Atrs_by_lnki(lnki_type, lnki_w, lnki_h, lnki_upright, lnki_thumbtime, lnki_page); + boolean found = Find_file(ctx, tmp_xfer_itm); + boolean file_queue_add = File_queue_add(wiki, tmp_xfer_itm, lnki_is_media_ns, found); + Xof_xfer_itm rv = tmp_xfer_itm; + if (file_queue_add) { + queue_add_ref.Val_y_(); + return Queue_add_manual(queue, tmp_xfer_itm); + } + rv.File_found_(found); + return rv; + } private Xof_xfer_itm tmp_xfer_itm = new Xof_xfer_itm(); + private boolean Find_file(Xop_ctx ctx, Xof_xfer_itm xfer_itm) { + if (wiki.File_mgr().Version() == Xow_file_mgr.Version_2) + return ctx.Cur_page().Lnki_file_mgr().Find(wiki, ctx.Cur_page(), Xof_exec_tid.Tid_wiki_page, xfer_itm); + else + return wiki.File_mgr().Find_meta(xfer_itm); + } + public static Xof_xfer_itm Queue_add_manual(Xof_xfer_queue queue, Xof_xfer_itm xfer_itm) { + int elem_id = queue.Elem_id().Val_add(); + Xof_xfer_itm rv = xfer_itm.Clone().Html_elem_atrs_(elem_id, Xof_html_elem.Tid_img); + queue.Add(rv); + return rv; + } + private static boolean File_queue_add(Xow_wiki wiki, Xof_xfer_itm xfer_itm, boolean lnki_is_media_ns, boolean found) { + if (!wiki.File_mgr().Cfg_download().Enabled()) return false; +// if (xfer_itm.Meta_itm() == null) return false; // occurs when repos are missing; // DELETE: caused Redownload_missing to fail; no reason why missing shouldn't return a default repo; DATE:2013-01-26 + if (lnki_is_media_ns) return false; + switch (wiki.File_mgr().Cfg_download().Redownload()) { + case Xof_cfg_download.Redownload_none: + if (found) return false; + if (!found + && xfer_itm.Meta_itm() != null // null check; fsdb_call does not set meta + && xfer_itm.Meta_itm().Orig_exists() == Xof_meta_itm.Exists_n) + return false; // not found, and orig_exists is n; do not download again (NOTE: even if current lnki is thumb, don't bother looking for thumb if orig is missing) + break; + case Xof_cfg_download.Redownload_missing: + if (found) return false; + break; + case Xof_cfg_download.Redownload_all: + break; + } + return true; + } + private Xop_link_parser tmp_link_parser = new Xop_link_parser(); + private Xohp_title_wkr anchor_title_wkr = new Xohp_title_wkr(); + public void Write_media(Bry_bfr bfr, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, Xof_xfer_itm xfer_itm, byte[] lnki_alt_text) { + try { + lnki_title_enabled = html_wtr.Cfg().Lnki_title(); + int elem_id = xfer_itm.Html_uid(); + int div_width = xfer_itm.Html_w(); + if (div_width < 1 && wiki.File_mgr().Version_2_y()) // NOTE: html_w is -1 for v2 and missing files; use lnki_w if available; primarily affects audio files with specified width; [[File:A.oga|30px]]; DATE:2014-05-03 + div_width = xfer_itm.Lnki_w(); + if (div_width < 1) div_width = wiki.Html_mgr().Img_thumb_width(); + int lnki_halign = lnki.Align_h(); + if (lnki_halign == Xop_lnki_align_h.Null) lnki_halign = wiki.Lang().Img_thumb_halign_default(); // if halign is not supplied, then default to align for language + byte[] lnki_halign_bry = Xop_lnki_align_h.Html_names[lnki_halign]; + byte[] lnki_href = wiki.App().Href_parser().Build_to_bry(wiki, lnki.Ttl()); + byte[] html_orig_src = xfer_itm.Html_orig_src(); + byte[] html_view_src = xfer_itm.Html_view_src(); + byte[] content = Bry_.Empty; + byte[] lnki_ttl = lnki.Ttl().Page_txt(); + Xof_ext lnki_ext = xfer_itm.Lnki_ext(); + if ( html_mgr.Img_suppress_missing_src() // option to suppress src when file is missing + && !xfer_itm.Html_pass() // file is missing; wipe values and wait for "correct" info before regenerating; mostly to handle unknown redirects + && !lnki_ext.Id_is_media() // file is media; never suppress; src needs to be available for "click" on play; note that most media will be missing (not downloaded) + && lnki.Ns_id() != Xow_ns_.Id_media // ns is media; never suppress; "src" will use only orig_src; DATE:2014-01-30 + ) { + html_orig_src = html_view_src = Bry_.Empty; // null out src + } + + if (lnki.Ns_id() == Xow_ns_.Id_media) { // REF.MW:Linker.php|makeMediaLinkObj; NOTE: regardless of ext (ogg vs jpeg) and literal status (Media vs :Media), [[Media]] links are always rendered the same way; see Beethoven; EX: [[:Media:De-Ludwig_van_Beethoven.ogg|listen]]); [[File:Beethoven 3.jpg|The [[Media:BeethovenWithLyreGuitar( W. J. Mahler - 1804).jpg|complete painting]]...]] + html_mgr.Lnki_full_media().Bld_bfr_many(bfr, html_orig_src // NOTE: should always use orig_src; was using view_src; DATE:2014-01-19 + , lnki.Ttl().Page_txt(), Caption(src, lnki, Xoh_html_wtr_ctx.Basic, html_orig_src)); + return; + } + if (lnki_ext.Id_is_media()) { + if ( Xof_ext_.Id_is_video_strict(lnki_ext.Id()) // id is .ogv or .webm + || ( lnki_ext.Id_is_ogg() // id is ogg + && wiki.File_mgr().Version_1_y() // version is v1 (v2 always marks ogg as aud); DATE:2014-02-01 + && ( xfer_itm.Html_pass() // NOTE: xfer_itm.Html_pass() checks for video .ogg files (ext = .ogg and thumb is available); EX: WWI; + || xfer_itm.Meta_itm().State_new() // NOTE: State_new() will always assume that ogg is video; needed for 1st load and dynamic updates + ) + ) + ) { + xfer_itm.Html_elem_tid_(Xof_html_elem.Tid_vid); + if (Xop_lnki_type.Id_defaults_to_thumb(lnki.Lnki_type())) { + content = Video(src, hctx, lnki, xfer_itm, elem_id, true, lnki_href, html_view_src, html_orig_src, lnki_alt_text); + } + else { + content = Video(src, hctx, lnki, xfer_itm, elem_id, false, lnki_href, html_view_src, html_orig_src, lnki_alt_text); + html_mgr.Plain().Bld_bfr_many(bfr, content); + return; + } + } + else if (lnki_ext.Id_is_audio()) { + content = Audio(src, hctx, lnki, elem_id, lnki_href, html_orig_src, lnki_alt_text); + if (lnki.Media_icon()) + html_mgr.Lnki_thumb_core().Bld_bfr_many(bfr, div_width, lnki_halign_bry, content, elem_id); + else + html_mgr.Plain().Bld_bfr_many(bfr, content); + return; + } + html_mgr.Lnki_thumb_core().Bld_bfr_many(bfr, div_width, lnki_halign_bry, content, elem_id); + } + else { // image + if (lnki_halign == Xop_lnki_align_h.Center) bfr.Add(Bry_div_bgn_center); + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + byte[] anchor_title = lnki_title_enabled + ? Make_anchor_title(tmp_bfr, src, lnki, lnki_ttl, anchor_title_wkr) // NOTE: Make_anchor_title should only be called if there is no caption, else refs may not show; DATE:2014-03-05 + : Bry_.Empty; + if (Xop_lnki_type.Id_is_thumbable(lnki.Lnki_type())) { // is "thumb" + if (bfr.Len() > 0) bfr.Add_byte_nl(); + content = Image_thumb(src, hctx, lnki, xfer_itm, elem_id, lnki_href, html_view_src, html_orig_src, lnki_alt_text, lnki_ttl, anchor_title); + html_mgr.Lnki_thumb_core().Bld_bfr_many(bfr, div_width, lnki_halign_bry, content, elem_id); + } + else { + if ( cfg_alt_defaults_to_caption + && Bry_.Len_eq_0(lnki_alt_text) // NOTE: if no alt, always use caption; DATE:2013-07-22 + && !lnki.Alt_exists()) { // unless blank alt exists; EX: [[File:A.png|a|alt=]] should have alt of "", not "a" + Caption(src, lnki, Xoh_html_wtr_ctx.Alt, html_orig_src).XferAry(tmp_bfr, 0); + lnki_alt_text = tmp_bfr.XtoAryAndClear(); + } +// if (lnki_img_type == Xop_lnki_type.Id_none) bfr.Add(Bry_div_float_none).Add_byte_nl(); + switch (lnki.Align_h()) { + case Xop_lnki_align_h.Left: bfr.Add(Bry_div_float_left).Add_byte_nl(); break; + case Xop_lnki_align_h.Right: bfr.Add(Bry_div_float_right).Add_byte_nl(); break; + case Xop_lnki_align_h.None: bfr.Add(Bry_div_float_none).Add_byte_nl(); break; + } + Arg_nde_tkn lnki_link_tkn = lnki.Link_tkn(); + if (lnki_link_tkn == Arg_nde_tkn.Null) + html_mgr.Lnki_full_image().Bld_bfr_many(bfr, elem_id, lnki_href, html_view_src, xfer_itm.Html_w(), xfer_itm.Html_h(), lnki_alt_text, lnki_ttl, Xow_html_mgr.Bry_anchor_class_image, Xow_html_mgr.Bry_anchor_rel_blank, anchor_title, Img_cls(lnki)); + else { + Arg_itm_tkn link_tkn = lnki_link_tkn.Val_tkn(); + byte[] link_ref = link_tkn.Dat_to_bry(src); + byte[] link_ref_new = tmp_link_parser.Parse(tmp_bfr, tmp_url, wiki, link_ref, lnki_href); + link_ref = link_ref_new == null ? lnki_href: link_ref_new; // if parse fails, then assign to lnki_href; EX:link={{{1}}} + lnki_ttl = Bry_.Coalesce(lnki_ttl, tmp_link_parser.Html_xowa_ttl()); + html_mgr.Lnki_full_image().Bld_bfr_many(bfr, elem_id, link_ref, html_view_src, xfer_itm.Html_w(), xfer_itm.Html_h(), lnki_alt_text, lnki_ttl, tmp_link_parser.Html_anchor_cls(), tmp_link_parser.Html_anchor_rel(), anchor_title, Img_cls(lnki)); + } + switch (lnki.Align_h()) { + case Xop_lnki_align_h.Left: + case Xop_lnki_align_h.Right: + case Xop_lnki_align_h.None: bfr.Add(Bry_div_end); break; + } + } + if (lnki_halign == Xop_lnki_align_h.Center) bfr.Add(Bry_div_end); + tmp_bfr.Mkr_rls(); + } + } catch (Exception e) { + wiki.App().Usr_dlg().Warn_many("", "", "lnki_wtr:fatal error while writing lnki: ttl=~{0} err=~{1}", String_.new_utf8_(lnki.Ttl().Raw()), Err_.Message_gplx_brief(e)); + } + } + private static byte[] Make_anchor_title(Bry_bfr bfr, byte[] src, Xop_lnki_tkn lnki, byte[] lnki_ttl, Xohp_title_wkr anchor_title_wkr) { + switch (lnki.Lnki_type()) { + case Xop_lnki_type.Id_thumb: // If the image is a thumb, do not add a title / alt, even if a caption is available + case Xop_lnki_type.Id_frame: + return Bry_.Empty; + case Xop_lnki_type.Id_frameless: // If the image is frameless, add the caption as a title / alt. If no caption is available, do not add a title / alt + break; + } + Xop_tkn_itm anchor_title_tkn = lnki.Caption_tkn(); + if (anchor_title_tkn == Xop_tkn_null.Null_tkn) return Bry_.Empty; // no caption; return empty; (do not use lnki); DATE:2013-12-31 + bfr.Add(Anchor_title); + anchor_title_wkr.Set(src, anchor_title_tkn).XferAry(bfr, 0); + bfr.Add_byte(Byte_ascii.Quote); + return bfr.XtoAryAndClear(); + } + private byte[] Video(byte[] src, Xoh_html_wtr_ctx hctx, Xop_lnki_tkn lnki, Xof_xfer_itm xfer_itm, int elem_id, boolean lnki_thumb, byte[] lnki_href, byte[] html_view_src, byte[] html_orig_src, byte[] lnki_alt_text) { + int thumb_w = xfer_itm.Html_w(); + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + int play_btn_width = thumb_w; if (play_btn_width < 1) play_btn_width = wiki.Html_mgr().Img_thumb_width(); + if (lnki_thumb) + html_mgr.Lnki_thumb_file_video().Bld_bfr_many(tmp_bfr, Play_btn(elem_id, play_btn_width, play_btn_width, html_orig_src, lnki.Ttl().Page_txt()), Img_thumb(lnki, xfer_itm, elem_id, lnki_href, html_view_src, lnki_alt_text), Caption_div(src, lnki, html_orig_src, lnki_href), Alt_html(src, lnki)); + else + html_mgr.Lnki_thumb_file_video().Bld_bfr_many(tmp_bfr, Play_btn(elem_id, play_btn_width, play_btn_width, html_orig_src, lnki.Ttl().Page_txt()), Img_thumb(lnki, xfer_itm, elem_id, lnki_href, html_view_src, lnki_alt_text), Bry_.Empty, Bry_.Empty); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private byte[] Image_thumb(byte[] src, Xoh_html_wtr_ctx hctx, Xop_lnki_tkn lnki, Xof_xfer_itm xfer_itm, int elem_id, byte[] lnki_href, byte[] html_view_src, byte[] html_orig_src, byte[] lnki_alt_text, byte[] lnki_ttl, byte[] anchor_title) { + byte[] lnki_alt_html = Alt_html(src, lnki); + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + byte[] lnki_class = xfer_itm.Html_pass() + ? Xow_html_mgr.Bry_img_class_thumbimage + : Xow_html_mgr.Bry_img_class_none; + html_mgr.Lnki_full_image().Bld_bfr_many(tmp_bfr, elem_id, lnki_href, html_view_src, xfer_itm.Html_w(), xfer_itm.Html_h(), lnki_alt_text, lnki_ttl, Xow_html_mgr.Bry_anchor_class_image, Xow_html_mgr.Bry_anchor_rel_blank, anchor_title, lnki_class); + byte[] thumb = tmp_bfr.XtoAryAndClear(); + if (!wiki.Html_mgr().Imgs_mgr().Alt_in_caption().Val()) lnki_alt_html = Bry_.Empty; + html_mgr.Lnki_thumb_file_image().Bld_bfr_many(tmp_bfr, thumb, Caption_div(src, lnki, html_orig_src, lnki_href), lnki_alt_html); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } private static final byte[] Anchor_title = Bry_.new_utf8_(" title=\""); + private byte[] Audio(byte[] src, Xoh_html_wtr_ctx hctx, Xop_lnki_tkn lnki, int elem_id, byte[] lnki_href, byte[] html_orig_src, byte[] lnki_alt_text) { + byte[] info_btn = Bry_.Empty; + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + if (lnki.Media_icon()) { + html_mgr.Lnki_thumb_part_info_btn().Bld_bfr_many(tmp_bfr, wiki.Html_mgr().Img_media_info_btn(), lnki_href); + info_btn = tmp_bfr.XtoAryAndClear(); + } + int play_btn_width = lnki.Lnki_w(); if (play_btn_width < 1) play_btn_width = wiki.Html_mgr().Img_thumb_width(); // if no width set width to default img width + html_mgr.Lnki_thumb_file_audio().Bld_bfr_many(tmp_bfr, Play_btn(elem_id, play_btn_width, Play_btn_max_width, html_orig_src, lnki.Ttl().Page_txt()), info_btn, Caption_div(src, lnki, html_orig_src, lnki_href), Alt_html(src, lnki)); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private byte[] Img_thumb(Xop_lnki_tkn lnki, Xof_xfer_itm xfer_itm, int elem_id, byte[] lnki_href, byte[] html_view_src, byte[] alt) { + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + html_mgr.Lnki_thumb_part_image().Bld_bfr_many(tmp_bfr, elem_id, Bry_class_internal, lnki_href, lnki.Ttl().Page_txt(), html_view_src, xfer_itm.Html_w(), xfer_itm.Html_h(), alt); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private byte[] Img_cls(Xop_lnki_tkn lnki) { + if (lnki.Border() == Bool_.Y_byte) { + return Img_cls_thumbborder; + } + else + return Bry_.Empty; + } private static final byte[] Img_cls_thumbborder = Bry_.new_ascii_(" class=\"thumbborder\""); + public static final byte[] Bry_class_internal = Bry_.new_ascii_("image"); + private byte[] Alt_text(byte[] src, Xop_lnki_tkn lnki) { + if (!lnki.Alt_exists()) return Bry_.Empty; + media_alt_fmtr.Set(html_wtr, ctx, Xoh_html_wtr_ctx.Alt, src, lnki.Alt_tkn().Val_tkn(), html_mgr.Plain()); + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + media_alt_fmtr.XferAry(tmp_bfr, 0); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private byte[] Alt_html(byte[] src, Xop_lnki_tkn lnki) { + if (!lnki.Alt_exists()) return Bry_.Empty; + media_alt_fmtr.Set(html_wtr, ctx, Xoh_html_wtr_ctx.Basic, src, lnki.Alt_tkn().Val_tkn(), html_mgr.Lnki_thumb_part_alt()); + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + media_alt_fmtr.XferAry(tmp_bfr, 0); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private byte[] Caption_div(byte[] src, Xop_lnki_tkn lnki, byte[] html_orig_src, byte[] lnki_href) { + Bry_fmtr_arg caption = Caption(src, lnki, Xoh_html_wtr_ctx.Basic, html_orig_src); + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + byte[] magnify_btn = Bry_.Empty; + if (lnki.Media_icon()) { + html_mgr.Lnki_thumb_part_magnfiy_btn().Bld_bfr_many(tmp_bfr, wiki.Html_mgr().Img_thumb_magnify(), lnki_href, wiki.Msg_mgr().Val_by_id(Xol_msg_itm_.Id_file_enlarge)); + magnify_btn = tmp_bfr.XtoAryAndClear(); + } + html_mgr.Lnki_thumb_part_caption().Bld_bfr_many(tmp_bfr, magnify_btn, caption); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private Bry_fmtr_arg Caption(byte[] src, Xop_lnki_tkn lnki, Xoh_html_wtr_ctx hctx, byte[] html_orig_src) { + return lnki.Caption_exists() + ? caption_fmtr.Set(html_wtr, ctx, hctx, src, lnki.Caption_val_tkn(), html_mgr.Plain()) + : Bry_fmtr_arg_.Null; + } + private byte[] Play_btn(int elem_id, int width, int max_width, byte[] html_orig_src, byte[] lnki_href) { + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + html_mgr.Lnki_thumb_part_play_btn().Bld_bfr_many(tmp_bfr, elem_id, wiki.Html_mgr().Img_media_play_btn(), width - 2, max_width, html_orig_src, lnki_href); // NOTE: -2 is fudge factor else play btn will jut out over video thumb; see Earth and ISS video + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private static final int Play_btn_max_width = 1024; + private static final byte[] Bry_div_bgn_center = Bry_.new_ascii_("
    "), Bry_div_end = Bry_.new_ascii_("
    ") + , Bry_div_float_none = Bry_.new_ascii_("
    "), Bry_div_float_left = Bry_.new_ascii_("
    "), Bry_div_float_right = Bry_.new_ascii_("
    "); + public static byte[] Lnki_cls_visited(gplx.xowa.users.history.Xou_history_mgr history_mgr, byte[] wiki_key, byte[] page_ttl) { + return history_mgr.Has(wiki_key, page_ttl) ? Lnki_cls_visited_bry : Bry_.Empty; + } private static final byte[] Lnki_cls_visited_bry = Bry_.new_ascii_(" class=\"xowa-visited\""); +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_audio_video_tst.java b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_audio_video_tst.java new file mode 100644 index 000000000..2f2e08b02 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_audio_video_tst.java @@ -0,0 +1,230 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xoh_lnki_file_wtr_audio_video_tst { + @Before public void init() {fxt.Reset();} private Xop_fxt fxt = new Xop_fxt(); + @Test public void Audio_full() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.oga|noicon]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + , "
    " + , " " + , "
    " + )); + } + @Test public void Audio_full_ogg() {// PURPOSE: ogg should show src on first load + fxt.Wiki().Html_mgr().Img_suppress_missing_src_(true); // simulate release-mode wherein missing images will not have src + fxt.Test_parse_page_all_str + ( "[[File:A.ogg]]", String_.Concat_lines_nl_skip_last + ( "
    " + , " " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + )); + fxt.Wiki().Html_mgr().Img_suppress_missing_src_(false); + } + @Test public void Audio_thumb() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.oga|thumb|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + , " " + , " " + , " " + , "
    " + , "
    " + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " a" + , "
    " + , "
    " + , "
    " + , "b" + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Audio_full_width() { // ISSUE: width arg ignored for v2; zh.b:小学数学/自然数; DATE:2014-05-03 + fxt.Wiki().File_mgr().Version_2_y_(); + fxt.Test_html_wiki_frag("[[File:A.oga|30px|a]]", "
    "); + fxt.Wiki().File_mgr().Version_1_y_(); + } + @Test public void Audio_noicon() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.oga|thumb|noicon|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + , "
    " + , " a" + , "
    " + , "
    " + , "
    " + , "b" + , "
    " + )); + } + @Test public void Video_full() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.ogv|400px|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " " + , " \"b\"" + , " " + , "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + )); + } + @Test public void Video_full_ogg() {// PURPOSE: ogg should default to video on first load; otherwise dynamic-update won't be able to put in thumb + fxt.Test_parse_page_wiki_str + ( "[[File:A.ogg|400px|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " " + , " \"b\"" + , " " + , "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + )); + } + @Test public void Video_thumb() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.ogv|thumb|400px|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , "
    " + , "
    " + , " " + , " \"b\"" + , " " + , "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " a" + , "
    " + , "
    " + , "
    " + , "b" + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Video_thumb_webm() { // PURPOSE: webm thumb wasn't being shown; DATE:2014-01-25 + fxt.Test_parse_page_wiki_str + ( "[[File:A.webm|thumb|400px|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , "
    " + , "
    " + , " " + , " \"b\"" + , " " + , "
    " + , "
    " + , " " + , " \"Play" + , " " + , "
    " + , "
    " + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " a" + , "
    " + , "
    " + , "
    " + , "b" + , "
    " + , "
    " + , "
    " + , "" + )); + } +} +// @Test public void Ogg() { +// fxt.Src_en_wiki_repo().Ext_rules().Get_or_new(Xof_ext_.Bry_ogg).View_max_(0); +// fxt .ini_page_api("commons", "A.ogg", "", 0, 0); +// fxt .Lnki_orig_("A.ogg") +// .Src( ) +// .Trg( +// fxt.reg_("mem/xowa/file/#meta/en.wikipedia.org/4/42.csv", "A.ogg|z||2?0,0|0?0,0") +// ) +// .tst(); +// fxt .Lnki_orig_("A.ogg") +// .Html_orig_src_("file:///mem/trg/en.wikipedia.org/raw/4/2/A.ogg") +// .tst(); +// fxt.Src_en_wiki_repo().Ext_rules().Get_or_new(Xof_ext_.Bry_ogg).View_max_(-1); +// } diff --git a/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_basic_tst.java b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_basic_tst.java new file mode 100644 index 000000000..d7739b338 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_basic_tst.java @@ -0,0 +1,232 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xoh_lnki_file_wtr_basic_tst { + private Xop_fxt fxt = new Xop_fxt(); + @Before public void init() {fxt.Reset();} + @Test public void Img_full() { // PURPOSE: full with title was outputting invalid html; DATE:2013-12-31 + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_wiki_str + ( "[[File:A.png]]" + , String_.Concat_lines_nl_skip_last + ( "\"\"" // NOTE: used to output class=\"image\"A.png + )); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Img_embed() { + fxt.Test_parse_page_wiki_str("[[File:A.png|9x8px|alt=abc]]", Xop_fxt.html_img_none("File:A.png", "abc", "file:///mem/wiki/repo/trg/thumb/7/0/A.png/9px.png", "A.png")); + } + @Test public void Img_none() { // NOTE: floatnone is WP behavior; MW omits div tag + fxt.Test_parse_page_wiki_str + ( "[[File:A.png|none|20x30px|b]]" + , String_.Concat_lines_nl_skip_last + ( "
    " + , "\"b\"
    " + )); + } + @Test public void Img_thumb_none() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.png|thumb|none|b]]" + , Img_thumb_str("none") + ); + } + @Test public void Img_thumb_ltr() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.png|thumb|b]]" + , Img_thumb_str("right") + ); + } + @Test public void Img_thumb_rtl() { + fxt.Wiki().Lang().Dir_ltr_(false); + fxt.Test_parse_page_wiki_str + ( "[[File:A.png|thumb|b]]" + , Img_thumb_str("left") + ); + fxt.Wiki().Lang().Dir_ltr_(true); + } + private String Img_thumb_str(String align) { + return String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " \"\"" + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " b" + , "
    " + , "
    " + , "
    " + , "" + ); + } + @Test public void Img_frame() { // PURPOSE: lnki with "frame" is same as thumb; DATE:2013-12-23 + fxt.Test_parse_page_wiki_str + ( "[[File:A.png|frame|220x110px|b]]" + , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " \"\"" + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " b" + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Border() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.png|border]]" + , "\"\""); + } + @Test public void Lnki_full_svg() { + fxt.Test_parse_page_wiki_str + ( "[[File:A.svg|a|alt=b]]", String_.Concat_lines_nl_skip_last + ( "\"b\"" // HACK: tries to get orig_w which is not available + )); + } + @Test public void Lnki_file_alt_link() { // PURPOSE: lnki in caption should not create alt="bcd" + fxt.Test_parse_page_wiki_str("[[File:A.png|thumb|alt=b [[c]] d]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " \"b" + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " " + , "
    " + , "
    " + , "
    " + , "b c d" + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Img_title() { + fxt.Wtr_cfg().Lnki_title_(true); + Tst_img_title("[[File:A.png|frameless|a b]]", "a b"); + Tst_img_title("[[File:A.png|thumb|a b]]", "Enlarge"); // caption should not replace text + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Lnki_alt_is_text() { // PURPOSE: (a) alt should default to caption; (b) alt should not show html chars (like \"abc\"" + ); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Alt_ignore_apos() {// PURPOSE: alt should ignore apos; EX: [[File:A.png|''A'']] should have alt of A; DATE:2013-10-25 + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_all_str + ( "[[File:A.png|''b'']]" + , "\"b\"" + ); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Alt_ignore_lnke() {// PURPOSE: alt should ignore lnke + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_all_str + ( "[[File:A.png|b[http://c.org d] e]]" + , "\"bd" + ); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Alt_ignore_list() {// PURPOSE: alt should ignore list + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_all_str + ( "[[File:A.png|b\n*c]]" + , "\"b*c\"" + ); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Alt_ignore_tblw() {// PURPOSE: alt should ignore tblw + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_all_str + ( "[[File:A.png|\n{|\n|-\n|b\n|}\n]]" + , "\"" + ); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Alt_ignore_para() {// PURPOSE: alt should ignore para + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str + ( "[[File:A.png|b\nc]]" + , String_.Concat_lines_nl + ( "

    \"b" + , "

    " + )); + fxt.Init_para_n_(); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Lnki_empty_alt_is_omitted() {// PURPOSE: empty alt should be ignored; DATE:2013-07-30 + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_all_str + ( "[[File:A.png|a|alt=]]" + , "\"\"" + ); + fxt.Wtr_cfg().Lnki_title_(false); + } + @Test public void Href_anchor_leading_space() { // PURPOSE: space before anchor should be preserved, not " " -> "#" + fxt.Test_parse_page_all_str("[[A #b]]", "A #b"); + } + @Test public void Href_anchor_leading_space_ns() { // PURPOSE: same as above, but with ns; DATE:2013-08-29 + fxt.Test_parse_page_all_str("[[Help:A #b]]", "Help:A #b"); + } + @Test public void Href_anchor_leading_ns_lc() { // PURPOSE: same as above but with lc title + fxt.Test_parse_page_all_str("[[Help:a#b]]", "Help:A#b"); + } + @Test public void Href_anchor_leading_space_ns_lc() { // PURPOSE: same as above but with lc title + fxt.Test_parse_page_all_str("[[Help:a #b]]", "Help:A #b"); + } + @Test public void Lnki_caption_nested_file() { // PURPOSE: nested lnki in caption breaks alt with html chars; EX:de.w:Wien; DATE:2013-12-16 + fxt.Wtr_cfg().Lnki_title_(true); + fxt.Test_parse_page_wiki_str("[[File:A.png|none|[[File:B.png|20px|d]] c]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "\"d
    " + , "" + )); + fxt.Wtr_cfg().Lnki_title_(false); + } + private void Tst_img_title(String raw, String expd_ttl) { + String actl = fxt.Exec_parse_page_wiki_as_str(raw); + String actl_ttl = null; + int title_bgn = String_.FindFwd(actl, " title=\""); + if (title_bgn != String_.Find_none) { + title_bgn += String_.Len(" title=\""); + int title_end = String_.FindFwd(actl, "\"", title_bgn); + if (title_end != String_.Find_none) actl_ttl = String_.Mid(actl, title_bgn, title_end); + } + Tfds.Eq(expd_ttl, actl_ttl, actl); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_media_tst.java b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_media_tst.java new file mode 100644 index 000000000..bfa5b38fc --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_lnki_file_wtr_media_tst.java @@ -0,0 +1,63 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xoh_lnki_file_wtr_media_tst { + private Xop_fxt fxt = new Xop_fxt(); + @Before public void init() {fxt.Reset();} + @Test public void Lnki_caption_nested_media() { // EX.WP:Beethoven; + fxt.Test_parse_page_wiki_str("[[File:A.png|thumb|b [[Media:A.ogg]] c]]", String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " \"\"" + , "
    " + , "
    " + , " " + , " \"\"" + , " " + , "
    " + , " b " + , " c" + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Lnki_media_normal() { + fxt.Test_parse_page_wiki_str("[[Media:A.png|b]]", String_.Concat_lines_nl_skip_last + ( "b" + , "" + )); + } + @Test public void Lnki_media_literal() { + fxt.Test_parse_page_wiki_str("[[:Media:A.ogg|b]]", String_.Concat_lines_nl_skip_last + ( "b" + , "" + )); + } + @Test public void Lnki_media_literal_pdf() { + fxt.Wiki().Html_mgr().Img_suppress_missing_src_(true); // simulate missing file; DATE:2014-01-30 + fxt.Test_parse_page_wiki_str("[[Media:A.pdf|b]]", String_.Concat_lines_nl_skip_last + ( "b" + , "" + )); + Tfds.Eq(0, fxt.Page().File_queue().Count()); // make sure media does not add to queue + fxt.Wiki().Html_mgr().Img_suppress_missing_src_(false); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_lnki_txt_fmtr.java b/400_xowa/src/gplx/xowa/html/Xoh_lnki_txt_fmtr.java new file mode 100644 index 000000000..6f291a4c8 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_lnki_txt_fmtr.java @@ -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 . +*/ +package gplx.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xoh_lnki_txt_fmtr implements Bry_fmtr_arg { // formats alt or caption + private Bry_fmtr fmtr; private Xoh_html_wtr wtr; private Xop_ctx ctx; private Xoh_html_wtr_ctx hctx; private byte[] src; private Xop_tkn_itm tkn; + public Xoh_lnki_txt_fmtr Set(Xoh_html_wtr wtr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_tkn_itm tkn, Bry_fmtr fmtr) { + this.wtr = wtr; this.ctx = ctx; this.hctx = hctx; this.src = src; this.tkn = tkn; this.fmtr = fmtr; + return this; + } + public void XferAry(Bry_bfr trg, int idx) { + Bry_bfr tmp_bfr = Bry_bfr.new_(); + wtr.Write_tkn(tmp_bfr, ctx, hctx, src, null, Xoh_html_wtr.Sub_idx_null, tkn); + byte[] bry = tmp_bfr.XtoAryAndClear(); + if (bry.length == 0) return; + fmtr.Bld_bfr_many(trg, bry); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_lnki_wtr.java b/400_xowa/src/gplx/xowa/html/Xoh_lnki_wtr.java new file mode 100644 index 000000000..8f3bbe7cd --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_lnki_wtr.java @@ -0,0 +1,175 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.files.*; import gplx.xowa.parsers.lnkis.redlinks.*; import gplx.xowa.users.history.*; +public class Xoh_lnki_wtr { + private Xoa_app app; private Xow_wiki wiki; private Xoa_page page; private Xop_ctx ctx; + private Xoh_html_wtr_cfg cfg; + private Xou_history_mgr history_mgr; + private Xop_lnki_caption_wtr_tkn caption_tkn_wtr; + private Xop_lnki_caption_wtr_bry caption_bry_wtr; + private Xop_lnki_logger_redlinks_mgr redlinks_mgr; + public Xoh_lnki_wtr(Xoh_html_wtr html_wtr, Xow_wiki wiki, Xow_html_mgr html_mgr, Xoh_html_wtr_cfg cfg) { + caption_tkn_wtr = new Xop_lnki_caption_wtr_tkn(html_wtr); + caption_bry_wtr = new Xop_lnki_caption_wtr_bry(); + this.wiki = wiki; this.app = wiki.App(); this.cfg = cfg; + file_wtr = new Xoh_lnki_file_wtr(wiki, html_mgr, html_wtr); + } + public Xoh_lnki_file_wtr File_wtr() {return file_wtr;} private Xoh_lnki_file_wtr file_wtr; + public void Init_by_page(Xop_ctx ctx, Xoa_page page) { + this.ctx = ctx; this.page = page; // NOTE: must set ctx for file.v2; DATE:2014-06-22 + this.wiki = ctx.Wiki(); + redlinks_mgr = page.Lnki_redlinks_mgr(); // NOTE: need to set redlinks_mgr, else toc parse may fail; EX:pl.d:head_sth_off;DATE:2014-05-07 + file_wtr.Page_bgn(page); + this.history_mgr = app.User().History_mgr(); + } + public void Write(Bry_bfr bfr, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki) { + Xoa_ttl lnki_ttl = lnki.Ttl(); + Xow_xwiki_itm lang = lnki_ttl == null ? null : lnki_ttl.Wik_itm(); + if (lang != null && lang.Type_is_lang(wiki.Lang().Lang_id()) && !lnki_ttl.ForceLiteralLink()) { + page.Xwiki_langs().Add(lnki_ttl); + return; + } + if (lnki_ttl == null) {// NOTE: parser failed to properly invalidate lnki; escape tkn now and warn; DATE:2014-06-06 + app.Usr_dlg().Warn_many("", "", "invalid lnki evaded parser; page=~{0} ex=~{1}", ctx.Cur_page().Url().X_to_full_str(), String_.new_utf8_(src, lnki.Src_bgn(), lnki.Src_end())); + Xoh_html_wtr_escaper.Escape(app, bfr, src, lnki.Src_bgn(), lnki.Src_end(), true, false); + return; + } + boolean literal_link = lnki_ttl.ForceLiteralLink(); // NOTE: if literal link, then override ns behavior; for File, do not show image; for Ctg, do not display at bottom of page + redlinks_mgr.Lnki_add(lnki); + boolean stage_is_alt = hctx.Mode_is_alt(); + switch (lnki.Ns_id()) { + case Xow_ns_.Id_media: if (!stage_is_alt) file_wtr.Write_or_queue(bfr, page, ctx, hctx, src, lnki); return; // NOTE: literal ":" has no effect; EX.WP:Beethoven and [[:Media:De-Ludwig_van_Beethoven.ogg|listen]] + case Xow_ns_.Id_file: if (!literal_link && !stage_is_alt) {file_wtr.Write_or_queue(bfr, page, ctx, hctx, src, lnki); return;} break; + case Xow_ns_.Id_category: if (!literal_link) {page.Html_data().Ctgs_add(lnki.Ttl()); return;} break; + } + Write_plain_by_tkn(bfr, hctx, src, lnki, lnki_ttl); + } + public void Write_file(Bry_bfr bfr, Xoa_page page, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, byte[] alt) { + file_wtr.Write_or_queue(bfr, page, ctx, hctx, src, lnki, alt); + } + public void Write_file(Bry_bfr bfr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, Xof_xfer_itm xfer, byte[] alt) { + file_wtr.Write_media(bfr, hctx, src, lnki, xfer, alt); + } + public void Write_plain_by_bry(Bry_bfr bfr, byte[] src, Xop_lnki_tkn lnki, byte[] caption) { + Write_plain(bfr, Xoh_html_wtr_ctx.Basic, src, lnki, lnki.Ttl(), caption_bry_wtr.Caption_bry_(caption)); + } + public void Write_plain_by_tkn(Bry_bfr bfr, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, Xoa_ttl lnki_ttl) { + Write_plain(bfr, hctx, src, lnki, lnki_ttl, caption_tkn_wtr); + } + private void Write_plain(Bry_bfr bfr, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, Xoa_ttl lnki_ttl, Xop_lnki_caption_wtr caption_wkr) { + byte[] ttl_bry = lnki.Ttl_ary(); + if (Bry_.Len_eq_0(ttl_bry)) ttl_bry = lnki_ttl.Full_txt_raw(); // NOTE: handles ttls like [[fr:]] and [[:fr;]] which have an empty Page_txt, but a valued Full_txt_raw + if (Bry_.Eq(lnki_ttl.Full_txt(), page.Ttl().Full_txt())) { // lnki is same as pagename; bold; SEE: Month widget on day pages will bold current day; EX.WP: January 1 + if (lnki_ttl.Anch_bgn() == -1 && Bry_.Eq(lnki_ttl.Wik_txt(), page.Ttl().Wik_txt())) { // only bold if lnki is not pointing to anchor on same page; EX.WP: Comet; [[Comet#Physical characteristics|ion tail]] + bfr.Add(Xoh_consts.B_bgn); + Write_caption(bfr, ctx, hctx, src, lnki, ttl_bry, true, caption_wkr); + bfr.Add(Xoh_consts.B_end); + return; + } + } + if (hctx.Mode_is_alt()) + Write_caption(bfr, ctx, hctx, src, lnki, ttl_bry, true, caption_wkr); + else { + bfr.Add(Xoh_consts.A_bgn); // ' Roman empire + if (cfg.Lnki_visited() + && history_mgr.Has(wiki.Domain_bry(), ttl_bry)) + bfr.Add(Bry_xowa_visited); // '" class="xowa-visited' + bfr.Add(Xoh_consts.__end_quote); // '">' + if (lnki_ttl.Anch_bgn() != -1 && !lnki_ttl.Ns().Id_main()) { // anchor exists and not main_ns; anchor must be manually added b/c Xoa_ttl does not handle # for non main-ns + byte[] anch_txt = lnki_ttl.Anch_txt(); + byte anch_spr + = (anch_txt.length > 0 && anch_txt[0] == Byte_ascii.Hash) // 1st char is #; occurs when page_txt has trailing space; causes 1st letter of anch_txt to start at # instead of 1st letter + ? Byte_ascii.Space // ASSUME: 1 space ("Help:A #b"); does not handle multiple spaces like ("Help:A #b"); needs change to Xoa_ttl + : Byte_ascii.Hash; // Anch_txt bgns at 1st letter, so add # for caption; + ttl_bry = Bry_.Add_w_dlm(anch_spr, ttl_bry, anch_txt); // manually add anchor; else "Help:A#b" becomes "Help:A". note that lnki.Ttl_ary() uses .Full_txt (wiki + page but no anchor) to captialize 1st letter of page otherwise "Help:A#b" shows as "Help:A" (so Help:a -> Help:A); DATE:2013-06-21 + } + Write_caption(bfr, ctx, hctx, src, lnki, ttl_bry, true, caption_wkr); + bfr.Add(Xoh_consts.A_end); // + } + } + private void Write_caption(Bry_bfr bfr, Xop_ctx ctx, Xoh_html_wtr_ctx hctx, byte[] src, Xop_lnki_tkn lnki, byte[] ttl_bry, boolean tail_enabled, Xop_lnki_caption_wtr caption_wkr) { + if (lnki.Caption_exists()) { // lnki has a caption seg; EX: [[A|caption]] + if (lnki.Caption_tkn_pipe_trick()) // "pipe trick"; [[A|]] is same as [[A|A]]; also, [[Help:A|]] -> [[Help:A|A]] + bfr.Add(lnki.Ttl().Page_txt()); + else + caption_wkr.Write_tkn(ctx, hctx, bfr, src, lnki, Xoh_html_wtr.Sub_idx_null, lnki.Caption_val_tkn()); + } + else { // lnki only has ttl + if (!Write_caption_for_rel2abs(bfr, lnki)) // write caption if rel2abs ttls + bfr.Add(ttl_bry); // write ttl_bry as caption; + } + if (tail_enabled) { // write tail if enabled; EX: [[A]]b -> Ab + int tail_bgn = lnki.Tail_bgn(); + if (tail_bgn != -1) bfr.Add_mid(src, tail_bgn, lnki.Tail_end()); + } + } + private static boolean Write_caption_for_rel2abs(Bry_bfr bfr, Xop_lnki_tkn lnki) { + int subpage_tid = lnki.Subpage_tid(); if (subpage_tid == Pf_xtn_rel2abs.Id_null) return false; // not a subpage + boolean subpage_slash_at_end = lnki.Subpage_slash_at_end(); + byte[] leaf_txt = lnki.Ttl().Leaf_txt_wo_qarg(); + switch (subpage_tid) { + case Pf_xtn_rel2abs.Id_slash: + if (subpage_slash_at_end) // "/" at end; only add text; EX: [[/A/]] -> A + bfr.Add(leaf_txt); + else // "/" absent; add slash to bgn; EX: [[/A]] -> /A + bfr.Add_byte(Byte_ascii.Slash).Add(leaf_txt); + return true; + case Pf_xtn_rel2abs.Id_dot_dot_slash: + if (subpage_slash_at_end) // "/" at end; only add text; EX: [[../A/]] -> A + bfr.Add(leaf_txt); + else // "/" absent; add page; EX: [[../A]] -> Page/A + bfr.Add(lnki.Ttl().Page_txt()); + return true; + } + return false; + } + private static final byte[] Bry_xowa_visited = Bry_.new_ascii_("\" class=\"xowa-visited"); +} +interface Xop_lnki_caption_wtr { + void Write_tkn(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_tkn_grp grp, int sub_idx, Xop_tkn_itm tkn); +} +class Xop_lnki_caption_wtr_bry implements Xop_lnki_caption_wtr { + private byte[] caption_bry; + public Xop_lnki_caption_wtr_bry Caption_bry_(byte[] caption_bry) { + this.caption_bry = caption_bry; + return this; + } + public void Write_tkn(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_tkn_grp grp, int sub_idx, Xop_tkn_itm tkn) { + bfr.Add(caption_bry); + } +} +class Xop_lnki_caption_wtr_tkn implements Xop_lnki_caption_wtr { + private Xoh_html_wtr html_wtr; + public Xop_lnki_caption_wtr_tkn(Xoh_html_wtr html_wtr) { + this.html_wtr = html_wtr; + } + public void Write_tkn(Xop_ctx ctx, Xoh_html_wtr_ctx hctx, Bry_bfr bfr, byte[] src, Xop_tkn_grp grp, int sub_idx, Xop_tkn_itm tkn) { + html_wtr.Write_tkn(bfr, ctx, hctx, src, grp, sub_idx, tkn); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_page_mgr.java b/400_xowa/src/gplx/xowa/html/Xoh_page_mgr.java new file mode 100644 index 000000000..7bced870c --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_page_mgr.java @@ -0,0 +1,57 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xoh_page_mgr implements GfoInvkAble { + public boolean Font_enabled() {return font_enabled;} private boolean font_enabled = false; + public void Font_enabled_(boolean v) {font_enabled = v;} + public String Font_name() {return font_name;} private String font_name = "Arial"; + public float Font_size() {return font_size;} private float font_size = Font_size_default; + public void Font_size_(float v) { + font_size = v; + this.Font_css_bry_update(); + } + public Bry_fmtr Font_css_fmtr() {return font_css_fmtr;} private Bry_fmtr font_css_fmtr = Bry_fmtr.new_("body {font-family: ~{font_name}; font-size: ~{font_size}px;}", "font_name", "font_size"); + public Bry_fmtr Content_code_fmtr() {return content_code_fmtr;} private Bry_fmtr content_code_fmtr = Bry_fmtr.new_("
    ~{page_text}
    ", "page_text"); + private void Font_css_fmtr_(byte[] bry) { + font_css_fmtr.Fmt_(bry); + Font_css_bry_update(); + } + public byte[] Font_css_bry() {return font_css_bry;} + public void Font_css_bry_update() { + font_css_bry = font_css_fmtr.Bld_bry_many(Bry_bfr.new_(), font_name, font_size); + } private byte[] font_css_bry = Bry_.Empty; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_font_name)) return font_name; + else if (ctx.Match(k, Invk_font_name_)) {font_name = m.ReadStr("v"); this.Font_css_bry_update();} + else if (ctx.Match(k, Invk_font_size)) return font_size; + else if (ctx.Match(k, Invk_font_size_)) {font_size = m.ReadFloat("v"); this.Font_css_bry_update();} + else if (ctx.Match(k, Invk_font_css_fmt)) return String_.new_utf8_(font_css_fmtr.Fmt()); + else if (ctx.Match(k, Invk_font_css_fmt_)) Font_css_fmtr_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_font_enabled)) return Yn.X_to_str(font_enabled); + else if (ctx.Match(k, Invk_font_enabled_)) font_enabled = m.ReadYn("v"); + else if (ctx.Match(k, Invk_content_code_fmt)) return String_.new_utf8_(content_code_fmtr.Fmt()); + else if (ctx.Match(k, Invk_content_code_fmt_)) content_code_fmtr.Fmt_(m.ReadBry("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_font_name = "font_name", Invk_font_name_ = "font_name_", Invk_font_size = "font_size", Invk_font_size_ = "font_size_" + , Invk_font_css_fmt = "font_css_fmt", Invk_font_css_fmt_ = "font_css_fmt_", Invk_font_enabled = "font_enabled", Invk_font_enabled_ = "font_enabled_" + , Invk_content_code_fmt = "content_code_fmt", Invk_content_code_fmt_ = "content_code_fmt_" + ; + public static final float Font_size_default = 16; +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_mgr.java b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_mgr.java new file mode 100644 index 000000000..b159cda8a --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_mgr.java @@ -0,0 +1,132 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.gui.*; +public class Xoh_page_wtr_mgr implements GfoInvkAble { + private Xoh_page_wtr_wkr edit_wtr, html_wtr, read_wtr; private Xoa_app app; + public Xoh_page_wtr_mgr(Xoa_app app, boolean html_capable) { + this.app = app; + page_edit_fmtr.Fmt_(String_.Concat_lines_nl_skip_last + ( "" + , "" + , " " + , " " + , "" + , "" + , " " + , "" + , "" + )); + this.html_capable = html_capable; + read_wtr = new Xoh_page_wtr_wkr(Xog_page_mode.Tid_read); + edit_wtr = new Xoh_page_wtr_wkr(Xog_page_mode.Tid_edit); + html_wtr = new Xoh_page_wtr_wkr(Xog_page_mode.Tid_html); + } + public boolean Html_capable() {return html_capable;} public Xoh_page_wtr_mgr Html_capable_(boolean v) {html_capable = v; return this;} private boolean html_capable; + public Bry_fmtr Page_read_fmtr() {return page_read_fmtr;} + public byte[] Css_common_bry() {return css_common_bry;} public Xoh_page_wtr_mgr Css_common_bry_(Io_url v) {css_common_bry = app.Url_converter_fsys().Encode_http(v); return this;} private byte[] css_common_bry; + public byte[] Css_wiki_bry() {return css_wiki_bry;} public Xoh_page_wtr_mgr Css_wiki_bry_(Io_url v) {css_wiki_bry = app.Url_converter_fsys().Encode_http(v); return this;} private byte[] css_wiki_bry; + private Bry_fmtr page_read_fmtr = Bry_fmtr.new_("" + , "page_id", "page_name", "page_title", "page_content_sub", "page_data", "page_langs", "page_modified_on_msg", "page_lang_ltr" + , "html_css_common_path", "html_css_wiki_path", "html_content_editable" + , "xowa_head" + , "portal_div_personal", "portal_div_ns", "portal_div_view" + , "portal_div_logo", "portal_div_home", "portal_div_xtn", "portal_div_wikis", "portal_sidebar" + , "edit_div_rename", "edit_div_preview" + , "app_version", "app_build_date", "app_root_dir", "js_article_view_vars", "js_wikidata", "js_edit_toolbar", "xowa_mode_is_server" + ); + public Bry_fmtr Page_edit_fmtr() {return page_edit_fmtr;} private Bry_fmtr page_edit_fmtr = Bry_fmtr.new_("" + , "page_id", "page_name", "page_title", "page_content_sub", "page_data", "page_langs", "page_modified_on_msg", "page_lang_ltr" + , "html_css_common_path", "html_css_wiki_path", "html_content_editable" + , "xowa_head" + , "portal_div_personal", "portal_div_ns", "portal_div_view" + , "portal_div_logo", "portal_div_home", "portal_div_xtn", "portal_div_wikis", "portal_sidebar" + , "edit_div_rename", "edit_div_preview" + , "app_version", "app_build_date", "app_root_dir", "js_article_view_vars", "js_wikidata", "js_edit_toolbar", "xowa_mode_is_server" + ); + public Bry_fmtr Page_html_fmtr() {return page_html_fmtr;} private Bry_fmtr page_html_fmtr = Bry_fmtr.new_("" + , "page_id", "page_name", "page_title", "page_content_sub", "page_data", "page_langs", "page_modified_on_msg", "page_lang_ltr" + , "html_css_common_path", "html_css_wiki_path", "html_content_editable" + , "xowa_head" + , "portal_div_personal", "portal_div_ns", "portal_div_view" + , "portal_div_logo", "portal_div_home", "portal_div_xtn", "portal_div_wikis", "portal_sidebar" + , "edit_div_rename", "edit_div_preview" + , "app_version", "app_build_date", "app_root_dir", "js_article_view_vars", "js_wikidata", "js_edit_toolbar", "xowa_mode_is_server" + ); + private Bry_bfr tmp_bfr = Bry_bfr.reset_(255); + public byte[] Edit_rename_div_bry(Xoa_ttl ttl) { + return div_edit_rename_fmtr.Bld_bry_many(tmp_bfr, ttl.Full_db()); + } private Bry_fmtr div_edit_rename_fmtr = Bry_fmtr.new_(String_.Concat_lines_nl + ( " " + , " " + , " Rename page" + , " " + , " " + , " Special:MovePage" + , " " + ), "src_full_db"); + public void Init_(boolean v) {init = v;} private boolean init = true; + public byte[] Gen(Xoa_page page, byte output_tid) { + Xoh_page_wtr_wkr wtr = Wkr(output_tid); + Xow_wiki wiki = page.Wiki(); + if (init) { + init = false; + page_edit_fmtr.Eval_mgr_(wiki.Eval_mgr()); + page_read_fmtr.Eval_mgr_(wiki.Eval_mgr()); + page_html_fmtr.Eval_mgr_(wiki.Eval_mgr()); + } + Bry_bfr tmp_bfr = wiki.Utl_bry_bfr_mkr().Get_m001(); + byte[] bry = wtr.Write(this, page, wiki.Ctx(), tmp_bfr); + tmp_bfr.Mkr_rls(); + return bry; + } + public Xoh_page_wtr_wkr Wkr(byte output_tid) { + switch (output_tid) { + case Xog_page_mode.Tid_edit: return edit_wtr; + case Xog_page_mode.Tid_html: return html_wtr; + case Xog_page_mode.Tid_read: return read_wtr; + default: throw Err_.unhandled(output_tid); + } + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_page_read_)) page_read_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_page_edit_)) page_edit_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_page_html_)) page_html_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_xowa_div_edit_rename_)) div_edit_rename_fmtr.Fmt_(m.ReadBry("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + public static final String Invk_page_read_ = "page_read_", Invk_page_edit_ = "page_edit_", Invk_page_html_ = "page_html_", Invk_xowa_div_edit_rename_ = "xowa_div_edit_rename_"; +} +/* +NOTE_1:xowa_anchor_button +. used for media (WP forces javascript with oggplayer. wanted "simpler" model) +. display:inline-block; must be set for centering to work; see USSR and anthem; (display:block; must be enabled in order for padding to work) +. text-align:center; forces img to be in center + +General notes: +. contentSub div is needed; EX.WP: Battle of Spotsylvania Court House +*/ diff --git a/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_mgr_tst.java b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_mgr_tst.java new file mode 100644 index 000000000..cbdef728f --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_mgr_tst.java @@ -0,0 +1,33 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +import gplx.xowa.gui.*; import gplx.xowa.html.*; import gplx.xowa.html.portal.*; +public class Xoh_page_wtr_mgr_tst { + @Before public void init() {} + @Test public void Logo_has_correct_main_page() { // PURPOSE: Logo href should be "/site/en.wikipedia.org/wiki/", not "/wiki/Main_Page" + Xoa_app app = Xoa_app_fxt.app_(); + Xow_wiki wiki = Xoa_app_fxt.wiki_tst_(app); + Xow_portal_mgr portal_mgr = wiki.Html_mgr().Portal_mgr(); + GfoInvkAble_.InvkCmd_val(portal_mgr, Xow_portal_mgr.Invk_div_logo_, Bry_.new_ascii_("~{portal_nav_main_href}")); + portal_mgr.Init_assert(); + Xoh_page_wtr_mgr page_wtr_mgr = new Xoh_page_wtr_mgr(app, true); + page_wtr_mgr.Gen(wiki.Ctx().Cur_page(), Xog_page_mode.Tid_read); + Tfds.Eq(String_.new_ascii_(portal_mgr.Div_logo_bry()), "/site/en.wikipedia.org/wiki/"); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr.java b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr.java new file mode 100644 index 000000000..768afdd59 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr.java @@ -0,0 +1,153 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.html.*; import gplx.xowa.html.portal.*; +import gplx.xowa.wikis.*; import gplx.xowa.gui.*; import gplx.xowa.xtns.wdatas.*; +public class Xoh_page_wtr_wkr implements Bry_fmtr_arg { + private Xop_ctx ctx; private Xoa_page page; private Bry_bfr tmp_bfr = Bry_bfr.reset_(255); + public Xoh_page_wtr_wkr(byte page_mode) {this.page_mode = page_mode;} private byte page_mode; + public Wdata_xwiki_link_wtr Wdata_lang_wtr() {return wtr_page_lang;} private Wdata_xwiki_link_wtr wtr_page_lang = new Wdata_xwiki_link_wtr(); + public Xoh_page_wtr_wkr Page_(Xoa_page v) {this.page = v; return this;} + public Xoh_page_wtr_wkr Mgr_(Xoh_page_wtr_mgr v) {this.mgr = v; return this;} private Xoh_page_wtr_mgr mgr; + public boolean Ctgs_enabled() {return ctgs_enabled;} public Xoh_page_wtr_wkr Ctgs_enabled_(boolean v) {ctgs_enabled = v; return this;} private boolean ctgs_enabled = true; + public byte[] Write(Xoh_page_wtr_mgr mgr, Xoa_page page, Xop_ctx ctx, Bry_bfr html_bfr) { + this.mgr = mgr; this.page = page; this.ctx = ctx; + Xow_wiki wiki = page.Wiki(); Xoa_app app = wiki.App(); + ctx.Cur_page_(page); // HACK: must update page for toc_mgr; WHEN: Xoa_page rewrite + Bry_fmtr fmtr = null; + if (mgr.Html_capable()) { + wtr_page_lang.Page_(page); + byte view_mode = page_mode; + switch (page_mode) { + case Xog_page_mode.Tid_edit: fmtr = mgr.Page_edit_fmtr(); break; + case Xog_page_mode.Tid_html: fmtr = mgr.Page_read_fmtr(); view_mode = Xog_page_mode.Tid_read; break; // set view_mode to read, so that "read" is highlighted in HTML + case Xog_page_mode.Tid_read: fmtr = mgr.Page_read_fmtr(); + ctx.Cur_page().Lnki_redlinks_mgr().Page_bgn(); // not sure if this is the best place to put it, but redlinks (a) must only fire once; (b) must fire before html generation; (c) cannot fire during edit (preview will handle separately) + break; + } + Write_page(html_bfr, app, wiki, mgr, page, view_mode, fmtr, this); + if (page_mode == Xog_page_mode.Tid_html) // if html, write page again, but wrap it in html skin this time + Write_page(html_bfr, app, wiki, mgr, page, page_mode, mgr.Page_html_fmtr(), Html_utl.Escape_html_as_str(html_bfr.XtoStrAndClear())); + wtr_page_lang.Page_(null); + } + else + XferAry(html_bfr, 0); + this.page = null; + return html_bfr.XtoAryAndClear(); + } + private void Write_page(Bry_bfr html_bfr, Xoa_app app, Xow_wiki wiki, Xoh_page_wtr_mgr mgr, Xoa_page page, byte view_tid, Bry_fmtr fmtr, Object page_data) { + byte[] custom_html = page.Html_data().Custom_html(); + if (custom_html != null) { + html_bfr.Add(custom_html); + return; + } + DateAdp page_modified_on_dte = page.Revision_data().Modified_on(); + byte[] page_modified_on_msg = wiki.Msg_mgr().Val_by_id_args(Xol_msg_itm_.Id_portal_lastmodified, page_modified_on_dte.XtoStr_fmt_yyyy_MM_dd(), page_modified_on_dte.XtoStr_fmt_HHmm()); + byte[] html_content_editable = wiki.Gui_mgr().Cfg_browser().Content_editable() ? Content_editable_bry : Bry_.Empty; + byte[] page_content_sub = Xoh_page_wtr_wkr_.Bld_page_content_sub(app, wiki, page, tmp_bfr); + byte[] js_wikidata_bry = Wdata_wiki_mgr.Wiki_page_is_json(wiki.Domain_tid(), page.Ttl().Ns().Id()) ? app.User().Lang().Fragment_mgr().Html_js_wikidata() : Bry_.Empty; + byte[] js_edit_toolbar_bry = view_tid == Xog_page_mode.Tid_edit ? wiki.Fragment_mgr().Html_js_edit_toolbar() : Bry_.Empty; + page.Xtn_mgr().Exec(); + Xow_portal_mgr portal_mgr = wiki.Html_mgr().Portal_mgr().Init_assert(); + fmtr.Bld_bfr_many(html_bfr, page.Revision_data().Id() + , Xoh_page_wtr_wkr_.Bld_page_name(tmp_bfr, page.Ttl(), null) // NOTE: page_name does not show display_title (). always pass in null + , Xoh_page_wtr_wkr_.Bld_page_name(tmp_bfr, page.Ttl(), page.Display_ttl()) + , page_content_sub, page_data, wtr_page_lang, page_modified_on_msg, page.Lang().Dir_bry() + , mgr.Css_common_bry(), mgr.Css_wiki_bry(), html_content_editable + , page.Html_data().Module_mgr().Init(app, wiki, page).Init_dflts() + , portal_mgr.Div_personal_bry(), portal_mgr.Div_ns_bry(app.Utl_bry_bfr_mkr(), page.Ttl(), wiki.Ns_mgr()), portal_mgr.Div_view_bry(app.Utl_bry_bfr_mkr(), view_tid, page.Html_data().Search_text()) + , portal_mgr.Div_logo_bry(), portal_mgr.Div_home_bry(), page.Html_data().Portal_div_xtn(), portal_mgr.Div_wikis_bry(app.Utl_bry_bfr_mkr()), portal_mgr.Sidebar_mgr().Html_bry() + , mgr.Edit_rename_div_bry(page.Ttl()), page.Html_data().Edit_preview() + , Xoa_app_.Version, Xoa_app_.Build_date + , app.Fsys_mgr().Root_dir().To_http_file_bry() + , wiki.Fragment_mgr().Html_js_table(), js_wikidata_bry, js_edit_toolbar_bry, app.Tcp_server().Running_str() + ); + Xoh_page_wtr_wkr_.Bld_head_end(html_bfr, page); + Xoh_page_wtr_wkr_.Bld_html_end(html_bfr, page); + } + public void XferAry(Bry_bfr html_bfr, int idx) { + Xow_wiki wiki = page.Wiki(); Xoa_app app = wiki.App(); + byte[] data_raw = page.Data_raw(); + int ns_id = page.Ttl().Ns().Id(); + byte page_tid = Xow_page_tid.Identify(wiki.Domain_tid(), ns_id, page.Ttl().Page_db()); + if (page_mode == Xog_page_mode.Tid_edit) { + Write_mode_edit(html_bfr, data_raw, ns_id, page_tid); + return; + } + int bfr_page_bgn = html_bfr.Len(); + boolean page_tid_uses_pre = false; + switch (page_tid) { + case Xow_page_tid.Tid_json: + app.Wiki_mgr().Wdata_mgr().Write_json_as_html(html_bfr, data_raw); + break; + case Xow_page_tid.Tid_js: + case Xow_page_tid.Tid_css: + case Xow_page_tid.Tid_lua: + Xoh_html_wtr_escaper.Escape(page.Wiki().App(), tmp_bfr, data_raw, 0, data_raw.length, false, false); + app.Html_mgr().Page_mgr().Content_code_fmtr().Bld_bfr_many(html_bfr, tmp_bfr); + tmp_bfr.Clear(); + page_tid_uses_pre = true; + break; + case Xow_page_tid.Tid_wikitext: + if (ns_id == Xow_ns_.Id_mediaWiki) { // if MediaWiki and wikitext, must be a message; convert args back to php; DATE:2014-06-13 + html_bfr.Add(gplx.xowa.apps.Xoa_gfs_php_mgr.Xto_php(tmp_bfr, Bool_.N, data_raw)); + return; + } + if (ns_id == Xow_ns_.Id_file) // if File ns, add boilerplate header + app.File_main_wkr().Bld_html(wiki, ctx, html_bfr, page.Ttl(), wiki.Cfg_file_page(), page.File_queue()); + gplx.xowa.html.tidy.Xoh_tidy_mgr tidy_mgr = app.Html_mgr().Tidy_mgr(); + boolean tidy_enabled = tidy_mgr.Enabled(); + Bry_bfr hdom_bfr = tidy_enabled ? app.Utl_bry_bfr_mkr().Get_m001() : html_bfr; // if tidy, then write to tidy_bfr; note that html_bfr already has and written to it, so this can't be passed to tidy; DATE:2014-06-11 + wiki.Html_mgr().Html_wtr().Write_all(hdom_bfr, page.Wiki().Ctx(), page.Root().Data_mid(), page.Root()); + if (tidy_enabled) { + tidy_mgr.Run_tidy_html(page, hdom_bfr); + html_bfr.Add_bfr_and_clear(hdom_bfr); + hdom_bfr.Mkr_rls(); + } + if (ns_id == Xow_ns_.Id_category) // if Category, render rest of html (Subcategories; Pages; Files); note that a category may have other html which requires wikitext processing + wiki.Html_mgr().Ns_ctg().Bld_html(page, html_bfr); + int ctgs_len = page.Category_list().length; // add Categories + if (ctgs_enabled && ctgs_len > 0) { + app.Usr_dlg().Prog_many("", "", "loading categories: count=~{0}", ctgs_len); + if (app.Ctg_mgr().Pagecats_grouping_enabled()) + app.Ctg_mgr().Pagectgs_wtr().Write(html_bfr, wiki, page); + else + wiki.Html_mgr().Ctg_mgr().Bld(html_bfr, page, ctgs_len); + } + break; + } + if ( wiki.Domain_tid() != Xow_wiki_domain_.Tid_home // allow home wiki to use javascript + && !page_tid_uses_pre) { // if .js, .css or .lua, skip test; may have js fragments, but entire text is escaped and put in pre; don't show spurious warning; DATE:2013-11-21 + app.Html_mgr().Js_cleaner().Clean_bfr(wiki, page.Ttl(), html_bfr, bfr_page_bgn); + } + } + private void Write_mode_edit(Bry_bfr html_bfr, byte[] data_raw, int ns_id, byte page_tid) { + if ( ns_id == Xow_ns_.Id_mediaWiki // if MediaWiki and wikitext, must be a message; convert args back to php; DATE:2014-06-13 + && page_tid == Xow_page_tid.Tid_wikitext + ) + data_raw = gplx.xowa.apps.Xoa_gfs_php_mgr.Xto_php(tmp_bfr, Bool_.N, data_raw); + int data_raw_len = data_raw.length; + if (mgr.Html_capable()) + Xoh_html_wtr_escaper.Escape(page.Wiki().App(), html_bfr, data_raw, 0, data_raw_len, false, false); // NOTE: must escape; assume that browser will automatically escape (<) (which Mozilla does) + else + html_bfr.Add(data_raw); + if (data_raw_len > 0) // do not add nl if empty String + html_bfr.Add_byte_nl(); // per MW:EditPage.php: "Ensure there's a newline at the end, otherwise adding lines is awkward." + } + private static final byte[] Content_editable_bry = Bry_.new_ascii_(" contenteditable=\"true\""); +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr_.java b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr_.java new file mode 100644 index 000000000..977eaecd9 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr_.java @@ -0,0 +1,55 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.html.*; +public class Xoh_page_wtr_wkr_ { + public static byte[] Bld_page_content_sub(Xoa_app app, Xow_wiki wiki, Xoa_page page, Bry_bfr tmp_bfr) { + byte[] page_content_sub = page.Html_data().Content_sub(); // contentSub exists; SEE: {{#isin}} + byte[] redirect_msg = Xop_redirect_mgr.Bld_redirect_msg(app, wiki, page); + return tmp_bfr.Concat_skip_empty(Xoh_consts.Br, page_content_sub, redirect_msg).XtoAryAndClear(); + } + public static byte[] Bld_page_name(Bry_bfr tmp_bfr, Xoa_ttl ttl, byte[] display_ttl) { + if (display_ttl != null) return display_ttl; // display_ttl explicitly set; use it + if (ttl.Ns().Id() == Xow_ns_.Id_special) { // special: omit query args, else excessively long titles: EX:"Special:Search/earth?fulltext=y&xowa page index=1" + tmp_bfr.Add(ttl.Ns().Name_txt_w_colon()).Add(ttl.Page_txt_wo_qargs()); + return tmp_bfr.XtoAryAndClear(); + } + else + return ttl.Full_txt(); // NOTE: include ns with ttl as per defect d88a87b3 + } + public static void Bld_head_end(Bry_bfr html_bfr, Xoa_page page) { + byte[] head_end = page.Html_data().Custom_head_end(); + if (head_end == null) return; + int insert_pos = Bry_finder.Find_fwd(html_bfr.Bfr(), Html_tags.Head_rhs); + if (insert_pos == Bry_finder.Not_found) { + page.App().Usr_dlg().Warn_many("", "", "could not find "); + return; + } + html_bfr.Insert_at(insert_pos, head_end); + } + public static void Bld_html_end(Bry_bfr html_bfr, Xoa_page page) { + byte[] html_end = page.Html_data().Custom_html_end(); + if (html_end == null) return; + int insert_pos = Bry_finder.Find_bwd(html_bfr.Bfr(), Html_tags.Html_rhs, html_bfr.Len()); + if (insert_pos == Bry_finder.Not_found) { + page.App().Usr_dlg().Warn_many("", "", "could not find "); + return; + } + html_bfr.Insert_at(insert_pos, html_end); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr_tst.java b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr_tst.java new file mode 100644 index 000000000..c85e0a16e --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_page_wtr_wkr_tst.java @@ -0,0 +1,75 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +import gplx.xowa.gui.*; +public class Xoh_page_wtr_wkr_tst { + @Before public void init() {fxt.Clear();} private Xoh_page_wtr_wkr_fxt fxt = new Xoh_page_wtr_wkr_fxt(); + @Test public void Page_name() { + fxt.Test_page_name_by_ttl("Earth", "Earth"); + fxt.Test_page_name_by_ttl("File:A.png", "File:A.png"); + fxt.Test_page_name_by_ttl("Special:Search/earth?fulltext=y", "Special:Search/earth"); + fxt.Test_page_name_by_ttl("Special:Search/earth", "Special:Search/earth"); + fxt.Test_page_name_by_display("Special:Allpages", "All pages", "All pages"); + } + @Test public void Edit() { + fxt.Test_edit(" ", "&#9;\n"); // NOTE: cannot by or will show up in edit box as "\t" and save as "\t" instead of + } + @Test public void Css() { + fxt.App().Html_mgr().Page_mgr().Content_code_fmtr().Fmt_("
    ~{page_text}
    "); + fxt.Test_read("MediaWiki:Common.css", ".xowa {}", "
    .xowa {}
    "); + fxt.App().Html_mgr().Page_mgr().Content_code_fmtr().Fmt_("
    ~{page_text}
    "); + } + @Test public void Amp_disable() { // PURPOSE: in js documents; " should be rendered as ", not as "; DATE:2013-11-07 + fxt.Test_read("MediaWiki:Gadget.js", """, "
    &quot;
    "); + } +} +class Xoh_page_wtr_wkr_fxt { + public void Clear() { + if (app == null) { + app = Xoa_app_fxt.app_(); + wiki = Xoa_app_fxt.wiki_tst_(app); + } + } private Bry_bfr tmp_bfr = Bry_bfr.reset_(255); private Xow_wiki wiki; + public Xoa_app App() {return app;} private Xoa_app app; + public void Test_page_name_by_display(String ttl, String display, String expd) { + Tfds.Eq(expd, String_.new_ascii_(Xoh_page_wtr_wkr_.Bld_page_name(tmp_bfr, Xoa_ttl.parse_(wiki, Bry_.new_ascii_(ttl)), Bry_.new_ascii_(display)))); + } + public void Test_page_name_by_ttl(String raw, String expd) { + Tfds.Eq(expd, String_.new_ascii_(Xoh_page_wtr_wkr_.Bld_page_name(tmp_bfr, Xoa_ttl.parse_(wiki, Bry_.new_ascii_(raw)), null))); + } + public void Test_edit(String raw, String expd) { + wiki.Html_mgr().Page_wtr_mgr().Html_capable_(true); + Xoa_page page = wiki.Ctx().Cur_page(); + page.Data_raw_(Bry_.new_utf8_(raw)); + Xoh_page_wtr_mgr mgr = wiki.Html_mgr().Page_wtr_mgr(); + Xoh_page_wtr_wkr wkr = mgr.Wkr(Xog_page_mode.Tid_edit).Page_(page).Mgr_(mgr); + wkr.XferAry(tmp_bfr, 0); + Tfds.Eq(expd, tmp_bfr.XtoStrAndClear()); + } + public void Test_read(String page_name, String page_text, String expd) { + wiki.Html_mgr().Page_wtr_mgr().Html_capable_(true); + Xoa_page page = wiki.Ctx().Cur_page(); + page.Ttl_(Xoa_ttl.parse_(wiki, Bry_.new_ascii_(page_name))); + page.Data_raw_(Bry_.new_utf8_(page_text)); + Xoh_page_wtr_mgr mgr = wiki.Html_mgr().Page_wtr_mgr(); + Xoh_page_wtr_wkr wkr = mgr.Wkr(Xog_page_mode.Tid_read).Page_(page).Mgr_(mgr); + wkr.XferAry(tmp_bfr, 0); + Tfds.Eq(expd, tmp_bfr.XtoStrAndClear()); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_xtn_itm.java b/400_xowa/src/gplx/xowa/html/Xoh_xtn_itm.java new file mode 100644 index 000000000..024c3b81d --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_xtn_itm.java @@ -0,0 +1,22 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public interface Xoh_xtn_itm { + byte[] Key(); + void Exec(); +} diff --git a/400_xowa/src/gplx/xowa/html/Xoh_xtn_mgr.java b/400_xowa/src/gplx/xowa/html/Xoh_xtn_mgr.java new file mode 100644 index 000000000..39aaaccf6 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xoh_xtn_mgr.java @@ -0,0 +1,31 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xoh_xtn_mgr { + private OrderedHash itms = OrderedHash_.new_bry_(); + public Xoh_xtn_itm Get_or_null(byte[] key) {return (Xoh_xtn_itm)itms.Fetch(key);} + public void Clear() {itms.Clear();} + public void Add(Xoh_xtn_itm itm) {itms.Add(itm.Key(), itm);} + public void Exec() { + int len = itms.Count(); + for (int i = 0; i < len; i++) { + Xoh_xtn_itm itm = (Xoh_xtn_itm)itms.FetchAt(i); + itm.Exec(); + } + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xohp_ctg_grp_mgr.java b/400_xowa/src/gplx/xowa/html/Xohp_ctg_grp_mgr.java new file mode 100644 index 000000000..1d9f14b43 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xohp_ctg_grp_mgr.java @@ -0,0 +1,60 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xohp_ctg_grp_mgr { + final Bry_fmtr grp_fmtr = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " ~{grp_lbl}" + , "
      ~{grp_itms}" + , "
    " + , "
    " + , "
    " + ), "grp_lbl", "grp_itms") + ; + final Bry_fmtr itm_fmtr = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
  • " + , " ~{itm_text}" + , "
  • " + ), "itm_href", "itm_title", "itm_text" + ); + Xoh_ctg_itm_fmtr itm_mgr = new Xoh_ctg_itm_fmtr(); + public void Bld(Bry_bfr bfr, Xoa_page page, int ctgs_len) { + byte[] categories_lbl = page.Wiki().Msg_mgr().Val_by_id(Xol_msg_itm_.Id_ctg_tbl_hdr); + itm_mgr.Set(page, itm_fmtr); + grp_fmtr.Bld_bfr_many(bfr, categories_lbl, itm_mgr); + } +} +class Xoh_ctg_itm_fmtr implements Bry_fmtr_arg { + public void Set(Xoa_page page, Bry_fmtr itm_fmtr) {this.page = page; this.itm_fmtr = itm_fmtr;} private Xoa_page page; Bry_fmtr itm_fmtr; + public void XferAry(Bry_bfr bfr, int idx) { + int ctgs_len = page.Category_list().length; + Bry_bfr tmp_bfr = page.Wiki().App().Utl_bry_bfr_mkr().Get_b128(); + Bry_bfr tmp_href = page.Wiki().App().Utl_bry_bfr_mkr().Get_b128(); + byte[] ctg_prefix = page.Wiki().Ns_mgr().Ns_category().Name_db_w_colon(); + for (int i = 0; i < ctgs_len; i++) { + byte[] page_name = page.Category_list()[i]; + tmp_bfr.Add(ctg_prefix).Add(page_name); + page.Wiki().App().Href_parser().Build_to_bfr(tmp_href, page.Wiki(), tmp_bfr.XtoAryAndClear()); + itm_fmtr.Bld_bfr(bfr, tmp_href.XtoAryAndClear(), page_name, page_name); + } + tmp_bfr.Mkr_rls(); + tmp_href.Mkr_rls(); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xohp_ctg_grp_mgr_tst.java b/400_xowa/src/gplx/xowa/html/Xohp_ctg_grp_mgr_tst.java new file mode 100644 index 000000000..3a1aa7238 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xohp_ctg_grp_mgr_tst.java @@ -0,0 +1,56 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xohp_ctg_grp_mgr_tst { + Xoh_ctg_mgr_fxt fxt = new Xoh_ctg_mgr_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Basic() { + fxt.Init_ctgs("A", "B").Test_html(String_.Concat_lines_nl + ( "
    " + , "
    " + , " Categories" + , "
      " + , "
    • " + , " A" + , "
    • " + , "
    • " + , " B" + , "
    • " + , "
    " + , "
    " + , "
    " + )); + } +} +class Xoh_ctg_mgr_fxt { + public Xoh_ctg_mgr_fxt Clear() { + app = Xoa_app_fxt.app_(); + wiki = Xoa_app_fxt.wiki_tst_(app); + ctg_grp_mgr = new Xohp_ctg_grp_mgr(); + return this; + } private Xohp_ctg_grp_mgr ctg_grp_mgr; Xoa_app app; Xow_wiki wiki; Bry_bfr tmp_bfr = Bry_bfr.new_(); + public Xoh_ctg_mgr_fxt Init_ctgs(String... v) {init_ctgs = v; return this;} private String[] init_ctgs; + public void Test_html(String expd) { + byte[][] ctgs_bry_ary = Bry_.Ary(init_ctgs); + Xoa_page page = wiki.Ctx().Cur_page(); + page.Category_list_(ctgs_bry_ary); + ctg_grp_mgr.Bld(tmp_bfr, page, ctgs_bry_ary.length); + Tfds.Eq_str_lines(expd, tmp_bfr.XtoStrAndClear()); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xohp_title_wkr.java b/400_xowa/src/gplx/xowa/html/Xohp_title_wkr.java new file mode 100644 index 000000000..a8be64862 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xohp_title_wkr.java @@ -0,0 +1,65 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +public class Xohp_title_wkr implements Bry_fmtr_arg { + public Xohp_title_wkr Set(byte[] src, Xop_tkn_itm tkn) {this.src = src; this.tkn = tkn; return this;} + public void XferAry(Bry_bfr bfr, int idx) { + Bld_recurse(bfr, tkn); + } + public void Bld_recurse(Bry_bfr bfr, Xop_tkn_itm tkn) { + switch (tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_newLine: case Xop_tkn_itm_.Tid_space: case Xop_tkn_itm_.Tid_txt: // NOTE: atomic tkns which will have no subs + Write_atr_text(bfr, src, tkn.Src_bgn(), tkn.Src_end()); + break; + case Xop_tkn_itm_.Tid_arg_nde: // caption tkns have no subs; just a key and a val; write val + Arg_nde_tkn arg_nde = (Arg_nde_tkn)tkn; + Bld_recurse(bfr, arg_nde.Val_tkn()); + break; + default: // all other tkns, just iterate over subs for txt tkns + if (tkn.Tkn_tid() == Xop_tkn_itm_.Tid_xnde) { + Xop_xnde_tkn xnde = (Xop_xnde_tkn)tkn; + if (xnde.Tag().Id() == Xop_xnde_tag_.Tid_ref) { // if ref, disable tkn + gplx.xowa.xtns.refs.Ref_nde ref_xnde = (gplx.xowa.xtns.refs.Ref_nde)xnde.Xnde_xtn(); + ref_xnde.Exists_in_lnki_title_(true); // ref found during html_title_wkr's generation; mark ref; will be ignored by references_html_wtr later; DATE:2014-03-05 + } + } + int len = tkn.Subs_len(); + for (int i = 0; i < len; i++) { + Xop_tkn_itm sub = tkn.Subs_get(i); + Bld_recurse(bfr, sub); + } + break; + } + } + public static void Write_atr_text(Bry_bfr bfr, byte[] src, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = src[i]; + switch (b) { + case Byte_ascii.NewLine: case Byte_ascii.CarriageReturn: case Byte_ascii.Tab: // NOTE: escape ws so that it renders correctly in tool tips + case Byte_ascii.Quote: case Byte_ascii.Lt: case Byte_ascii.Gt: case Byte_ascii.Amp: // NOTE: escape possible javascript injection characters + bfr.Add(Escape_bgn); + bfr.Add_int_variable(b); + bfr.Add_byte(Byte_ascii.Semic); + break; + default: bfr.Add_byte(b); break; + } + } + } + byte[] src; Xop_tkn_itm tkn; + public static final byte[] Escape_bgn = Bry_.new_ascii_("&#"); +} diff --git a/400_xowa/src/gplx/xowa/html/Xohp_title_wkr_tst.java b/400_xowa/src/gplx/xowa/html/Xohp_title_wkr_tst.java new file mode 100644 index 000000000..3af978b1a --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xohp_title_wkr_tst.java @@ -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 . +*/ +package gplx.xowa.html; import gplx.*; import gplx.xowa.*; +import org.junit.*; +public class Xohp_title_wkr_tst { + @Before public void init() {fxt.Clear();} private Xohp_title_wkr_fxt fxt = new Xohp_title_wkr_fxt(); + @Test public void Basic() { + fxt.Test_parse("a b c", "a b c"); + fxt.Test_parse("a ''b'' c", "a b c"); + fxt.Test_parse("a b c", "a b c"); + fxt.Test_parse("a\nb", "a b"); + fxt.Test_parse("a\"b", "a"b"); + } +} +class Xohp_title_wkr_fxt { + private Xop_fxt fxt = new Xop_fxt(); + Bry_bfr bfr = Bry_bfr.new_(); + Xohp_title_wkr title_wkr = new Xohp_title_wkr(); + public Xohp_title_wkr_fxt Clear() {return this;} + public void Test_parse(String raw, String expd) { + byte[] raw_bry = Bry_.new_utf8_(raw); + Xop_root_tkn root = fxt.Ctx().Tkn_mkr().Root(raw_bry); + fxt.Parser().Parse_page_all_clear(root, fxt.Ctx(), fxt.Ctx().Tkn_mkr(), raw_bry); + title_wkr.Set(raw_bry, root).Bld_recurse(bfr, root); + Tfds.Eq(expd, bfr.XtoStrAndClear()); + } +} diff --git a/400_xowa/src/gplx/xowa/html/Xow_html_mgr.java b/400_xowa/src/gplx/xowa/html/Xow_html_mgr.java new file mode 100644 index 000000000..97d84e6b5 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/Xow_html_mgr.java @@ -0,0 +1,169 @@ +/* +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.xowa.html; import gplx.*; import gplx.xowa.*; +import gplx.xowa.ctgs.*; import gplx.xowa.xtns.gallery.*; +import gplx.xowa.html.portal.*; import gplx.xowa.html.tocs.*; import gplx.xowa.html.modules.*; +public class Xow_html_mgr implements GfoInvkAble { + public Xow_html_mgr(Xow_wiki wiki) { + this.wiki = wiki; + html_wtr = new Xoh_html_wtr(wiki, this); + Xoa_app app = wiki.App(); + page_wtr_mgr = new Xoh_page_wtr_mgr(app, app.Gui_mgr().Kit().Tid() != gplx.gfui.Gfui_kit_.Swing_tid); // reverse logic to handle swt,drd but not mem + Io_url file_dir = app.User().Fsys_mgr().App_img_dir().GenSubDir_nest("file"); + img_media_play_btn = app.Url_converter_fsys().Encode_http(file_dir.GenSubFil("play.png")); + img_media_info_btn = app.Url_converter_fsys().Encode_http(file_dir.GenSubFil("info.png")); + img_thumb_magnify = app.Url_converter_fsys().Encode_http(file_dir.GenSubFil("magnify-clip.png")); + img_xowa_protocol = app.Url_converter_fsys().Encode_http(app.User().Fsys_mgr().App_img_dir().GenSubFil_nest("xowa", "protocol.png")); + portal_mgr = new Xow_portal_mgr(wiki); + imgs_mgr = new Xoh_imgs_mgr(this); + module_mgr = new Xow_module_mgr(wiki); + } + public void Init_by_wiki(Xow_wiki wiki) { + html_wtr.Init_by_wiki(wiki); + module_mgr.Init_by_wiki(wiki); + } + public void Init_by_lang(Xol_lang lang) { + portal_mgr.Init_by_lang(lang); + } + public Xow_wiki Wiki() {return wiki;} private Xow_wiki wiki; + public Xoh_html_wtr Html_wtr() {return html_wtr;} private Xoh_html_wtr html_wtr; + public Xoh_page_wtr_mgr Page_wtr_mgr() {return page_wtr_mgr;} private Xoh_page_wtr_mgr page_wtr_mgr; + public Xow_portal_mgr Portal_mgr() {return portal_mgr;} private Xow_portal_mgr portal_mgr; + public Xow_toc_mgr Toc_mgr() {return toc_mgr;} private Xow_toc_mgr toc_mgr = new Xow_toc_mgr(); + public Xow_module_mgr Module_mgr() {return module_mgr;} private Xow_module_mgr module_mgr; + public int Img_thumb_width() {return img_thumb_width;} private int img_thumb_width = 220; + public byte[] Img_media_play_btn() {return img_media_play_btn;} private byte[] img_media_play_btn; + public byte[] Img_media_info_btn() {return img_media_info_btn;} private byte[] img_media_info_btn; + public byte[] Img_thumb_magnify() {return img_thumb_magnify;} private byte[] img_thumb_magnify; + public byte[] Img_xowa_protocol() {return img_xowa_protocol;} private byte[] img_xowa_protocol; + public boolean Img_suppress_missing_src() {return img_suppress_missing_src;} public Xow_html_mgr Img_suppress_missing_src_(boolean v) {img_suppress_missing_src = v; return this;} private boolean img_suppress_missing_src = true; + public Xohp_ctg_grp_mgr Ctg_mgr() {return ctg_mgr;} private Xohp_ctg_grp_mgr ctg_mgr = new Xohp_ctg_grp_mgr(); + public Xoctg_html_mgr Ns_ctg() {return ns_ctg;} private Xoctg_html_mgr ns_ctg = new Xoctg_html_mgr(); + public Xoh_imgs_mgr Imgs_mgr() {return imgs_mgr;} private Xoh_imgs_mgr imgs_mgr; + public Bry_fmtr Lnki_full_image() {return lnki_full_image;} Bry_fmtr lnki_full_image = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "\"~{alt}\"" + ), "elem_id", "href", "src", "width", "height", "alt", "lnki_title", "anchor_class", "anchor_rel", "anchor_title", "img_class" + ); + public Bry_fmtr Lnki_full_media() {return lnki_full_media;} Bry_fmtr lnki_full_media = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "~{lnki_caption}" + , "" + ), "lnki_src", "lnki_title", "lnki_caption" + ); + + public Bry_fmtr Lnki_thumb_core() {return lnki_thumb_core;} Bry_fmtr lnki_thumb_core = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last // REF.MW: Linker.php|makeImageLink2 + ( "
    " + , "
    " + , "~{lnki_content}" + , "
    " + , "
    " + , "" + ), "div_width", "lnki_halign", "lnki_content", "elem_id" + ); + public Bry_fmtr Lnki_thumb_file_image() {return lnki_thumb_file_image;} Bry_fmtr lnki_thumb_file_image = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( " ~{thumb_image}~{lnki_caption}~{lnki_alt}" + ), "thumb_image", "lnki_caption", "lnki_alt"); + public Bry_fmtr Lnki_thumb_file_video() {return lnki_thumb_file_video;} Bry_fmtr lnki_thumb_file_video = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "
    ~{video_thumb}~{play_btn}" + , "
    ~{lnki_caption}~{lnki_alt}" + ), "play_btn", "video_thumb", "lnki_caption", "lnki_alt"); + public Bry_fmtr Lnki_thumb_file_audio() {return lnki_thumb_file_audio;} Bry_fmtr lnki_thumb_file_audio = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "
    ~{play_btn}~{info_btn}" + , "
    ~{lnki_caption}~{lnki_alt}" + ), "play_btn", "info_btn", "lnki_caption", "lnki_alt"); + public Bry_fmtr Lnki_thumb_part_image() {return lnki_thumb_part_image;} Bry_fmtr lnki_thumb_part_image = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
    " + , " " + , " \"~{lnki_alt}\"" + , " " + , "
    " + ), "elem_id", "lnki_class", "lnki_href", "lnki_title", "lnki_src", "lnki_width", "lnki_height", "lnki_alt"); + public Bry_fmtr Lnki_thumb_part_caption() {return lnki_thumb_part_caption;} Bry_fmtr lnki_thumb_part_caption = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
    ~{magnify_btn}" + , " ~{lnki_caption}" + , "
    " + ), "magnify_btn", "lnki_caption"); + public Bry_fmtr Lnki_thumb_part_alt() {return lnki_thumb_part_alt;} Bry_fmtr lnki_thumb_part_alt = Bry_fmtr.new_ + (String_.Concat_lines_nl_skip_last + ( "" + , "
    " + , "
    " + , "~{alt_html}" + , "
    " + ) + , "alt_html"); + public Bry_fmtr Lnki_thumb_part_magnfiy_btn() {return lnki_thumb_part_magnify_btn;} Bry_fmtr lnki_thumb_part_magnify_btn = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
    " + , " " + , " \"\"" + , " " + , "
    " + ), "magnify_icon", "lnki_src", "lnki_enlarge_msg"); + public Bry_fmtr Lnki_thumb_part_play_btn() {return lnki_thumb_part_play_btn;} Bry_fmtr lnki_thumb_part_play_btn = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
    " + , " " + , " \"Play" + , " " + , "
    " + ), "play_id", "play_icon", "play_width", "play_max_width", "lnki_url", "lnki_title"); + public Bry_fmtr Lnki_thumb_part_info_btn() {return lnki_thumb_part_info_btn;} Bry_fmtr lnki_thumb_part_info_btn = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
    " + , " " + , " " + , " " + , "
    " + ), "info_icon", "lnki_href"); + public Bry_fmtr Plain() {return plain;} Bry_fmtr plain = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "~{val}" + ), "val"); + public void Copy_cfg(Xow_html_mgr html_mgr) {imgs_mgr.Copy_cfg(html_mgr.Imgs_mgr());} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_lnki_full_image_)) lnki_full_image.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_full_media_)) lnki_full_media.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_core_)) lnki_thumb_core.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_file_image_)) lnki_thumb_file_image.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_file_audio_)) lnki_thumb_file_audio.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_file_video_)) lnki_thumb_file_video.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_part_image_)) lnki_thumb_part_image.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_part_caption_)) lnki_thumb_part_caption.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_part_alt_)) lnki_thumb_part_alt.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_part_magnify_btn_)) lnki_thumb_part_magnify_btn.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_part_play_btn_)) lnki_thumb_part_play_btn.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_lnki_thumb_part_info_btn_)) lnki_thumb_part_info_btn.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_article)) return page_wtr_mgr; + else if (ctx.Match(k, Invk_portal)) return portal_mgr; + else if (ctx.Match(k, Invk_imgs)) return imgs_mgr; + else if (ctx.Match(k, Invk_ns_ctg)) return ns_ctg; + else return GfoInvkAble_.Rv_unhandled; + return this; + } + public static final String + Invk_lnki_full_image_ = "lnki_full_image_", Invk_lnki_full_media_ = "lnki_full_media_", Invk_lnki_thumb_core_ = "lnki_thumb_core_" + , Invk_lnki_thumb_file_image_ = "lnki_thumb_file_image_", Invk_lnki_thumb_file_audio_ = "lnki_thumb_file_audio_", Invk_lnki_thumb_file_video_ = "lnki_thumb_file_video_" + , Invk_lnki_thumb_part_image_ = "lnki_thumb_part_image_", Invk_lnki_thumb_part_caption_ = "lnki_thumb_part_caption_", Invk_lnki_thumb_part_alt_ = "lnki_thumb_part_alt_" + , Invk_lnki_thumb_part_magnify_btn_ = "lnki_thumb_part_magnify_btn_", Invk_lnki_thumb_part_play_btn_ = "lnki_thumb_part_play_btn_", Invk_lnki_thumb_part_info_btn_ = "lnki_thumb_part_info_btn_" + , Invk_article = "article" + , Invk_portal = "portal", Invk_imgs = "imgs", Invk_ns_ctg = "ns_ctg" + ; + public static final String Str_img_class_thumbimage = "thumbimage"; + public static final byte[] Bry_anchor_class_image = Bry_.new_ascii_(" class=\"image\""), Bry_anchor_class_blank = Bry_.Empty, Bry_anchor_rel_nofollow = Bry_.new_ascii_(" rel=\"nofollow\""), Bry_anchor_rel_blank = Bry_.Empty, Bry_img_class_thumbimage = Bry_.new_ascii_(" class=\"thumbimage\""), Bry_img_class_none = Bry_.Empty; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm.java new file mode 100644 index 000000000..cbc5af6f3 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm.java @@ -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 . +*/ +package gplx.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public interface Xoh_module_itm { + byte[] Key(); + void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr); + void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr); + void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr); + void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr); + void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr); + void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr); + void Clear(); +} +/* +Position // top, bottom +Targets // mobile, desktop +Dependencies +Messages +*/ diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__collapsible.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__collapsible.java new file mode 100644 index 000000000..ea80a892c --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__collapsible.java @@ -0,0 +1,41 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xoh_module_itm__collapsible implements Xoh_module_itm { + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("collapsible"); + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_js_global_ini_atr_val(Key_enabled , true); + wtr.Write_js_global_ini_atr_val(Key_collapsed , app.Api_root().Html().Modules().Collapsible().Collapsed()); + wtr.Write_js_global_ini_atr_msg(wiki , Key_collapse); + wtr.Write_js_global_ini_atr_msg(wiki , Key_expand); + } + private static final byte[] + Key_enabled = Bry_.new_ascii_("collapsible-enabled") + , Key_collapsed = Bry_.new_ascii_("collapsible-collapsed") + , Key_collapse = Bry_.new_ascii_("collapsible-collapse") + , Key_expand = Bry_.new_ascii_("collapsible-expand") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__css.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__css.java new file mode 100644 index 000000000..5d1c6381f --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__css.java @@ -0,0 +1,38 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.gui.*; +public class Xoh_module_itm__css implements Xoh_module_itm { + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("css"); + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_css_style_itm(app.Ctg_mgr().Missing_ctg_cls_css()); + if (app.Html_mgr().Page_mgr().Font_enabled()) + wtr.Write_css_style_itm(app.Html_mgr().Page_mgr().Font_css_bry()); + byte[] css_xtn = app.Gui_mgr().Html_mgr().Css_xtn(); + if (Bry_.Len_gt_0(css_xtn)) + wtr.Write_css_style_itm(css_xtn); + } + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__gallery.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__gallery.java new file mode 100644 index 000000000..715bd7adc --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__gallery.java @@ -0,0 +1,36 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.gui.*; +public class Xoh_module_itm__gallery implements Xoh_module_itm { + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("gallery"); + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_js_global_ini_atr_val(Key_enabled , true); + } + private static final byte[] + Key_enabled = Bry_.new_ascii_("gallery-packed-enabled") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__globals.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__globals.java new file mode 100644 index 000000000..7f4edb96d --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__globals.java @@ -0,0 +1,89 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.langs.numbers.*; +public class Xoh_module_itm__globals implements Xoh_module_itm { + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("globals"); + public boolean Enabled() {return enabled;} public void Enabled_n_() {enabled = false;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + if (Url_core == null) Url_core = app.Fsys_mgr().Bin_any_dir().GenSubFil_nest("xowa", "html", "modules", "xowa.core", "xowa.core.js").To_http_file_bry(); + wtr.Write_js_include(Url_core); + } + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_js_var(Var_xowa_root_dir , Bool_.Y, app.Fsys_mgr().Root_dir().To_http_file_bry()); + wtr.Write_js_var(Var_xowa_mode_is_server , Bool_.N, app.Tcp_server().Running() ? Bool_.True_bry : Bool_.False_bry); + } + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_js_global_ini_atr_msg(wiki, Key_sort_ascending); + wtr.Write_js_global_ini_atr_msg(wiki, Key_sort_descending); + Xol_lang lang = wiki.Lang(); Xow_msg_mgr msg_mgr = wiki.Msg_mgr(); + Bry_bfr bfr = lang.App().Utl_bry_bfr_mkr().Get_b512(); + byte[] months_long = Html_js_table_months(bfr, msg_mgr, Xol_msg_itm_.Id_dte_month_name_january, Xol_msg_itm_.Id_dte_month_name_december); + byte[] months_short = Html_js_table_months(bfr, msg_mgr, Xol_msg_itm_.Id_dte_month_abrv_jan, Xol_msg_itm_.Id_dte_month_abrv_dec); + byte[] num_format_separators = Html_js_table_num_format_separators(bfr, lang.Num_mgr().Separators_mgr()); + bfr.Mkr_rls(); + wtr.Write_js_global_ini_atr_val(Key_wgContentLanguage , lang.Key_bry()); + wtr.Write_js_global_ini_atr_obj(Key_wgSeparatorTransformTable , num_format_separators); + wtr.Write_js_global_ini_atr_obj(Key_wgDigitTransformTable , Num_format_digits); + wtr.Write_js_global_ini_atr_val(Key_wgDefaultDateFormat , Date_format_default); + wtr.Write_js_global_ini_atr_obj(Key_wgMonthNames , months_long); + wtr.Write_js_global_ini_atr_obj(Key_wgMonthNamesShort , months_short); + } + private static final byte[] + Key_sort_descending = Bry_.new_ascii_("sort-descending") + , Key_sort_ascending = Bry_.new_ascii_("sort-ascending") + , Key_wgContentLanguage = Bry_.new_ascii_("wgContentLanguage") + , Key_wgSeparatorTransformTable = Bry_.new_ascii_("wgSeparatorTransformTable") + , Key_wgDigitTransformTable = Bry_.new_ascii_("wgDigitTransformTable") + , Key_wgDefaultDateFormat = Bry_.new_ascii_("wgDefaultDateFormat") + , Key_wgMonthNames = Bry_.new_ascii_("wgMonthNames") + , Key_wgMonthNamesShort = Bry_.new_ascii_("wgMonthNamesShort") + ; + private static byte[] Html_js_table_months(Bry_bfr bfr, Xow_msg_mgr msg_mgr, int january_id, int december_id) {// ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + bfr.Add_byte(Byte_ascii.Brack_bgn).Add_byte(Byte_ascii.Apos).Add_byte(Byte_ascii.Apos); + for (int i = january_id; i <= december_id; i++) { + bfr.Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Space).Add_byte(Byte_ascii.Apos); + bfr.Add(msg_mgr.Val_by_id(i)); + bfr.Add_byte(Byte_ascii.Apos); + } + bfr.Add_byte(Byte_ascii.Brack_end); + return bfr.XtoAryAndClear(); + } + private static byte[] Html_js_table_num_format_separators(Bry_bfr bfr, Xol_transform_mgr separator_mgr) { + byte[] dec_spr = separator_mgr.Get_val_or_self(Xol_num_mgr.Separators_key__dec); + bfr.Add_byte(Byte_ascii.Brack_bgn) .Add_byte(Byte_ascii.Apos).Add(dec_spr).Add_byte(Byte_ascii.Tab).Add_byte(Byte_ascii.Dot).Add_byte(Byte_ascii.Apos); + byte[] grp_spr = separator_mgr.Get_val_or_self(Xol_num_mgr.Separators_key__grp); + bfr.Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Space) .Add_byte(Byte_ascii.Apos).Add(grp_spr).Add_byte(Byte_ascii.Tab).Add_byte(Byte_ascii.Comma).Add_byte(Byte_ascii.Apos); + bfr.Add_byte(Byte_ascii.Brack_end); + return bfr.XtoAryAndClear(); + } + private static final byte[] + Date_format_default = Bry_.new_ascii_("dmy") + , Num_format_digits = Bry_.new_ascii_("['', '']") + , Var_xowa_root_dir = Bry_.new_ascii_("xowa_root_dir") + , Var_xowa_mode_is_server = Bry_.new_ascii_("xowa_mode_is_server") + ; + private static byte[] Url_core; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__hiero.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__hiero.java new file mode 100644 index 000000000..b48c9fc58 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__hiero.java @@ -0,0 +1,35 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.gui.*; +public class Xoh_module_itm__hiero implements Xoh_module_itm { + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("hiero"); + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + if (Url_css == null) Url_css = app.Fsys_mgr().Bin_any_dir().GenSubFil_nest("xowa", "xtns", "Wikihiero", "modules", "ext.wikihiero.css").To_http_file_bry(); + wtr.Write_css_include(Url_css); + } + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + private static byte[] Url_css; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__mathjax.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__mathjax.java new file mode 100644 index 000000000..f3998cc39 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__mathjax.java @@ -0,0 +1,35 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.gui.*; +public class Xoh_module_itm__mathjax implements Xoh_module_itm { + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("mathjax"); + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + if (Url_mathjax == null) Url_mathjax = app.Fsys_mgr().Bin_any_dir().GenSubFil_nest("javascript", "xowa", "mathjax", "xowa_mathjax.js").To_http_file_bry(); + wtr.Write_js_include(Url_mathjax); + } + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + private static byte[] Url_mathjax; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__navframe.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__navframe.java new file mode 100644 index 000000000..9bc680740 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__navframe.java @@ -0,0 +1,41 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xoh_module_itm__navframe implements Xoh_module_itm { + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("navframe"); + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_js_global_ini_atr_val(Key_enabled , true); + wtr.Write_js_global_ini_atr_val(Key_collapsed , app.Api_root().Html().Modules().Navframe().Collapsed()); + wtr.Write_js_global_ini_atr_msg(wiki , Key_show); + wtr.Write_js_global_ini_atr_msg(wiki , Key_hide); + } + private static final byte[] + Key_enabled = Bry_.new_ascii_("navframe-enabled") + , Key_collapsed = Bry_.new_ascii_("navframe-collapsed") + , Key_show = Bry_.new_ascii_("show") + , Key_hide = Bry_.new_ascii_("hide") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__popups.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__popups.java new file mode 100644 index 000000000..2c44a5050 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__popups.java @@ -0,0 +1,67 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.gui.*; +import gplx.xowa.apis.xowa.html.modules.*; +public class Xoh_module_itm__popups implements Xoh_module_itm { + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("popups"); + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + if (Css_url == null) Css_url = app.Fsys_mgr().Bin_any_dir().GenSubFil_nest("xowa", "html", "modules", "xowa.popups", "xowa.popups.css").To_http_file_bry(); + wtr.Write_css_include(Css_url); + } + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + if (Js_line_2 == null) { + Js_line_2 = Bry_.Add + ( Js_line_2_bgn + , app.Fsys_mgr().Bin_any_dir().GenSubFil_nest("xowa", "html", "modules", "xowa.popups", "xowa.popups.js").To_http_file_bry() + , Js_line_2_end + ); + } + wtr.Write_js_line(Js_line_1); + wtr.Write_js_line(Js_line_2); + } + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + Xoapi_popups api_popups = app.Api_root().Html().Modules().Popups(); + wtr.Write_js_global_ini_atr_val(Key_win_show_delay , api_popups.Win_show_delay()); + wtr.Write_js_global_ini_atr_val(Key_win_hide_delay , api_popups.Win_hide_delay()); + wtr.Write_js_global_ini_atr_val(Key_win_max_w , api_popups.Win_max_w()); + wtr.Write_js_global_ini_atr_val(Key_win_max_h , api_popups.Win_max_h()); + wtr.Write_js_global_ini_atr_val(Key_win_show_all_max_w , api_popups.Win_show_all_max_w()); + wtr.Write_js_global_ini_atr_val(Key_win_bind_focus_blur , api_popups.Win_bind_focus_blur()); + } + private static byte[] Css_url, Js_line_2; + private static final byte[] + Js_line_1 = Bry_.new_ascii_("xowa.js.jquery.init();") + , Js_line_2_bgn = Bry_.new_ascii_("xowa.js.load_lib('") + , Js_line_2_end = Bry_.new_ascii_("');") + , Key_win_show_delay = Bry_.new_ascii_("popups-win-show_delay") + , Key_win_hide_delay = Bry_.new_ascii_("popups-win-hide_delay") + , Key_win_max_w = Bry_.new_ascii_("popups-win-max_w") + , Key_win_max_h = Bry_.new_ascii_("popups-win-max_h") + , Key_win_show_all_max_w = Bry_.new_ascii_("popups-win-show_all_max_w") + , Key_win_bind_focus_blur = Bry_.new_ascii_("popups-win-bind_focus_blur") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__toc.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__toc.java new file mode 100644 index 000000000..af6da67ee --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_itm__toc.java @@ -0,0 +1,46 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.gui.*; +public class Xoh_module_itm__toc implements Xoh_module_itm { + public byte[] Key() {return Key_const;} private static final byte[] Key_const = Bry_.new_ascii_("toc"); + public boolean Enabled() {return enabled;} public void Enabled_y_() {enabled = true;} public void Enabled_(boolean v) {enabled = v;} private boolean enabled; + public void Clear() {enabled = false;} + public void Write_css_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_css_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_include(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_tail_script(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) {} + public void Write_js_head_global(Xoa_app app, Xow_wiki wiki, Xoa_page page, Xoh_module_wtr wtr) { + if (!enabled) return; + wtr.Write_js_global_ini_atr_val(Key_exists , true); + wtr.Write_js_global_ini_atr_val(Key_collapsed , app.Api_root().Html().Modules().Collapsible().Collapsed() ? Val_collapsed_y : Val_collapsed_n); + wtr.Write_js_global_ini_atr_msg(wiki , Key_showtoc); + wtr.Write_js_global_ini_atr_msg(wiki , Key_hidetoc); + } + private static final byte[] + Key_exists = Bry_.new_ascii_("toc-enabled") + , Key_collapsed = Bry_.new_ascii_("mw_hidetoc") + , Val_collapsed_y = Bry_.new_ascii_("1") + , Val_collapsed_n = Bry_.new_ascii_("0") + ; + public static final byte[] + Key_showtoc = Bry_.new_ascii_("showtoc") + , Key_hidetoc = Bry_.new_ascii_("hidetoc") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_mgr.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_mgr.java new file mode 100644 index 000000000..9cda69dbb --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_mgr.java @@ -0,0 +1,102 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xoh_module_mgr implements Bry_fmtr_arg { + private Xoa_app app; private Xow_wiki wiki; private Xoa_page page; private Xoh_module_itm[] itms; private int itms_len; + private Xoh_module_wtr wtr = new Xoh_module_wtr(); + public Xoh_module_mgr() { + Itms_add(itm_css, itm_globals, itm_popups, itm_toc, itm_collapsible, itm_navframe, itm_gallery, itm_mathjax, itm_hiero); + } + public Xoh_module_itm__css Itm_css() {return itm_css;} private Xoh_module_itm__css itm_css = new Xoh_module_itm__css(); + public Xoh_module_itm__globals Itm_globals() {return itm_globals;} private Xoh_module_itm__globals itm_globals = new Xoh_module_itm__globals(); + public Xoh_module_itm__popups Itm_popups() {return itm_popups;} private Xoh_module_itm__popups itm_popups = new Xoh_module_itm__popups(); + public Xoh_module_itm__toc Itm_toc() {return itm_toc;} private Xoh_module_itm__toc itm_toc = new Xoh_module_itm__toc(); + public Xoh_module_itm__collapsible Itm_collapsible() {return itm_collapsible;} private Xoh_module_itm__collapsible itm_collapsible = new Xoh_module_itm__collapsible(); + public Xoh_module_itm__navframe Itm_navframe() {return itm_navframe;} private Xoh_module_itm__navframe itm_navframe = new Xoh_module_itm__navframe(); + public Xoh_module_itm__gallery Itm_gallery() {return itm_gallery;} private Xoh_module_itm__gallery itm_gallery = new Xoh_module_itm__gallery(); + public Xoh_module_itm__mathjax Itm_mathjax() {return itm_mathjax;} private Xoh_module_itm__mathjax itm_mathjax = new Xoh_module_itm__mathjax(); + public Xoh_module_itm__hiero Itm_hiero() {return itm_hiero;} private Xoh_module_itm__hiero itm_hiero = new Xoh_module_itm__hiero(); + public Xoh_module_mgr Init(Xoa_app app, Xow_wiki wiki, Xoa_page page) { + this.app = app; this.wiki = wiki; this.page = page; + if (page.Hdr_mgr().Toc_enabled()) + itm_toc.Enabled_y_(); + return this; + } + public Xoh_module_mgr Init_dflts() { + itm_css.Enabled_y_(); + itm_globals.Enabled_y_(); // for now, always mark this and rest as exists; DATE:2014-06-09 + itm_collapsible.Enabled_y_(); + itm_navframe.Enabled_y_(); + itm_popups.Enabled_(app.Api_root().Html().Modules().Popups().Enabled()); + return this; + } + public void Clear() { + for (int i = 0; i < itms_len; ++i) + itms[i].Clear(); + } + public void XferAry(Bry_bfr bfr, int idx) {Write(bfr, app, wiki, page);} + public void Write(Bry_bfr bfr, Xoa_app app, Xow_wiki wiki, Xoa_page page) { + wtr.Init(bfr); + wtr.Indent_add(); + for (int i = 0; i < itms_len; ++i) { + Xoh_module_itm itm = itms[i]; + itm.Write_css_include(app, wiki, page, wtr); + } + wtr.Write_css_style_bgn(); + for (int i = 0; i < itms_len; ++i) { + Xoh_module_itm itm = itms[i]; + itm.Write_css_script(app, wiki, page, wtr); + } + wtr.Write_css_style_end(); + int reset_bgn = wtr.Bfr().Len(); + wtr.Write_js_script_bgn(); // write " + )); + } +} +class Xoh_module_mgr_fxt { + private Xop_fxt fxt = new Xop_fxt(); + private Xoh_module_mgr mgr; + private Bry_bfr bfr = Bry_bfr.reset_(255); + public Xow_wiki Wiki() {return wiki;} private Xow_wiki wiki; + public Xoh_module_mgr Mgr() {return mgr;} + public void Clear() { + fxt.Reset(); + mgr = fxt.Page().Html_data().Module_mgr(); + wiki = fxt.Wiki(); + } + public void Init_msg(byte[] key, String val) { + wiki.Msg_mgr().Get_or_make(key).Atrs_set(Bry_.new_ascii_(val), false, false); + } + public void Test_write(String expd) { + mgr.Write(bfr, fxt.App(), wiki, fxt.Page()); + Tfds.Eq_str_lines(expd, bfr.XtoStrAndClear()); + } +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_wtr.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_wtr.java new file mode 100644 index 000000000..36ad3f49c --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_wtr.java @@ -0,0 +1,155 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.html.*; +public class Xoh_module_wtr { + private int indent; private int reset_bgn, reset_end; + public Bry_bfr Bfr() {return bfr;} private Bry_bfr bfr; + public void Init(Bry_bfr bfr) {this.bfr = bfr;} + public void Term() {this.bfr = null;} + public void Write_css_include(byte[] url) { + Write_nl_and_indent(); + bfr.Add(Css_include_bgn).Add(url).Add(Css_include_end); + } + public void Write_js_include(byte[] url) { + Write_nl_and_indent(); + bfr.Add(Js_include_bgn).Add(url).Add(Js_include_end); + } + public void Write_css_style_bgn() { + reset_bgn = bfr.Len(); + Write_nl_and_indent(); + bfr.Add(Html_tags.Style_lhs_w_type); + Indent_add(); + reset_end = bfr.Len(); + } + public void Write_css_style_end() { + Indent_del(); + if (Reset()) return; + Write_nl_and_indent(); + bfr.Add(Html_tags.Style_rhs); + } + public void Write_css_style_itm(byte[] bry) { + Write_nl_and_indent(); + bfr.Add(bry); + } + public void Write_js_line(byte[] bry) { + Write_nl_and_indent(); + bfr.Add(bry); + } + public void Write_js_script_bgn() { + reset_bgn = bfr.Len(); + Write_nl_and_indent(); + bfr.Add(Html_tags.Script_lhs_w_type); + Indent_add(); + reset_end = bfr.Len(); + } + public void Write_js_script_end() { + Indent_del(); + if (Reset()) return; + Write_nl_and_indent(); + bfr.Add(Html_tags.Script_rhs); + } + public void Write_js_head_global_bgn() { + reset_bgn = bfr.Len(); + Write_nl_and_indent(); + bfr.Add(Js_globals_ini_var_bgn); + Indent_add(); + reset_end = bfr.Len(); + } + public void Write_js_head_global_end() { + Indent_del(); + if (Reset()) return; + Write_nl_and_indent(); + bfr.Add(Js_globals_ini_var_end); + } + private boolean Reset() { + if (bfr.Len() == reset_end) { // itms wrote nothing + bfr.Delete_rng_to_end(reset_bgn); // delete bgn + return true; + } + else + return false; + } + public void Write_js_global_ini_atr_val(byte[] key, boolean val) {Write_js_global_ini_atr(key, Bool_.N, val ? Bool_.True_bry : Bool_.False_bry);} + public void Write_js_global_ini_atr_val(byte[] key, byte[] val) {Write_js_global_ini_atr(key, Bool_.Y, val);} + public void Write_js_global_ini_atr_obj(byte[] key, byte[] val) {Write_js_global_ini_atr(key, Bool_.N, val);} + public void Write_js_global_ini_atr_msg(Xow_wiki wiki, byte[] key) {Write_js_global_ini_atr(key, Bool_.Y, wiki.Msg_mgr().Val_by_key_obj(key));} + private void Write_js_global_ini_atr(byte[] key, boolean quote_val, byte[] val) { + Write_js_global_ini_atr_bgn(key); + if (quote_val) + Write_quote(Byte_ascii.Apos, val); + else + bfr.Add(val); + bfr.Add_byte(Byte_ascii.Comma); + } + public void Write_js_global_ini_atr_val(byte[] key, int val) { + Write_js_global_ini_atr_bgn(key); + bfr.Add_int_variable(val); + bfr.Add_byte(Byte_ascii.Comma); + } + private void Write_js_global_ini_atr_bgn(byte[] key) { + Write_nl_and_indent(); + bfr.Add_byte_apos(); + bfr.Add(key); + bfr.Add_byte_apos(); + bfr.Add(Js_globals_ini_atr_mid); + } + public void Write_js_var(byte[] key, boolean quote_val, byte[] val) { + Write_nl_and_indent(); + bfr.Add(Js_var_bgn); + bfr.Add(key); + bfr.Add(Js_var_mid); + if (quote_val) + Write_quote(Byte_ascii.Apos, val); + else + bfr.Add(val); + bfr.Add(Js_var_end); + } + public void Write(byte[] v) { + Indent(); + bfr.Add(v); + } + private void Write_quote(byte quote_byte, byte[] val) { + int val_len = val.length; + bfr.Add_byte(quote_byte); + for (int i = 0; i < val_len; i++) { + byte b = val[i]; + if (b == quote_byte) bfr.Add_byte(b); // double up quotes + bfr.Add_byte(b); + } + bfr.Add_byte(quote_byte); + } + private void Write_nl_and_indent() { + bfr.Add_byte_nl(); Indent(); + } + private void Indent() {bfr.Add_byte_repeat(Byte_ascii.Space, indent);} + public void Indent_add() {indent += 2;} + public void Indent_del() {indent -= 2;} + private static final byte[] + Css_include_bgn = Bry_.new_ascii_("") + , Js_include_bgn = Bry_.new_ascii_("") + , Js_globals_ini_var_bgn = Bry_.new_ascii_("var xowa_global_values = {") + , Js_globals_ini_var_end = Bry_.new_ascii_("}") + , Js_globals_ini_atr_mid = Bry_.new_ascii_(" : ") + , Js_var_bgn = Bry_.new_ascii_("var ") + , Js_var_mid = Bry_.new_ascii_(" = ") + , Js_var_end = Bry_.new_ascii_(";") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xoh_module_wtr_tst.java b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_wtr_tst.java new file mode 100644 index 000000000..aa7de2193 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xoh_module_wtr_tst.java @@ -0,0 +1,63 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import org.junit.*; +import gplx.xowa.gui.*; +public class Xoh_module_wtr_tst { + @Before public void init() {fxt.Clear();} private Xoh_module_wtr_fxt fxt = new Xoh_module_wtr_fxt(); + @Test public void Globals_none() { + Xoh_module_wtr wtr = fxt.Wtr(); + wtr.Write_js_head_global_bgn(); + wtr.Write_js_head_global_end(); + fxt.Test(""); + } + @Test public void Globals_some() { + Xoh_module_wtr wtr = fxt.Wtr(); + wtr.Write_js_head_global_bgn(); + fxt.Exec_Write_js_global_ini_atr_val("key_1", "val_1"); + fxt.Exec_Write_js_global_ini_atr_val("key_2", "val_2"); + fxt.Exec_Write_js_global_ini_atr_val("key_3", "apos_'_1"); + wtr.Write_js_head_global_end(); + fxt.Test(String_.Concat_lines_nl_skip_last + ( "" + , "var xowa_global_values = {" + , " 'key_1' : 'val_1'," + , " 'key_2' : 'val_2'," + , " 'key_3' : 'apos_''_1'," + , "}" + )); + } +} +class Xoh_module_wtr_fxt { + private Bry_bfr bfr = Bry_bfr.reset_(255); + public Xoh_module_wtr Wtr() {return wtr;} private Xoh_module_wtr wtr = new Xoh_module_wtr(); + public void Clear() { + wtr.Init(bfr); + } + public void Exec_Write_js_global_ini_atr_val(String key, String val) {wtr.Write_js_global_ini_atr_val(Bry_.new_utf8_(key), Bry_.new_utf8_(val));} + public void Test(String expd) { + Tfds.Eq_str_lines(expd, bfr.XtoStrAndClear()); + } +// public void Init_msg(byte[] key, String val) { +// wiki.Msg_mgr().Get_or_make(key).Atrs_set(Bry_.new_ascii_(val), false, false); +// } +// public void Test_write(String expd) { +// mgr.Write(bfr, fxt.App(), wiki, fxt.Page()); +// Tfds.Eq_str_lines(expd, bfr.XtoStrAndClear()); +// } +} diff --git a/400_xowa/src/gplx/xowa/html/modules/Xow_module_mgr.java b/400_xowa/src/gplx/xowa/html/modules/Xow_module_mgr.java new file mode 100644 index 000000000..d150bb8ef --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/Xow_module_mgr.java @@ -0,0 +1,26 @@ +/* +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.xowa.html.modules; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.html.modules.popups.*; +public class Xow_module_mgr { + public Xow_module_mgr(Xow_wiki wiki) {this.popup_mgr = new Xow_popup_mgr(wiki);} + public void Init_by_wiki(Xow_wiki wiki) { + popup_mgr.Init_by_wiki(wiki); + } + public Xow_popup_mgr Popup_mgr() {return popup_mgr;} private Xow_popup_mgr popup_mgr; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_itm.java b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_itm.java new file mode 100644 index 000000000..f8991a748 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_itm.java @@ -0,0 +1,45 @@ +/* +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.xowa.html.modules.popups; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; import gplx.xowa.html.modules.*; +public class Xow_popup_itm implements Cancelable { + public Xow_popup_itm(int id, byte[] page_href, int init_words_needed) { + this.popup_id = "popup_" + Int_.XtoStr(id); + this.words_needed = init_words_needed; + this.page_href = page_href; + } + public boolean Canceled() {return canceled;} private boolean canceled = false; + public void Cancel() {canceled = true;} + public void Cancel_reset() {canceled = false;} + public byte Mode() {return mode;} private byte mode = Mode_init; + public Xow_popup_itm Mode_more_(int more_words) { + mode = Mode_more; + words_needed = popup_html_word_count + more_words; + return this; + } + public Xow_popup_itm Mode_all_() { + mode = Mode_all; + words_needed = Int_.MaxValue; + return this; + } + public String Popup_id() {return popup_id;} private String popup_id; + public byte[] Popup_html() {return popup_html;} public Xow_popup_itm Popup_html_(byte[] v) {popup_html = v; return this;} private byte[] popup_html; + public byte[] Page_href() {return page_href;} private byte[] page_href; + public int Popup_html_word_count() {return popup_html_word_count;} public void Popup_html_word_count_(int v) {popup_html_word_count = v;} private int popup_html_word_count; + public int Words_needed() {return words_needed;} private int words_needed; + public static final byte Mode_init = 0, Mode_more = 1, Mode_all = 2; +} diff --git a/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_mgr.java b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_mgr.java new file mode 100644 index 000000000..34f56acff --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_mgr.java @@ -0,0 +1,203 @@ +/* +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.xowa.html.modules.popups; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; import gplx.xowa.html.modules.*; +import gplx.threads.*; +import gplx.xowa.gui.views.*; +import gplx.xowa.specials.search.*; +import gplx.xowa.apis.xowa.html.modules.*; +public class Xow_popup_mgr implements GfoInvkAble, GfoEvObj { + private Xoa_app app; private Xow_wiki wiki; private Js_wtr js_wtr = new Js_wtr(); + private int show_init_word_count = Xoapi_popups.Dflt_show_init_word_count, show_more_word_count = Xoapi_popups.Dflt_show_more_word_count; + private Xoh_href temp_href = new Xoh_href(); + private Object async_thread_guard = new Object(); private Xow_popup_itm async_itm; private GfoInvkAble async_cmd_show; private int async_id_next = 1; + public Xow_popup_mgr(Xow_wiki wiki) { + this.wiki = wiki; this.app = wiki.App(); + ev_mgr = GfoEvMgr.new_(this); + } + public GfoEvMgr EvMgr() {return ev_mgr;} private GfoEvMgr ev_mgr; + public Xow_popup_parser Parser() {return parser;} private Xow_popup_parser parser = new Xow_popup_parser(); + public void Init_by_wiki(Xow_wiki wiki) { + parser.Init_by_wiki(wiki); + Xoapi_popups api_popups = app.Api_root().Html().Modules().Popups(); + show_init_word_count = api_popups.Show_init_word_count(); + show_more_word_count = api_popups.Show_more_word_count(); + Ns_allowed_(api_popups.Ns_allowed()); + parser.Show_all_if_less_than_(api_popups.Show_all_if_less_than()); + parser.Xnde_ignore_ids_(api_popups.Xnde_ignore_ids()); + parser.Scan_len_(api_popups.Scan_len()); + parser.Scan_max_(api_popups.Scan_max()); + parser.Html_fmtr().Fmt_(api_popups.Html_fmt()); + parser.Read_til_stop_fwd_(api_popups.Read_til_stop_fwd()); + parser.Read_til_stop_bwd_(api_popups.Read_til_stop_bwd()); + parser.Tmpl_tkn_max_(api_popups.Tmpl_tkn_max()); + GfoEvMgr_.SubSame_many(api_popups, this + , Xoapi_popups.Evt_show_init_word_count_changed + , Xoapi_popups.Evt_show_more_word_count_changed + , Xoapi_popups.Evt_show_all_if_less_than_changed + , Xoapi_popups.Evt_xnde_ignore_ids_changed + , Xoapi_popups.Evt_scan_len_changed + , Xoapi_popups.Evt_scan_max_changed + , Xoapi_popups.Evt_html_fmt_changed + , Xoapi_popups.Evt_read_til_stop_fwd_changed + , Xoapi_popups.Evt_read_til_stop_bwd_changed + , Xoapi_popups.Evt_ns_allowed_changed + , Xoapi_popups.Evt_tmpl_tkn_max_changed + ); + } + public String Show_init(byte[] href, int id) { + Xoa_page cur_page = Cur_page(); + Xog_tab_itm tab = cur_page.Tab(); + if (tab != null && tab.Tab_is_loading()) return ""; // NOTE: tab is null when previewing + Xow_popup_itm itm = new Xow_popup_itm(id, href, show_init_word_count); + String rv = String_.new_utf8_(Get_popup_html(cur_page, itm)); + return tab != null && tab.Tab_is_loading() ? "" : rv; + } + public void Show_more(String popup_id) { + Xoa_page cur_page = Cur_page(); + Xow_popup_itm popup_itm = Itms_get_or_null(cur_page, popup_id).Mode_more_(show_more_word_count); + popup_itm.Popup_html_(Get_popup_html(cur_page, popup_itm)); + Show_popup_html(Cbk_xowa_popups_show_update, Mode_show_more, popup_itm); + } + public void Show_all(String popup_id) { + Xoa_page cur_page = Cur_page(); + Xow_popup_itm popup_itm = Itms_get_or_null(cur_page, popup_id).Mode_all_(); + popup_itm.Popup_html_(Get_popup_html(cur_page, popup_itm)); + Show_popup_html(Cbk_xowa_popups_show_update, Mode_show_all, popup_itm); + } + public String Get_async_bgn(byte[] js_cbk, byte[] href) { + if (Bry_.HasAtBgn(href, gplx.xowa.parsers.lnkes.Xop_lnke_wkr.Bry_xowa_protocol)) return null; // ignore xowa-cmd + synchronized (async_thread_guard) { + if (async_itm != null) async_itm.Cancel(); + async_itm = new Xow_popup_itm(++async_id_next, href, show_init_word_count); + String id_str = async_itm.Popup_id(); + ThreadAdp_.invk_(id_str, this, Invk_show_popup_async).Start(); + return id_str; + } + } + private byte[] Get_popup_html(Xoa_page cur_page, Xow_popup_itm itm) { + try { + synchronized (async_thread_guard) { + if (itm.Canceled()) return null; + cur_page.Popup_itms().AddReplace(itm.Popup_id(), itm); + app.Href_parser().Parse(temp_href, itm.Page_href(), wiki, cur_page.Ttl().Page_url()); + Xow_wiki popup_wiki = app.Wiki_mgr().Get_by_key_or_null(temp_href.Wiki()); + popup_wiki.Init_assert(); + Xoa_ttl popup_ttl = Xoa_ttl.parse_(popup_wiki, temp_href.Page()); + if (ns_allowed_regy.Count() > 0 && !ns_allowed_regy.Has(ns_allowed_regy_key.Val_(popup_ttl.Ns().Id()))) return Bry_.Empty; + Xoa_page popup_page = popup_wiki.Data_mgr().Get_page(popup_ttl, false); + byte[] rv = popup_wiki.Html_mgr().Module_mgr().Popup_mgr().Parser().Parse(itm, popup_page, wiki.Domain_bry(), cur_page.Tab()); + Xog_win_itm__prog_href_mgr.Hover(app, cur_page, String_.new_utf8_(itm.Page_href())); // set page ttl again in prog bar; DATE:2014-06-28 + return rv; + } + } + catch(Exception e) { + app.Usr_dlg().Warn_many("", "", "failed to get popup: href=~{0} err=~{1}", String_.new_utf8_(itm.Page_href()), Err_.Message_gplx_brief(e)); + return null; + } + } + public void Show_popup_html(String cbk, byte[] mode, Xow_popup_itm popup_itm) { + Xog_tab_itm cur_tab = app.Gui_mgr().Browser_win().Active_tab(); + cur_tab.Html_box().Html_js_eval_script(Xow_popup_mgr_.Bld_js_cmd(js_wtr, cbk, mode, popup_itm.Page_href(), popup_itm.Popup_html())); + } + private void Show_popup_async() { + try { + synchronized (async_thread_guard) { + Xoa_page cur_page = app.Gui_mgr().Browser_win().Active_page(); + async_itm.Popup_html_(Get_popup_html(cur_page, async_itm)); + } + if (async_cmd_show == null) + async_cmd_show = app.Gui_mgr().Kit().New_cmd_sync(this); + GfoInvkAble_.InvkCmd(async_cmd_show, Invk_show_popup); + } + catch(Exception e) { + app.Usr_dlg().Warn_many("", "", "failed to get popup: href=~{0} err=~{1}", String_.new_utf8_(async_itm.Page_href()), Err_.Message_gplx_brief(e)); + } + } + private void Show_popup() { + if (async_itm.Canceled()) return; + Show_popup_html(Cbk_xowa_popups_show_create, Bry_.Empty, async_itm); + } + public void Ns_allowed_(byte[] raw) { + ns_allowed_regy.Clear(); + Int_obj_ref[] ns_ids = Ns_allowed_parse(wiki, raw); + int len = ns_ids.length; + for (int i = 0; i < len; i++) { + Int_obj_ref ns_id = ns_ids[i]; + ns_allowed_regy.Add(ns_id, ns_id); + } + } + public static Int_obj_ref[] Ns_allowed_parse(Xow_wiki wiki, byte[] raw) { + ListAdp rv = ListAdp_.new_(); + byte[][] ary = Bry_.Split(raw, Byte_ascii.Pipe); + int ary_len = ary.length; + Xow_ns_mgr ns_mgr = wiki.Ns_mgr(); + for (int i = 0; i < ary_len; i++) { + byte[] bry = ary[i]; + int bry_len = bry.length; if (bry_len == 0) continue; // ignore empty entries; EX: "0|" + Xow_ns ns = Bry_.Eq(bry, Xow_ns_.Ns_name_main_bry) + ? ns_mgr.Ns_main() + : ns_mgr.Names_get_or_null(bry) + ; + if (ns == null) { + wiki.App().Usr_dlg().Log_many("", "", "popup.ns_allowed: ns not in wiki: ns=~{0} wiki=~{1}", String_.new_utf8_(bry), wiki.Domain_str()); // ns may not be in wiki; EX: Portal and www.wikidata.org + continue; + } + Int_obj_ref ns_id_itm = Int_obj_ref.new_(ns.Id()); + rv.Add(ns_id_itm); + } + return (Int_obj_ref[])rv.XtoAry(Int_obj_ref.class); + } private HashAdp ns_allowed_regy = HashAdp_.new_(); private Int_obj_ref ns_allowed_regy_key = Int_obj_ref.zero_(); + private Xoa_page Cur_page() {return app.Gui_mgr().Browser_win().Active_page();} + private Xow_popup_itm Itms_get_or_null(Xoa_page page, String popup_id) {return (Xow_popup_itm)page.Popup_itms().Fetch(popup_id);} + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_show_popup_async)) Show_popup_async(); + else if (ctx.Match(k, Invk_show_popup)) Show_popup(); + else if (ctx.Match(k, Xoapi_popups.Evt_show_init_word_count_changed)) show_init_word_count = m.ReadInt("v"); + else if (ctx.Match(k, Xoapi_popups.Evt_show_more_word_count_changed)) show_more_word_count = m.ReadInt("v"); + else if (ctx.Match(k, Xoapi_popups.Evt_show_all_if_less_than_changed)) parser.Show_all_if_less_than_(m.ReadInt("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_xnde_ignore_ids_changed)) parser.Xnde_ignore_ids_(m.ReadBry("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_scan_len_changed)) parser.Scan_len_(m.ReadInt("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_scan_max_changed)) parser.Scan_max_(m.ReadInt("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_html_fmt_changed)) parser.Html_fmtr().Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_read_til_stop_fwd_changed)) parser.Read_til_stop_fwd_(m.ReadInt("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_ns_allowed_changed)) Ns_allowed_(m.ReadBry("v")); + else if (ctx.Match(k, Xoapi_popups.Evt_tmpl_tkn_max_changed)) parser.Tmpl_tkn_max_(m.ReadInt("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } + public static final String Invk_show_popup_async = "show_popup_async", Invk_show_popup = "show_popup"; + private static final String + Cbk_xowa_popups_show_update = "xowa_popups_show_update" + , Cbk_xowa_popups_show_create = "xowa_popups_show_create" + ; + private static final byte[] + Mode_show_more = Bry_.new_ascii_("more") + , Mode_show_all = Bry_.new_ascii_("all") + ; +} +class Xow_popup_mgr_ { + public static String Bld_js_cmd(Js_wtr js_wtr, String cbk, byte[] mode, byte[] href, byte[] html) { + js_wtr.Add_str(cbk); + js_wtr.Add_paren_bgn(); + js_wtr.Add_str_quote(mode).Add_comma(); + js_wtr.Add_str_quote(href).Add_comma(); + js_wtr.Add_str_quote_html(html); + js_wtr.Add_paren_end_semic(); + return js_wtr.Xto_str_and_clear(); + } +} diff --git a/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_parser.java b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_parser.java new file mode 100644 index 000000000..6cc153d83 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_parser.java @@ -0,0 +1,403 @@ +/* +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.xowa.html.modules.popups; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; import gplx.xowa.html.modules.*; +import gplx.html.*; +import gplx.xowa.apis.xowa.html.modules.*; +import gplx.xowa.parsers.lnkes.*; +import gplx.xowa.gui.views.*; +public class Xow_popup_parser { + private Xoa_app app; private Xow_wiki wiki; private Xop_parser parser; + private ByteTrieMgr_fast orig_trie, wtxt_trie; private Xop_tkn_mkr tkn_mkr; + private Xop_ctx orig_ctx; private Xop_root_tkn orig_root, wtxt_root; private Xot_compile_data orig_props = new Xot_compile_data(); + private Bry_bfr hdom_bfr = Bry_bfr.reset_(255); + private Xoh_html_wtr_ctx hctx = Xoh_html_wtr_ctx.Popup; + private boolean add_wtxt_skip_space = false; + private boolean stop_at_hdr_done; + private int words_found; + private int words_needed = -1; + private Cancelable cancelable; private Bry_bfr tmp_bfr = Bry_bfr.reset_(32); + public Xop_ctx Wtxt_ctx() {return wtxt_ctx;} private Xop_ctx wtxt_ctx; + private ListAdp words_found_list = ListAdp_.new_(); + public int Scan_len() {return scan_len;} public void Scan_len_(int v) {scan_len = v;} private int scan_len = Xoapi_popups.Dflt_scan_len; + public int Scan_max() {return scan_max;} public void Scan_max_(int v) {scan_max = v;} private int scan_max = Xoapi_popups.Dflt_scan_max; + public int Show_all_if_less_than() {return show_all_if_less_than;} public void Show_all_if_less_than_(int v) {show_all_if_less_than = v;} private int show_all_if_less_than = Xoapi_popups.Dflt_show_all_if_less_than; + public byte[] Ellipsis() {return ellipsis;} public void Ellipsis_(byte[] v) {ellipsis = v;} private byte[] ellipsis = Ellipsis_const; + public byte[] Notoc() {return notoc;} public void Notoc_(byte[] v) {notoc = v;} private byte[] notoc = Notoc_const; + public boolean Stop_at_hdr() {return stop_at_hdr;} public void Stop_at_hdr_(boolean v) {stop_at_hdr = v;} private boolean stop_at_hdr = false; + public boolean Output_js_clean() {return output_js_clean;} public void Output_js_clean_(boolean v) {output_js_clean = v;} private boolean output_js_clean = true; + public boolean Output_tidy() {return output_tidy;} public void Output_tidy_(boolean v) {output_tidy = v;} private boolean output_tidy = true; + public int Read_til_stop_fwd() {return read_til_stop_fwd;} public void Read_til_stop_fwd_(int v) {read_til_stop_fwd = v;} private int read_til_stop_fwd = Xoapi_popups.Dflt_read_til_stop_fwd; + public int Read_til_stop_bwd() {return read_til_stop_bwd;} public void Read_til_stop_bwd_(int v) {read_til_stop_bwd = v;} private int read_til_stop_bwd = Xoapi_popups.Dflt_read_til_stop_bwd; + public int Tmpl_tkn_max() {return tmpl_tkn_max;} + public void Tmpl_tkn_max_(int v) { + if (v < 0) v = Int_.MaxValue; // allow -1 as shortcut to deactivate + orig_ctx.Tmpl_tkn_max_(v); + wtxt_ctx.Tmpl_tkn_max_(v); + } private int tmpl_tkn_max = Xoapi_popups.Dflt_tmpl_tkn_max; + public Bry_fmtr Html_fmtr() {return html_fmtr;} + public void Init_by_wiki(Xow_wiki wiki) { + this.wiki = wiki; this.app = wiki.App(); this.parser = wiki.Parser(); this.tkn_mkr = app.Tkn_mkr(); + this.orig_ctx = Xop_ctx.new_(wiki); this.wtxt_ctx = Xop_ctx.new_(wiki); + Xop_lxr_mgr orig_lxr_mgr = Xop_lxr_mgr.Popup_lxr_mgr; + orig_lxr_mgr.Init_by_wiki(wiki); + this.orig_trie = orig_lxr_mgr.Trie(); this.wtxt_trie = parser.Wtxt_lxr_mgr().Trie(); + orig_ctx.Parse_tid_(Xop_parser_.Parse_tid_page_tmpl); wtxt_ctx.Parse_tid_(Xop_parser_.Parse_tid_page_wiki); + orig_ctx.Xnde_names_tid_(Xop_parser_.Parse_tid_page_wiki); + orig_ctx.Tid_is_popup_(true); wtxt_ctx.Tid_is_popup_(true); + orig_root = tkn_mkr.Root(Bry_.Empty); wtxt_root = tkn_mkr.Root(Bry_.Empty); + html_fmtr.Eval_mgr_(wiki.Eval_mgr()); + view_time_fmtr.Eval_mgr_(wiki.Eval_mgr()); + xwiki_fmtr.Eval_mgr_(wiki.Eval_mgr()); + ellipsis = wiki.Msg_mgr().Val_by_key_obj(Msg_key_ellipsis); + } private static byte[] Msg_key_ellipsis = Bry_.new_ascii_("ellipsis"); + private Hash_adp_bry xnde_id_ignore_list = Hash_adp_bry.ci_ascii_(); + public void Xnde_ignore_ids_(byte[] xnde_id_ignore_bry) { + byte[][] ary = Bry_.Split(xnde_id_ignore_bry, Byte_ascii.Pipe); + int ary_len = ary.length; + xnde_id_ignore_list.Clear(); + for (int i = 0; i < ary_len; i++) { + byte[] bry = ary[i]; + if (bry.length == 0) continue; // ignore empty entries; EX: "a|" + xnde_id_ignore_list.Add(bry, bry); + } + } + private int words_needed_orig = -1; + public byte[] Parse(Xow_popup_itm popup_itm, Xoa_page page, byte[] cur_wiki_domain, Xog_tab_itm cur_tab) { + byte[] orig_src = page.Data_raw(); int orig_len = orig_src.length; if (orig_len == 0) return Bry_.Empty; + String page_url = hdom_bfr.Add(page.Wiki().Domain_bry()).Add(Xoa_consts.Url_wiki_intermediary).Add(app.Url_converter_href() + .Encode(page.Ttl().Full_db())) // NOTE: was page.Url().Raw(), but that doesn't work for Special:Search; PAGE:en.w:Earth and "Quotations"; DATE:2014-06-29 + .XtoStrAndClear() + ; + this.cancelable = popup_itm; + int orig_pos = Xop_parser_.Doc_bgn_bos; + orig_ctx.Clear(); + orig_ctx.Cur_page().Ttl_(page.Ttl()); // NOTE: must set cur_page, else page-dependent templates won't work; EX: {{FULLPAGENAME}}; PAGE:en.w:June_20; DATE:2014-06-20 + orig_ctx.Page_bgn(orig_root, orig_src); + Wtxt_ctx_init(true, orig_src); + orig_ctx.Cur_page().Ttl_(page.Ttl()); // NOTE: must set cur_page, or rel lnkis won't work; EX: [[../A]] + hdom_bfr.Clear(); + int scan_cur = scan_len; + words_found_list.Clear(); + words_needed = popup_itm.Words_needed(); words_found = 0; stop_at_hdr_done = false; + if (read_til_stop_fwd > 0 && words_needed != Int_.MaxValue) { + words_needed_orig = words_needed; + words_needed += read_til_stop_fwd; + } + else + words_needed_orig = -1; + if (orig_len < show_all_if_less_than) words_needed = Int_.MaxValue; + if (words_needed == Int_.MaxValue) scan_max = Int_.MaxValue; // if max words, automatically set scan to max + while (words_found < words_needed) { + if (cancelable.Canceled()) return null; + orig_root.Clear(); + int orig_end = orig_pos + scan_cur; if (orig_end > orig_len) orig_end = orig_len; // limit to orig_len; EX: page is 16 bytes, but block is 1024 + if (cur_tab != null && cur_tab.Tab_is_loading()) return null; + int new_pos = parser.Parse_to_stack_end(orig_root, orig_ctx, tkn_mkr, orig_src, orig_len, orig_trie, orig_pos, orig_end); + if (cur_tab != null && cur_tab.Tab_is_loading()) return null; + byte[] wtxt_bry = Parse_to_wtxt(orig_src); + int wtxt_len = wtxt_bry.length; // if (wtxt_len == 0) {break;}// no wtxt; continue; EX: blank page + wtxt_root.Clear(); + int wtxt_bgn = (orig_pos == Xop_parser_.Doc_bgn_bos) ? Xop_parser_.Doc_bgn_bos : 0; // if first pass, parse from -1; needed for lxrs which assume nl at bos; EX: "*a" + if (cur_tab != null && cur_tab.Tab_is_loading()) return null; + parser.Parse_to_src_end(wtxt_root, wtxt_ctx, tkn_mkr, wtxt_bry, wtxt_trie, wtxt_bgn, wtxt_len); + if ( wtxt_ctx.Stack_len() > 0 // dangling lnki / hdr / tblw + && (orig_pos + scan_cur) < scan_max // too much read; stop and give whatever's available; PAGE:en.w:List_of_air_forces; DATE:2014-06-18 + && scan_cur < orig_len // only reparse if scan_cur is < entire page; needed for pages which have dangling items; EX:"a" + ) { + new_pos = orig_pos; //;stack_0.Src_bgn(); + scan_cur += scan_len; + wtxt_ctx.Clear(); + } + else { + Add_to_hdom_bfr(wtxt_root, wtxt_bry, wtxt_len); + scan_cur = scan_len; + } + orig_pos = new_pos; + if ( orig_pos == orig_len + || orig_pos > scan_max // too much read; stop and give whatever's available + ) break; + } + Adjust_for_header(); + if ( words_found >= words_needed // don't add ellipsis if words_found < words_needed. note that it will add "..." if words_on_page == words_needed; DATE:2014-06-18 + && !stop_at_hdr_done // don't add ellipsis if stopped at hdr + ) + hdom_bfr.Add(ellipsis); + if (cancelable.Canceled()) return null; + if (cur_tab != null && cur_tab.Tab_is_loading()) return null; + byte[] rv = Parse_to_html(page); + html_fmtr.Bld_bfr_many(hdom_bfr + , rv + , wiki.Lang().Dir_bry() + , page_url, String_.new_utf8_(page.Ttl().Full_txt()) + , cur_wiki_domain // NOTE: use cur_wiki, not page_wiki; DATE:2014-06-28 + , Get_xwiki_item(tmp_bfr, cur_wiki_domain, page.Wiki().Domain_bry()) + , gplx.ios.Io_size_.Xto_str(page.Data_raw().length), page.Revision_data().Modified_on().XtoStr_fmt_yyyy_MM_dd_HH_mm_ss() + , Get_view_time(page.Ttl()) + , app.Fsys_mgr().Root_dir().To_http_file_bry() + , popup_itm.Popup_id() + ); + if (cancelable.Canceled()) return null; + popup_itm.Popup_html_word_count_(words_found); + return hdom_bfr.XtoAryAndClear(); + } + private void Adjust_for_header() { + Xow_popup_word[] words_found_ary = (Xow_popup_word[])words_found_list.XtoAryAndClear(Xow_popup_word.class); + int words_found_all = words_found_ary.length; + boolean read_til_stop_done = false; + if (read_til_stop_fwd != -1 && words_needed_orig != -1) { + Xow_popup_word hdr_word = null; + for (int i = words_needed_orig; i < words_found_all; ++i) { + Xow_popup_word word = words_found_ary[i]; + if (word.Tid() == Xop_tkn_itm_.Tid_hdr) { + hdr_word = word; + break; + } + } + if (hdr_word == null) { + if (words_needed_orig < words_found_all) { + hdr_word = words_found_ary[words_needed_orig - 1]; + hdom_bfr.Delete_rng_to_end(hdr_word.Bfr_end()); + } + } + else { + hdr_word = words_found_ary[hdr_word.Idx() - 1]; + hdom_bfr.Delete_rng_to_end(hdr_word.Bfr_end()); + read_til_stop_done = true; + } + } + if ( read_til_stop_bwd != -1 + && !read_til_stop_done + ) { +// Xow_popup_word hdr_word = null; +// for (int i = words_needed_orig; i < words_found_all; ++i) { +// Xow_popup_word word = words_found_ary[i]; +// if (word.Tid() == Xop_tkn_itm_.Tid_hdr) { +// hdr_word = word; +// break; +// } +// } + } + } + private byte[] Parse_to_wtxt(byte[] src) { + int subs_len = orig_root.Subs_len(); + for (int i = 0; i < subs_len; i++) + orig_root.Subs_get(i).Tmpl_compile(orig_ctx, src, orig_props); + return Xot_tmpl_wtr._.Write_all(orig_ctx, orig_root, src); + } + private byte[] Parse_to_html(Xoa_page page) { + hdom_bfr.Add(notoc); + byte[] hdom_bry = hdom_bfr.XtoAryAndClear(); + wtxt_root.Clear(); + Wtxt_ctx_init(Bool_.N, hdom_bry); + parser.Parse_to_src_end(wtxt_root, wtxt_ctx, tkn_mkr, hdom_bry, wtxt_trie, Xop_parser_.Doc_bgn_bos, hdom_bry.length); + wtxt_ctx.Page_end(wtxt_root, hdom_bry, hdom_bry.length); + wiki.Html_mgr().Html_wtr().Write_all(hdom_bfr, wtxt_ctx, hctx, hdom_bry, wtxt_root); + app.Html_mgr().Js_cleaner().Clean_bfr(wiki, page.Ttl(), hdom_bfr, 0); + if (output_tidy) + app.Html_mgr().Tidy_mgr().Run_tidy_html(page, hdom_bfr); + return hdom_bfr.XtoAryAndClear(); + } + private void Wtxt_ctx_init(boolean incremental, byte[] bry) { + wtxt_ctx.Clear(); + wtxt_ctx.Para().Enabled_(!incremental); // NOTE: if incremental, disable para; easier to work with \n rather than

    ; also, must be enabled before Page_bgn; DATE:2014-06-18DATE:2014-06-18 + wtxt_ctx.Lnke().Dangling_goes_on_stack_(incremental); + wtxt_ctx.Page_bgn(wtxt_root, bry); + } + private byte[] Get_view_time(Xoa_ttl ttl) { + byte[] view_time_item = Bry_.Empty; + gplx.xowa.users.history.Xou_history_itm history_itm = app.User().History_mgr().Get_or_null(wiki.Domain_bry(), ttl.Full_txt()); + if (history_itm != null) + view_time_item = view_time_fmtr.Bld_bry_many(hdom_bfr, history_itm.View_end().XtoStr_fmt_yyyy_MM_dd_HH_mm_ss()); + return view_time_item; + } + private byte[] Get_xwiki_item(Bry_bfr tmp_bfr, byte[] wiki_domain, byte[] page_domain) { + return Bry_.Eq(wiki_domain, page_domain) + ? Bry_.Empty // same domain; return ""; + : xwiki_fmtr.Bld_bry_many(tmp_bfr, page_domain); + } + private void Increment_words_found(Xop_tkn_itm tkn) { + words_found_list.Add(new Xow_popup_word(tkn.Tkn_tid(), hdom_bfr.Len(), words_found, tkn.Src_bgn(), tkn.Src_end())); + ++words_found; + } + private void Add_to_hdom_bfr(Xop_tkn_itm tkn, byte[] wtxt_bry, int wtxt_len) { + if (cancelable.Canceled()) return; + boolean add = true, recur = true; Xop_xnde_tkn xnde = null; + int tkn_src_bgn = tkn.Src_bgn(), tkn_src_end = tkn.Src_end(); + switch (tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_root: + add = false; // don't add root + break; + case Xop_tkn_itm_.Tid_txt: + Increment_words_found(tkn); + break; + case Xop_tkn_itm_.Tid_tblw_tb: case Xop_tkn_itm_.Tid_tblw_tc: case Xop_tkn_itm_.Tid_tblw_td: + case Xop_tkn_itm_.Tid_tblw_te: case Xop_tkn_itm_.Tid_tblw_th: case Xop_tkn_itm_.Tid_tblw_tr: + add = recur = false; // skip tblws + break; + case Xop_tkn_itm_.Tid_xnde: + xnde = (Xop_xnde_tkn)tkn; + switch (xnde.Tag().Id()) { + case Xop_xnde_tag_.Tid_ref: + case Xop_xnde_tag_.Tid_div: + case Xop_xnde_tag_.Tid_gallery: + case Xop_xnde_tag_.Tid_imageMap: + case Xop_xnde_tag_.Tid_xowa_html: // needed for Help:Options, else \n at top of doc; DATE:2014-06-22 + case Xop_xnde_tag_.Tid_table: case Xop_xnde_tag_.Tid_tr: case Xop_xnde_tag_.Tid_td: case Xop_xnde_tag_.Tid_th: + case Xop_xnde_tag_.Tid_caption: case Xop_xnde_tag_.Tid_thead: case Xop_xnde_tag_.Tid_tfoot: case Xop_xnde_tag_.Tid_tbody: + add = recur = false; // skip tblxs + xnde = null; + break; + case Xop_xnde_tag_.Tid_br: + if (hdom_bfr.Len_eq_0()) // don't add
    to start of document; needed for Help:Options, but good to have everywhere; DATE:2014-06-22 + add = recur = false; + break; + default: + add = false; // don't add xnde, but still recur + if (Xnde_id_ignore_list_chk(xnde, wtxt_bry)) { + recur = false; + xnde = null; + } + break; + } + break; + case Xop_tkn_itm_.Tid_lnke: + Xop_lnke_tkn lnke = (Xop_lnke_tkn)tkn; + switch (lnke.Lnke_typ()) { + case Xop_lnke_tkn.Lnke_typ_brack: + Add_to_hdom_bfr_recurse(tkn, wtxt_bry, wtxt_len, Bool_.N); // add subs which are caption tkns; note that Bool_.N will add all words so that captions don't get split; EX: "a [http://a.org b c d]" -> "a b c d" if words_needed == 2; + add = recur = false; // ignore lnke, but add any text tkns; EX: [http://a.org b c d] -> "b c d" + break; + case Xop_lnke_tkn.Lnke_typ_text: + Increment_words_found(tkn); // increment words_found; EX: a http://b.org c -> 3 words; + break; + } + break; + case Xop_tkn_itm_.Tid_lnki: + Xop_lnki_tkn lnki = (Xop_lnki_tkn)tkn; + switch (lnki.Ns_id()) { + case Xow_ns_.Id_category: // skip [[Category:]] + case Xow_ns_.Id_file: // skip [[File:]] + add = recur = false; + break; + default: + Increment_words_found(tkn); // increment words_found; EX: a [[B|c d e]] f -> 3 words; + break; + } + break; + case Xop_tkn_itm_.Tid_space: + if ( add_wtxt_skip_space // previous tkn skipped add and set add_wtxt_skip_space to true + && hdom_bfr.Match_end_byt_nl_or_bos() // only ignore space if it will cause pre; note that some s will have spaces that should be preserved; EX:"ab c"; PAGE:en.w:Mehmed_the_Conqueror; DATE:2014-06-18 + ) + add = false; // skip ws + break; + case Xop_tkn_itm_.Tid_newLine: { + // heuristic to handle skipped

    / which does not skip \n; EX:"
    a
    \nb"; div is skipped, but "\n" remains; PAGE:en.w:Eulogy;DATE:2014-06-18 + int hdom_bfr_len = hdom_bfr.Len(); + if (hdom_bfr_len == 0) // don't add \n at bos; does not handle pages where bos intentionally has multiple \n\n + add = false; + else if (hdom_bfr_len > 2) { // bounds check + if (Wtxt_bfr_ends_w_2_nl(hdom_bfr_len)) // don't add \n if "\n\n"; does not handle intentional sequences of 2+ \n; + add = false; + } + break; + } + case Xop_tkn_itm_.Tid_hdr: { + if (stop_at_hdr && words_found != 0) { // if 1st word is header (no intro para), don't exit + words_found = words_needed; + stop_at_hdr_done = true; + return; + } + Increment_words_found(tkn); // count entire header as one word; not worth counting words in header + recur = false; // add entire tkn; do not recur + int hdom_bfr_len = hdom_bfr.Len(); + if (hdom_bfr_len > 2) { // bounds check + if (Wtxt_bfr_ends_w_2_nl(hdom_bfr_len)) // heuristic: 2 \n in bfr, and about to add a hdr tkn which starts with "\n"; delete last \n + hdom_bfr.Del_by_1(); + } + if ( tkn_src_end < wtxt_len // bounds check + && wtxt_bry[tkn_src_end] == Byte_ascii.NewLine // hdr_tkn will not include trailing "\n". add it; note that this behavior is by design. NOTE:hdr.trailing_nl; DATE:2014-06-17 + ) { + hdom_bfr.Add_mid(wtxt_bry, tkn_src_bgn, tkn_src_end + 1); // +1 to add the trailing \n + add = false; + } + break; + } + default: + break; + } + add_wtxt_skip_space = false; // always reset; only used once above for Tid_space; DATE:2014-06-17 + if (add) { + if (tkn_src_end - tkn_src_bgn > 0) // handle paras which have src_bgn == src_end + hdom_bfr.Add_mid(wtxt_bry, tkn_src_bgn, tkn_src_end); + } + else // tkn not added + add_wtxt_skip_space = true; // skip next space; note this is done with member variable to handle recursive iteration; DATE:2014-06-17 + if (recur) { + if (xnde != null) hdom_bfr.Add_mid(wtxt_bry, xnde.Tag_open_bgn(), xnde.Tag_open_end()); // add open tag; EX: "" + Add_to_hdom_bfr_recurse(tkn, wtxt_bry, wtxt_len, Bool_.Y); + if (xnde != null) hdom_bfr.Add_mid(wtxt_bry, xnde.Tag_close_bgn(), xnde.Tag_close_end()); // add close tag; EX: "" + } + } + private void Add_to_hdom_bfr_recurse(Xop_tkn_itm tkn, byte[] wtxt_bry, int wtxt_len, boolean chk_words_found) { + int subs_len = tkn.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Xop_tkn_itm sub = tkn.Subs_get(i); + Add_to_hdom_bfr(sub, wtxt_bry, wtxt_len); + if (chk_words_found && words_found >= words_needed) break; + } + } + private boolean Xnde_id_ignore_list_chk(Xop_xnde_tkn xnde, byte[] src) { + Xop_xatr_itm[] atrs_ary = xnde.Atrs_ary(); + int atrs_len = atrs_ary.length; + for (int i = 0; i < atrs_len; i++) { + Xop_xatr_itm atr = atrs_ary[i]; + if ( Bry_.Eq(atr.Key_bry(), Html_atrs.Id_bry) + && xnde_id_ignore_list.Get_by_bry(atr.Val_as_bry(src)) != null + ) { + return true; + } + } + return false; + } + private boolean Wtxt_bfr_ends_w_2_nl(int hdom_bfr_len) { + byte[] hdom_bfr_bry = hdom_bfr.Bfr(); + return + ( hdom_bfr_bry[hdom_bfr_len - 1] == Byte_ascii.NewLine // prv 2 bytes are \n + && hdom_bfr_bry[hdom_bfr_len - 2] == Byte_ascii.NewLine + ); + } + private static final byte[] + Notoc_const = Bry_.new_ascii_(" __NOTOC__") // NOTE: always add a space else __NOTOC__ will be deactivated if last tkn is lnke; DATE:2014-06-22 + , Ellipsis_const = Bry_.new_ascii_("...") + ; + private Bry_fmtr + html_fmtr = Bry_fmtr.keys_(Xoapi_popups.Dflt_html_fmt_keys) + , view_time_fmtr = Bry_fmtr.new_("\n ~{<>msgs.get('api-xowa.html.modules.popups.msgs.viewed-name');<>}:~{view_time}", "view_time") + , xwiki_fmtr = Bry_fmtr.new_("\n ~{<>msgs.get('api-xowa.html.modules.popups.msgs.wiki-name');<>}:~{wiki_domain}", "wiki_domain") + ; +} +class Xow_popup_word { + public Xow_popup_word(int tid, int bfr_bgn, int idx, int bgn, int end) {this.tid = tid; this.bfr_bgn = bfr_bgn; this.idx = idx; this.bgn = bgn; this.end = end;} + public int Tid() {return tid;} private int tid; + public int Bfr_bgn() {return bfr_bgn;} private int bfr_bgn; + public int Bfr_end() {return bfr_bgn + this.Len();} + public int Idx() {return idx;} private int idx; + public int Bgn() {return bgn;} private int bgn; + public int End() {return end;} private int end; + public int Len() {return end - bgn;} +} diff --git a/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_parser_tst.java b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_parser_tst.java new file mode 100644 index 000000000..7670955d5 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/modules/popups/Xow_popup_parser_tst.java @@ -0,0 +1,390 @@ +/* +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.xowa.html.modules.popups; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; import gplx.xowa.html.modules.*; +import org.junit.*; +import gplx.xowa.apis.xowa.html.modules.*; +import gplx.xowa.gui.views.*; +public class Xow_popup_parser_tst { + @Before public void init() {fxt.Clear();} private Xop_popup_parser_fxt fxt = new Xop_popup_parser_fxt(); + @Test public void Text_chars_one() { + fxt.Test_parse + ( "a b c d", String_.Concat_lines_nl_skip_last + ( "

    a b" + , "

    " + )); + } + @Test public void Text_chars_many() { // PURPOSE: text.read_spans_scan + fxt.Test_parse + ( "abc def ghi", String_.Concat_lines_nl_skip_last + ( "

    abc def" + , "

    " + )); + } + @Test public void Text_chars_bound() {// PURPOSE: text.word_spans_scan + fxt.Test_parse + ( "abcde fghij k l", String_.Concat_lines_nl_skip_last + ( "

    abcde fghij" + , "

    " + )); + } + @Test public void Apos() { + fxt.Test_parse + ( "'''ab''' ''c'' de", String_.Concat_lines_nl_skip_last + ( "

    ab c" + , "

    " + , "" + )); + } + @Test public void Lnki() { + fxt.Test_parse("a [[b|c d e]] f" + , String_.Concat_lines_nl_skip_last + ( "

    a c d e" + , "

    " + )); + } + @Test public void Lnke_brack() { // PURPOSE: count lnke caption words; DATE:2014-06-20 + fxt.Init_scan_len_(32).Init_word_min_(5).Test_parse + ( "a [http://b.org b c] d e f g", String_.Concat_lines_nl_skip_last + ( "

    a b c d e" + , "

    " + )); + } + @Test public void Lnke_text() { // PURPOSE: count entire lnke as one word + fxt.Init_scan_len_(32).Init_word_min_(5).Test_parse + ( "a http://b.org c d e f g", String_.Concat_lines_nl_skip_last + ( "

    a http://b.org c d e" + , "

    " + )); + } + @Test public void Lnke_dangling() { // PURPOSE: handle dangling lnke; DATE:2014-06-20 + fxt.Test_parse + ( "a [http://b.org c d] e f g", String_.Concat_lines_nl_skip_last // NOTE: scan_len = 4, so 1st pass will be "a [h" + ( "

    a c d" // NOTE: (a) lnke correctly parsed, else would see "[" or "http"; (b) "c d" counts as 1 word + , "

    " + )); + } + @Test public void Hdr() { + fxt.Test_parse + ( "a\n===b===\n c", String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + , "" + , "

    b

    " + )); + } + @Test public void Hdr_one_word() { // PURPOSE: hdr.entire_tkn_counts_as_one_word + fxt.Test_parse + ( "===a b c===\nd", String_.Concat_lines_nl_skip_last + ( "

    a b c

    " + , "" + , "

    d" + , "

    " + )); + } + @Test public void Hdr_para() { // PURPOSE: hdr.para; handle para mode and hdr (para causes trailing \n to be para, not \n); PAGE:en.w:Flavius_Valerius_Severus DATE:2014-06-17 + fxt.Init_para_enabled_(true).Test_parse(String_.Concat_lines_nl_skip_last + ( "" + , "a" + , "" + , "==b==" + , "c" + , "" + , "d" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + , "" + , "

    b

    " + , "" + )); + } + @Test public void List() { + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "a" + , "*b" + , "c" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + , "" + , "
      " + , "
    • b" + , "
    • " + , "
    " + )); + } + @Test public void Xnde_pair() { + fxt.Test_parse + ( "b" + , String_.Concat_lines_nl_skip_last + ( "

    b" + , "

    " + )); + } + @Test public void Xnde_inline() { + fxt.Test_parse + ( "" + , String_.Concat_lines_nl_skip_last + ( "

    " + , "

    " + )); + } + @Test public void Ignore_tblw() {// also checks for tbl spanning multiple blocks; PAGE:en.w:Stratosphere; DATE:2014-06-17 + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "a " + , "{|" + , "|-" + , "|b" + , "|} c" + ), String_.Concat_lines_nl_skip_last + ( "

    a c" + , "

    " + )); + } + @Test public void Ignore_tblw_nested() {// PAGE:en.w:Cosmoloyg; DATE:2014-06-17 + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "a" + , "{|" + , "|-" + , "|b" + , "|}" + , "" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "" + , "

    " + )); + } + @Test public void Ignore_tblx() { + fxt.Test_parse + ( "a
    b
    c" + , String_.Concat_lines_nl_skip_last + ( "

    a c" + , "

    " + )); + } + @Test public void Ignore_ref() { + fxt.Test_parse + ( "a b c" + , String_.Concat_lines_nl_skip_last + ( "

    a c" + , "

    " + )); + } + @Test public void Ignore_div() { + fxt.Test_parse + ( "a
    b
    c" + , String_.Concat_lines_nl_skip_last + ( "

    a c" + , "

    " + )); + } + @Test public void Ignore_space_bos() { // pre. ignore spaces, else pre; PAGE:en.w:Volcano; en.w:War_elephant; DATE:2014-06-17 + fxt.Test_parse + ( "
    a
    b c d" // spaces before "b" are ignored + , String_.Concat_lines_nl_skip_last + ( "

    b c" + , "

    " + )); + } + @Test public void Ignore_space_nl() { + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "a" + , "
    b
    c" // space before "c" is ignored + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "c" + , "

    " + )); + } + @Test public void Ignore_nl_bos() { + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "" + , "" + , "a" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + )); + } + @Test public void Ignore_nl_multiple() { + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "a" + , "" + , "" + , "" // ignored + , "b" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + , "" + , "

    b" + , "

    " + )); + } + @Test public void Ignore_nl_hdr() { + fxt.Test_parse(String_.Concat_lines_nl_skip_last + ( "a" + , "" + , "" + , "" // ignored + , "==b==" + ), String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + , "" + , "

    b

    " + )); + } + @Test public void Ignore_lnki_file() { + fxt.Test_parse + ( "a [[File:b.png|thumb]] c" + , String_.Concat_lines_nl_skip_last + ( "

    a c" + , "

    " + )); + } + @Test public void Ignore_gallery() { + fxt.Test_parse + ( "a File:B.png|c d" + , String_.Concat_lines_nl_skip_last + ( "

    a d" + , "

    " + )); + } + @Test public void Ignore_xnde() { + fxt.Test_parse + ( "a b c" + , String_.Concat_lines_nl_skip_last + ( "

    a c" + , "

    " + )); + } + @Test public void Dangling() { // make sure dangling nodes don't fail + fxt.Test_parse + ( "a" + , String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + )); + } + @Test public void End_early_dangling() { // PURPOSE: dangling tkn is too long; end early; PAGE:en.w:List_of_air_forces; DATE:2014-06-18 + fxt.Init_scan_max_(8).Test_parse + ( "a [[File:Test.png]] k" + , String_.Concat_lines_nl_skip_last + ( "

    a " + , "

    " + )); + } + @Test public void Ellipsis_() { + fxt.Init_ellipsis_("...").Test_parse + ( "a b c d" + , String_.Concat_lines_nl_skip_last + ( "

    a b..." + , "

    " + )); + fxt.Test_parse // no ellipsis: entire extract + ( "a" + , String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + )); + fxt.Test_parse // no ellipsis: entire extract with skip + ( "a
    b
    " + , String_.Concat_lines_nl_skip_last + ( "

    a " + , "

    " + )); + } + @Test public void Stop_at_hdr() { + fxt.Init_ellipsis_("...").Init_stop_at_header_(true).Test_parse + ( "a\n==b==\nc" + , String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + )); + } + @Test public void Ns_allowed() { + fxt.Test_ns_allowed("Help" , Xow_ns_.Id_help); + fxt.Test_ns_allowed("(Main)" , Xow_ns_.Id_main); + fxt.Test_ns_allowed("" ); + fxt.Test_ns_allowed("(Main)|Help" , Xow_ns_.Id_main, Xow_ns_.Id_help); + } + @Test public void Read_til_stop_fwd() { + fxt.Init_word_min_(2).Init_read_til_stop_fwd_(2).Test_parse("a b c\n==d==", String_.Concat_lines_nl_skip_last + ( "

    a b c" + , "

    " + )); + fxt.Init_word_min_(2).Init_read_til_stop_fwd_(2).Test_parse("a b c d", String_.Concat_lines_nl_skip_last + ( "

    a b" + , "

    " + )); + } + @Test public void Tmpl_tkn_max() { + fxt.Init_tmpl_tkn_max_(5).Init_page("Template:A", "a"); + fxt.Test_parse + ( "{{A}}" + , String_.Concat_lines_nl_skip_last + ( "

    a" + , "

    " + )); + fxt.Test_parse( "{{A|b|c}}", "

    \n

    "); + } +} +class Xop_popup_parser_fxt { + private Xow_popup_parser parser; private Xow_wiki wiki; + private int word_min = 2; + public void Clear() { + Xoa_app app = Xoa_app_fxt.app_(); + this.wiki = Xoa_app_fxt.wiki_(app, "en.wiki"); + parser = wiki.Html_mgr().Module_mgr().Popup_mgr().Parser(); + parser.Init_by_wiki(wiki); + parser.Scan_len_(4); + parser.Html_fmtr().Fmt_("~{content}"); + parser.Ellipsis_(Bry_.Empty); + parser.Notoc_(Bry_.Empty); + parser.Stop_at_hdr_(false); + parser.Output_js_clean_(false); + parser.Output_tidy_(false); + parser.Show_all_if_less_than_(-1); + parser.Xnde_ignore_ids_(Xoapi_popups.Dflt_coordinates); + parser.Read_til_stop_fwd_(-1); + word_min = 2; + } + public Xop_popup_parser_fxt Init_scan_len_(int v) {parser.Scan_len_(v); return this;} + public Xop_popup_parser_fxt Init_scan_max_(int v) {parser.Scan_max_(v); return this;} + public Xop_popup_parser_fxt Init_word_min_(int v) {word_min = v; return this;} + public Xop_popup_parser_fxt Init_para_enabled_(boolean v) {parser.Wtxt_ctx().Para().Enabled_(v); return this;} + public Xop_popup_parser_fxt Init_stop_at_header_(boolean v) {parser.Stop_at_hdr_(v); return this;} + public Xop_popup_parser_fxt Init_ellipsis_(String v) {parser.Ellipsis_(Bry_.new_utf8_(v)); return this;} + public Xop_popup_parser_fxt Init_read_til_stop_fwd_(int v) {parser.Read_til_stop_fwd_(v); return this;} + public Xop_popup_parser_fxt Init_tmpl_tkn_max_(int v) {parser.Tmpl_tkn_max_(v); return this;} + public Xop_popup_parser_fxt Init_page(String ttl, String txt) {Xop_fxt.Init_page_create_static(wiki, ttl, txt); return this;} + public Xop_popup_parser_fxt Test_ns_allowed(String raw, int... expd) { + Int_obj_ref[] ids = Xow_popup_mgr.Ns_allowed_parse(wiki, Bry_.new_utf8_(raw)); + Tfds.Eq_ary(expd, Int_obj_ref.Ary_xto_int_ary(ids)); + return this; + } + public void Test_parse(String raw, String expd) { + Xoa_page page = Xoa_page.create_(wiki, Xoa_ttl.parse_(wiki, Bry_.new_ascii_("Test_1"))); + page.Data_raw_(Bry_.new_utf8_(raw)); + Xow_popup_itm itm = new Xow_popup_itm(1, Bry_.new_utf8_(raw), word_min); + byte[] actl = parser.Parse(itm, page, wiki.Domain_bry(), null); + Tfds.Eq_str_lines(expd, String_.new_utf8_(actl)); + } +} diff --git a/400_xowa/src/gplx/xowa/html/portal/Xoa_available_wikis_mgr.java b/400_xowa/src/gplx/xowa/html/portal/Xoa_available_wikis_mgr.java new file mode 100644 index 000000000..d59e430b8 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/portal/Xoa_available_wikis_mgr.java @@ -0,0 +1,52 @@ +/* +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.xowa.html.portal; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.wikis.*; +public class Xoa_available_wikis_mgr implements GfoInvkAble { + private Bry_fmtr itms_as_html_fmtr = Bry_fmtr.new_("\n
  • ~{domain}
  • ", "domain", "itm_cls"); + public Xoa_available_wikis_mgr(Xoa_app app) {this.app = app;} private Xoa_app app; + public String Itms_as_html() { + if (itms_as_html == null) { + boolean popups_enabled = app.Api_root().Html().Modules().Popups().Enabled(); + String itm_cls = popups_enabled ? " class='xowa-hover-off'" : ""; + Bry_bfr tmp_bfr = Bry_bfr.new_(); // NOTE: do not use app.Utl_bry_bfr_mkr().Get_k004() as it is being used simultaneously by another caller; TODO: find call + Xow_xwiki_mgr xwiki_mgr = app.User().Wiki().Xwiki_mgr(); + xwiki_mgr.Sort_by_key(); + int len = xwiki_mgr.Len(); + for (int i = 0; i < len; i++) { + Xow_xwiki_itm itm = xwiki_mgr.Get_at(i); + if (itm.Wiki_tid() == Xow_wiki_domain_.Tid_home) continue;// don't show home wiki + itms_as_html_fmtr.Bld_bfr_many(tmp_bfr, itm.Domain(), itm_cls); + } + itms_as_html = tmp_bfr.XtoStr(); + } + return itms_as_html; + } private String itms_as_html; + public void Itms_refresh() {itms_as_html = null;} + public boolean Visible() {return visible;} private boolean visible = true; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_itms_as_html)) return this.Itms_as_html(); + else if (ctx.Match(k, Invk_itms_refresh)) Itms_refresh(); + else if (ctx.Match(k, Invk_visible)) return Yn.X_to_str(visible); + else if (ctx.Match(k, Invk_visible_)) visible = m.ReadYn("v"); + else if (ctx.Match(k, Invk_visible_toggle)) {visible = !visible; app.Gui_mgr().Browser_win().Active_html_box().Html_js_eval_proc("xowa-portal-wikis-visible-toggle", Bool_.XtoStr_lower(visible));} + else if (ctx.Match(k, Invk_itms_as_html_fmtr_)) itms_as_html_fmtr.Fmt_(m.ReadBry("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } private static final String Invk_visible = "visible", Invk_visible_ = "visible_", Invk_visible_toggle = "visible_toggle", Invk_itms_as_html = "itms_as_html", Invk_itms_as_html_fmtr_ = "itms_as_html_fmtr_", Invk_itms_refresh = "itms_refresh"; +} diff --git a/400_xowa/src/gplx/xowa/html/portal/Xoa_portal_mgr.java b/400_xowa/src/gplx/xowa/html/portal/Xoa_portal_mgr.java new file mode 100644 index 000000000..951ba512c --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/portal/Xoa_portal_mgr.java @@ -0,0 +1,26 @@ +/* +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.xowa.html.portal; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xoa_portal_mgr implements GfoInvkAble { + public Xoa_portal_mgr(Xoa_app app) {wikis = new Xoa_available_wikis_mgr(app);} + public Xoa_available_wikis_mgr Wikis() {return wikis;} private Xoa_available_wikis_mgr wikis; + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_wikis)) return wikis; + else return GfoInvkAble_.Rv_unhandled; + } private static final String Invk_wikis = "wikis"; +} diff --git a/400_xowa/src/gplx/xowa/html/portal/Xoh_rtl_utl.java b/400_xowa/src/gplx/xowa/html/portal/Xoh_rtl_utl.java new file mode 100644 index 000000000..ddfee777b --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/portal/Xoh_rtl_utl.java @@ -0,0 +1,73 @@ +/* +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.xowa.html.portal; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xoh_rtl_utl { + private static final int[] tmp_ary = new int[32]; // support no more than 16 items + private static Bry_bfr bfr = Bry_bfr.reset_(32); + public static byte[] Reverse_li(byte[] src) { + int src_len = src.length; + int pos = 0; + while (true) { + int ul_bgn = Bry_finder.Find_fwd(src, Ul_bgn, pos, src_len); + if (ul_bgn == Bry_finder.Not_found) break; // no more
      + bfr.Add_mid(src, pos, ul_bgn); // add pos ->
        + tmp_ary[tmp_idx++] = li_bgn; + tmp_ary[tmp_idx++] = li_end + Li_end.length; + pos = li_end + Li_end.length; + } + int ul_bgn_rhs = Bry_finder.Find_fwd(src, Byte_ascii.Gt, ul_bgn); + if (tmp_idx < 3 // 0 or 1 li; add everything between ul + || ul_bgn_rhs == Bry_finder.Not_found + ) { + bfr.Add_mid(src, ul_bgn, ul_end); + return; + } + int li_n_end = tmp_ary[tmp_idx - 1]; + ++ul_bgn_rhs; + bfr.Add_mid(src, ul_bgn, ul_bgn_rhs); // add from " to 1st ">" + for (int i = tmp_idx - 1; i > -1; i -= 2) { + int li_end = tmp_ary[i]; + int prv_pos = i < 2 ? ul_bgn_rhs : tmp_ary[i - 2]; + bfr.Add_mid(src, prv_pos, li_end); // add from " to "" + } + bfr.Add_mid(src, li_n_end, ul_end); // add from nth "" -> "
      " + } + private static final byte[] + Ul_bgn = Bry_.new_utf8_("") + , Li_bgn = Bry_.new_utf8_("") + ; +} diff --git a/400_xowa/src/gplx/xowa/html/portal/Xoh_rtl_utl_tst.java b/400_xowa/src/gplx/xowa/html/portal/Xoh_rtl_utl_tst.java new file mode 100644 index 000000000..bf0be20e7 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/portal/Xoh_rtl_utl_tst.java @@ -0,0 +1,64 @@ +/* +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.xowa.html.portal; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import org.junit.*; +public class Xoh_rtl_utl_tst { + @Before public void init() {fxt.Init();} private Xoh_rtl_utl_fxt fxt = new Xoh_rtl_utl_fxt(); + @Test public void Basic() { + fxt.Test_reverse_li("
      • a
      • b
      ", "
      • b
      • a
      "); + } + @Test public void Zero() { + fxt.Test_reverse_li("a", "a"); + } + @Test public void One() { + fxt.Test_reverse_li("
      • a
      ", "
      • a
      "); + } + @Test public void Example() { + fxt.Test_reverse_li(String_.Concat_lines_nl_skip_last + ( "
      " + , "
        " + , "
      • a" + , "
      • " + , "
      • b" + , "
      • " + , "
      • c" + , "
      • " + , "
      " + , "
      " + ), String_.Concat_lines_nl_skip_last + ( "
      " + , "
        " + , "
      • c" + , "
      • " + , "
      • b" + , "
      • " + , "
      • a" + , "
      • " + , "
      " + , "
      " + )); + } +} +class Xoh_rtl_utl_fxt { + public void Init() { + } + public void Test_reverse_li(String raw, String expd) { + byte[] actl = Xoh_rtl_utl.Reverse_li(Bry_.new_utf8_(raw)); + Tfds.Eq_str_lines(expd, String_.new_utf8_(actl)); + } +} diff --git a/400_xowa/src/gplx/xowa/html/portal/Xow_portal_mgr.java b/400_xowa/src/gplx/xowa/html/portal/Xow_portal_mgr.java new file mode 100644 index 000000000..f29edfb2f --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/portal/Xow_portal_mgr.java @@ -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 . +*/ +package gplx.xowa.html.portal; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.xowa.wikis.*; import gplx.xowa.gui.*; import gplx.xowa.html.sidebar.*; +public class Xow_portal_mgr implements GfoInvkAble { + public Xow_portal_mgr(Xow_wiki wiki) { + this.wiki = wiki; + this.sidebar_mgr = new Xowh_sidebar_mgr(wiki); + this.missing_ns_cls = Bry_.Eq(wiki.Domain_bry(), Xow_wiki_domain_.Key_home_bry) ? Missing_ns_cls_hide : null; // if home wiki, set missing_ns to application default; if any other wiki, set to null; will be overriden during init + } private Xow_wiki wiki; private boolean lang_is_rtl; + public void Init_by_lang(Xol_lang lang) { + lang_is_rtl = !lang.Dir_ltr(); + } + public Xowh_sidebar_mgr Sidebar_mgr() {return sidebar_mgr;} private Xowh_sidebar_mgr sidebar_mgr; + public Bry_fmtr Div_home_fmtr() {return div_home_fmtr;} Bry_fmtr div_home_fmtr = Bry_fmtr.new_(""); + public Xow_portal_mgr Init_assert() {if (init_needed) Init(); return this;} + public void Init() { + init_needed = false; + if (missing_ns_cls == null) // if missing_ns_cls not set for wiki, use the home wiki's + Missing_ns_cls_(wiki.App().User().Wiki().Html_mgr().Portal_mgr().Missing_ns_cls()); + Bry_fmtr_eval_mgr eval_mgr = wiki.Eval_mgr(); + Bry_bfr tmp_bfr = wiki.Utl_bry_bfr_mkr().Get_b512(); + Init_fmtr(tmp_bfr, eval_mgr, div_view_fmtr); + Init_fmtr(tmp_bfr, eval_mgr, div_ns_fmtr); + byte[] wiki_user_name = wiki.User().Name(); + div_personal_bry = Init_fmtr(tmp_bfr, eval_mgr, div_personal_fmtr, Bry_.Add(Xoh_href_parser.Href_wiki_bry, wiki.Ns_mgr().Ids_get_or_null(Xow_ns_.Id_user).Name_db_w_colon(), wiki_user_name), wiki_user_name, Ns_cls_by_id(wiki.Ns_mgr(), Xow_ns_.Id_user), Bry_.Add(Xoh_href_parser.Href_wiki_bry, wiki.Ns_mgr().Ids_get_or_null(Xow_ns_.Id_user_talk).Name_db_w_colon(), wiki_user_name), Ns_cls_by_id(wiki.Ns_mgr(), Xow_ns_.Id_user_talk)); + byte[] main_page_href_bry = tmp_bfr.Add(Xoh_href_parser.Href_site_bry).Add(wiki.Domain_bry()).Add(Xoh_href_parser.Href_wiki_bry).XtoAryAndClear(); // NOTE: build /site/en.wikipedia.org/wiki/ href; no Main_Page, as that will be inserted by Xoh_href_parser + div_logo_bry = Init_fmtr(tmp_bfr, eval_mgr, div_logo_fmtr, main_page_href_bry, wiki.App().Url_converter_fsys().Encode_http(wiki.App().User().Fsys_mgr().Wiki_root_dir().GenSubFil_nest(wiki.Domain_str(), "html", "logo.png"))); + div_home_bry = Init_fmtr(tmp_bfr, eval_mgr, div_home_fmtr); + div_wikis_fmtr.Eval_mgr_(eval_mgr); + tmp_bfr.Mkr_rls(); + sidebar_mgr.Init(); + } private boolean init_needed = true; + private byte[] Init_fmtr(Bry_bfr tmp_bfr, Bry_fmtr_eval_mgr eval_mgr, Bry_fmtr fmtr, Object... fmt_args) { + fmtr.Eval_mgr_(eval_mgr); + fmtr.Bld_bfr_many(tmp_bfr, fmt_args); + byte[] rv = tmp_bfr.XtoAryAndClear(); + // fmtr.Fmt_(rv); + return rv; + } + public byte[] Div_personal_bry() {return div_personal_bry;} private byte[] div_personal_bry = Bry_.Empty; + public byte[] Div_ns_bry(Bry_bfr_mkr bfr_mkr, Xoa_ttl ttl, Xow_ns_mgr ns_mgr) { + Xow_ns ns = ttl.Ns(); + byte[] subj_cls = Ns_cls_by_ord(ns_mgr, ns.Ord_subj_id()), talk_cls = Ns_cls_by_ord(ns_mgr, ns.Ord_talk_id()); + if (ns.Id_talk()) + talk_cls = Xow_portal_mgr.Cls_selected_y; + else + subj_cls = Xow_portal_mgr.Cls_selected_y; + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + div_ns_fmtr.Bld_bfr_many(tmp_bfr, Bry_.Add(Xoh_href_parser.Href_wiki_bry, ttl.Subj_txt()), subj_cls, Bry_.Add(Xoh_href_parser.Href_wiki_bry, ttl.Talk_txt()), talk_cls); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + private byte[] Ns_cls_by_ord(Xow_ns_mgr ns_mgr, int ns_ord) { + Xow_ns ns = ns_mgr.Ords_get_at(ns_ord); + return ns == null || ns.Exists() ? Bry_.Empty : missing_ns_cls; + } + private byte[] Ns_cls_by_id(Xow_ns_mgr ns_mgr, int ns_id) { + Xow_ns ns = ns_mgr.Ids_get_or_null(ns_id); + return ns == null || ns.Exists() ? Bry_.Empty : missing_ns_cls; + } + public byte[] Div_view_bry(Bry_bfr_mkr bfr_mkr, byte output_tid, byte[] search_text) { + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + byte[] read_cls = Bry_.Empty, edit_cls = Bry_.Empty, html_cls = Bry_.Empty; + switch (output_tid) { + case Xog_page_mode.Tid_read: read_cls = Cls_selected_y; break; + case Xog_page_mode.Tid_edit: edit_cls = Cls_selected_y; break; + case Xog_page_mode.Tid_html: html_cls = Cls_selected_y; break; + } + div_view_fmtr.Bld_bfr_many(tmp_bfr, read_cls, edit_cls, html_cls, search_text); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } public static final byte[] Cls_selected_y = Bry_.new_ascii_("selected"), Cls_new = Bry_.new_ascii_("new"), Cls_display_none = Bry_.new_ascii_("xowa_display_none"); + public byte[] Div_logo_bry() {return div_logo_bry;} private byte[] div_logo_bry = Bry_.Empty; + public byte[] Div_home_bry() {return div_home_bry;} private byte[] div_home_bry = Bry_.Empty; + public byte[] Div_wikis_bry(Bry_bfr_mkr bfr_mkr) { + Bry_bfr tmp_bfr = bfr_mkr.Get_k004(); + div_wikis_fmtr.Bld_bfr_many(tmp_bfr); + return tmp_bfr.Mkr_rls().XtoAryAndClear(); + } + public byte[] Missing_ns_cls() {return missing_ns_cls;} public Xow_portal_mgr Missing_ns_cls_(byte[] v) {missing_ns_cls = v; return this;} private byte[] missing_ns_cls; // NOTE: must be null due to Init check above + private Bry_fmtr div_personal_fmtr = Bry_fmtr.new_("~{portal_personal_subj_href};~{portal_personal_subj_text};~{portal_personal_talk_cls};~{portal_personal_talk_href};~{portal_personal_talk_cls};", "portal_personal_subj_href", "portal_personal_subj_text", "portal_personal_subj_cls", "portal_personal_talk_href", "portal_personal_talk_cls"); + private Bry_fmtr div_ns_fmtr = Bry_fmtr.new_("~{portal_ns_subj_href};~{portal_ns_subj_cls};~{portal_ns_talk_href};~{portal_ns_talk_cls}", "portal_ns_subj_href", "portal_ns_subj_cls", "portal_ns_talk_href", "portal_ns_talk_cls"); + private Bry_fmtr div_view_fmtr = Bry_fmtr.new_("", "portal_view_read_cls", "portal_view_edit_cls", "portal_view_html_cls", "search_text"); + private Bry_fmtr div_logo_fmtr = Bry_fmtr.new_("", "portal_nav_main_href", "portal_logo_url"); + private Bry_fmtr div_wikis_fmtr = Bry_fmtr.new_(""); + private byte[] Reverse_li(byte[] bry) { + return lang_is_rtl ? Xoh_rtl_utl.Reverse_li(bry) : bry; + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_div_personal_)) div_personal_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_div_ns_)) div_ns_fmtr.Fmt_(Reverse_li(m.ReadBry("v"))); + else if (ctx.Match(k, Invk_div_view_)) div_view_fmtr.Fmt_(Reverse_li(m.ReadBry("v"))); + else if (ctx.Match(k, Invk_div_logo_)) div_logo_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_div_home_)) div_home_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_div_wikis_)) div_wikis_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_missing_ns_cls)) return String_.new_utf8_(missing_ns_cls); + else if (ctx.Match(k, Invk_missing_ns_cls_)) missing_ns_cls = m.ReadBry("v"); + else if (ctx.Match(k, Invk_missing_ns_cls_list)) return Options_missing_ns_cls_list; + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String Invk_div_personal_ = "div_personal_", Invk_div_view_ = "div_view_", Invk_div_ns_ = "div_ns_", Invk_div_home_ = "div_home_", Invk_div_wikis_ = "div_wikis_" + , Invk_missing_ns_cls = "missing_ns_cls", Invk_missing_ns_cls_ = "missing_ns_cls_", Invk_missing_ns_cls_list = "missing_ns_cls_list" + ; + public static final String Invk_div_logo_ = "div_logo_"; + private static KeyVal[] Options_missing_ns_cls_list = KeyVal_.Ary(KeyVal_.new_("", "Show as blue link"), KeyVal_.new_("new", "Show as red link"), KeyVal_.new_("xowa_display_none", "Hide")); + private static final byte[] Missing_ns_cls_hide = Bry_.new_ascii_("xowa_display_none"); +} \ No newline at end of file diff --git a/400_xowa/src/gplx/xowa/html/portal/Xow_portal_mgr_tst.java b/400_xowa/src/gplx/xowa/html/portal/Xow_portal_mgr_tst.java new file mode 100644 index 000000000..04f708ed7 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/portal/Xow_portal_mgr_tst.java @@ -0,0 +1,51 @@ +/* +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.xowa.html.portal; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import org.junit.*; +public class Xow_portal_mgr_tst { + @Before public void init() {fxt.Init();} private Xowh_portal_mgr_fxt fxt = new Xowh_portal_mgr_fxt(); + @Test public void Div_ns_bry() { + fxt.Test_div_ns_bry("A" , "/wiki/A;selected;/wiki/Talk:A;xowa_display_none"); + fxt.Test_div_ns_bry("Talk:A" , "/wiki/A;;/wiki/Talk:A;selected"); + } + @Test public void Div_personal_bry() { + fxt.Test_div_personal_bry("/wiki/User:anonymous;anonymous;xowa_display_none;/wiki/User_talk:anonymous;xowa_display_none;"); + } + @Test public void Missing_ns_cls() { + fxt.Test_missing_ns_cls("xowa_display_none"); + } +} +class Xowh_portal_mgr_fxt { + public void Init() { + if (app == null) { + app = Xoa_app_fxt.app_(); + wiki = Xoa_app_fxt.wiki_tst_(app); + wiki.Ns_mgr().Ns_main().Exists_(true); // needed for ns + wiki.Html_mgr().Portal_mgr().Init_assert(); // needed for personal + } + } private Xoa_app app; Xow_wiki wiki; + public void Test_div_ns_bry(String ttl, String expd) { + Tfds.Eq(expd, String_.new_ascii_(wiki.Html_mgr().Portal_mgr().Div_ns_bry(wiki.Utl_bry_bfr_mkr(), Xoa_ttl.parse_(wiki, Bry_.new_ascii_(ttl)), wiki.Ns_mgr()))); + } + public void Test_div_personal_bry(String expd) { + Tfds.Eq(expd, String_.new_ascii_(wiki.Html_mgr().Portal_mgr().Div_personal_bry())); + } + public void Test_missing_ns_cls(String expd) { + Tfds.Eq(expd, String_.new_ascii_(wiki.Html_mgr().Portal_mgr().Missing_ns_cls())); + } +} diff --git a/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_itm.java b/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_itm.java new file mode 100644 index 000000000..3f39a4c11 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_itm.java @@ -0,0 +1,50 @@ +/* +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.xowa.html.sidebar; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xowh_sidebar_itm { + public Xowh_sidebar_itm(byte tid) {this.tid = tid;} + public byte Tid() {return tid;} private byte tid; + public byte[] Id() {return id;} public Xowh_sidebar_itm Id_(byte[] v) {id = v; return this;} private byte[] id; + public byte[] Href() {return href;} public Xowh_sidebar_itm Href_(byte[] v) {href = v; return this;} private byte[] href; + public byte[] Title() {return title;} public Xowh_sidebar_itm Title_(byte[] v) {title = v; return this;} private byte[] title = Bry_.Empty; + public byte[] Accesskey() {return accesskey;} public Xowh_sidebar_itm Accesskey_(byte[] v) {accesskey = v; return this;} private byte[] accesskey; + public byte[] Atr_accesskey_and_title() {return atr_accesskey_and_title;} public Xowh_sidebar_itm Atr_accesskey_and_title_(byte[] v) {atr_accesskey_and_title = v; return this;} private byte[] atr_accesskey_and_title = Bry_.Empty; + public byte[] Text() {return text;} public Xowh_sidebar_itm Text_(byte[] v) {text = v; return this;} private byte[] text; + public int Itms_len() {return itms.Count();} ListAdp itms = ListAdp_.new_(); + public Xowh_sidebar_itm Itms_get_at(int i) {return (Xowh_sidebar_itm)itms.FetchAt(i);} + public Xowh_sidebar_itm Itms_add(Xowh_sidebar_itm... ary) { + int ary_len = ary.length; + for (int i = 0; i < ary_len; i++) + itms.Add(ary[i]); + return this; + } + public static final byte Tid_grp = 1, Tid_itm = 2; // NOTE: values are used by parse to indicate # of asterisks +} +class Xowh_sidebar_grp_fmtr_arg implements Bry_fmtr_arg { + private Xow_wiki wiki; private Xowh_sidebar_itm grp; private Bry_fmtr fmtr; + public void Grp_(Xow_wiki wiki, Xowh_sidebar_itm grp, Bry_fmtr fmtr) {this.wiki = wiki; this.grp = grp; this.fmtr = fmtr;} + public void XferAry(Bry_bfr bfr, int idx) { + int len = grp.Itms_len(); + boolean popups_enabled = wiki.App().Api_root().Html().Modules().Popups().Enabled(); + String itm_cls = popups_enabled ? " class='xowa-hover-off'" : ""; + for (int i = 0; i < len; i++) { + Xowh_sidebar_itm itm = (Xowh_sidebar_itm )grp.Itms_get_at(i); + fmtr.Bld_bfr_many(bfr, itm.Id(), itm.Href(), itm_cls, itm.Atr_accesskey_and_title(), itm.Text()); + } + } +} diff --git a/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_mgr.java b/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_mgr.java new file mode 100644 index 000000000..412e6f1fe --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_mgr.java @@ -0,0 +1,128 @@ +/* +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.xowa.html.sidebar; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xowh_sidebar_mgr implements GfoInvkAble { + public Xowh_sidebar_mgr(Xow_wiki wiki) {this.wiki = wiki;} private Xow_wiki wiki; + public int Grps_len() {return grps.Count();} ListAdp grps = ListAdp_.new_(); + public Xowh_sidebar_itm Grps_get_at(int i) {return (Xowh_sidebar_itm)grps.FetchAt(i);} + public byte[] Html_bry() {return html_bry;} private byte[] html_bry; + public void Init() { + try { + Bry_bfr bfr = wiki.Utl_bry_bfr_mkr().Get_b512(); + Xol_msg_itm sidebar_msg = Pf_msg_mgr.Get_msg_itm(bfr, wiki, wiki.Lang(), CONST_sidebar_ttl); + if ( sidebar_msg.Src() == Xol_msg_itm.Src_missing + || ( sidebar_msg.Src() == Xol_msg_itm.Src_lang + && wiki.Domain_tid() == gplx.xowa.wikis.Xow_wiki_domain_.Tid_home + )) { + html_bry = Bry_.Empty; + bfr.Mkr_rls(); + return; + } + Bry_bfr comment_bfr = wiki.Utl_bry_bfr_mkr().Get_b512(); + Parse(bfr, comment_bfr, sidebar_msg.Val()); + Bld_html(bfr); + html_bry = bfr.Mkr_rls().XtoAryAndClear(); + comment_bfr = comment_bfr.Mkr_rls().Clear(); + } catch (Exception e) { + wiki.App().Usr_dlg().Warn_many(GRP_KEY, "sidebar.init", "sidebar init failed: ~{0} ~{1}", wiki.Domain_str(), Err_.Message_gplx_brief(e)); + html_bry = Bry_.Empty; + } + } + private static boolean Ignore(byte[] wiki, byte[] item) { + return + ( Bry_.Eq(wiki, Ignore_wiki_ess) // occurs in 2014-02-03 dump; ignored by MW + && Bry_.Eq(item, Ignore_item_ess_random) + ); + } private static byte[] Ignore_wiki_ess = Bry_.new_ascii_("es.wikisource.org"), Ignore_item_ess_random = Bry_.new_utf8_("special:Random/Página djvu"); + public void Parse(Bry_bfr bfr, Bry_bfr comment_bfr, byte[] src) { + byte[][] lines = Bry_.Split(src, Byte_ascii.NewLine); + int lines_len = lines.length; + Xoa_app app = wiki.App(); Url_encoder id_encoder = app.Url_converter_id(); + Xowh_sidebar_itm cur_grp = null; + Xop_link_parser link_parser = new Xop_link_parser(); + for (int i = 0; i < lines_len; i++) { + byte[] line = lines[i]; int line_len = line.length; + if (line_len == 0) continue; // skip blank lines + if (line[0] != Byte_ascii.Asterisk) continue; // skip non-list items; must begin with "*" + byte tid = line[1] == Byte_ascii.Asterisk ? Xowh_sidebar_itm.Tid_itm : Xowh_sidebar_itm.Tid_grp; + byte[] bry = Bry_.Trim(line, tid, line_len); // trim *, **; note that tid indicates # of asterisks + bry = gplx.html.Html_utl.Del_comments(comment_bfr, bry); // strip comments; DATE:2014-03-08 + if (Bry_.Match(bry, CONST_itm_search) || Bry_.Match(bry, CONST_itm_toolbox) || Bry_.Match(bry, CONST_itm_languages)) continue; // ignore SEARCH, TOOLBOX, LANGUAGES + int pipe_pos = Bry_finder.Find_fwd(bry, Byte_ascii.Pipe); + byte[] text_key = tid == Xowh_sidebar_itm.Tid_grp ? bry : Bry_.Mid(bry, pipe_pos + 1, bry.length); // get text_key; note that grp is entire bry, while itm is after | + byte[] text_val = Resolve_key(text_key); + byte[] id = id_encoder.Encode(bfr.Add(CONST_id_prefix), text_key).XtoAryAndClear(); // build id; "n-encoded_id" + Xowh_sidebar_itm cur_itm = new Xowh_sidebar_itm(tid).Id_(id).Text_(text_val); + wiki.Msg_mgr().Val_html_accesskey_and_title(id, bfr, cur_itm); + if (tid == Xowh_sidebar_itm.Tid_grp) { + cur_grp = cur_itm; + grps.Add(cur_grp); + } + else { + if (pipe_pos == Bry_.NotFound) { // not of format of "href|main"; (EX: "href_only") + if (!Ignore(wiki.Domain_bry(), bry)) // suppress warning if ignored; DATE:2014-02-11 + wiki.App().Usr_dlg().Warn_many(GRP_KEY, "parse.line.missing_text", "sidebar item is missing pipe; only href is available; item will be hidden: item=~{0}", String_.new_utf8_(bry)); + continue; + } + byte[] href_key = Bry_.Mid(bry, 0, pipe_pos); + byte[] href_val = Resolve_key(href_key); + href_val = link_parser.Parse(bfr, tmp_url, wiki, href_val, Bry_.Empty); + cur_itm.Href_(href_val); + if (cur_grp == null) // handle null_ref; should only occur for tests + grps.Add(cur_itm); + else + cur_grp.Itms_add(cur_itm); + } + } + } private Xoa_url tmp_url = new Xoa_url(); + public void Bld_html(Bry_bfr bfr) { + int len = grps.Count(); + for (int i = 0; i < len; i++) { + Xowh_sidebar_itm grp = (Xowh_sidebar_itm)grps.FetchAt(i); + html_grp_fmtr_arg.Grp_(wiki, grp, html_itm_fmtr); + html_grp_fmtr.Bld_bfr_many(bfr, grp.Id(), grp.Text(), html_grp_fmtr_arg); + } + } private Xowh_sidebar_grp_fmtr_arg html_grp_fmtr_arg = new Xowh_sidebar_grp_fmtr_arg(); + private Bry_fmtr html_grp_fmtr = Bry_fmtr.new_(String_.Concat_lines_nl + ( "
      " + , "

      ~{grp_text}

      " + , "
      " + , "
        ~{itms}" + , "
      " + , "
      " + , "
      ") + , "grp_id", "grp_text", "itms"); + private Bry_fmtr html_itm_fmtr = Bry_fmtr.new_(String_.Concat_lines_nl_skip_last + ( "" + , "
    • ~{itm_text}
    • " + ), "itm_id", "itm_href", "itm_cls", "itm_accesskey_and_title", "itm_text"); + private static final byte[] CONST_itm_search = Bry_.new_ascii_("SEARCH"), CONST_itm_toolbox = Bry_.new_ascii_("TOOLBOX"), CONST_itm_languages = Bry_.new_ascii_("LANGUAGES"), CONST_id_prefix = Bry_.new_ascii_("n-"); + private static final String GRP_KEY = "xowa.wiki.gui.skin.mgr"; + private byte[] Resolve_key(byte[] key) { + byte[] val = wiki.Msg_mgr().Val_by_key_obj(key); + if (Bry_.Len_eq_0(val)) val = key; // if key is not found, default to val + return wiki.Parser().Parse_text_to_wtxt(val); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_html_grp_fmt_)) html_grp_fmtr.Fmt_(m.ReadBry("v")); + else if (ctx.Match(k, Invk_html_itm_fmt_)) html_itm_fmtr.Fmt_(m.ReadBry("v")); + else return GfoInvkAble_.Rv_unhandled; + return this; + } private static final String Invk_html_grp_fmt_ = "html_grp_fmt_", Invk_html_itm_fmt_ = "html_itm_fmt_"; + private static final byte[] CONST_sidebar_ttl = Bry_.new_ascii_("Sidebar"); +} diff --git a/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_mgr_tst.java b/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_mgr_tst.java new file mode 100644 index 000000000..279135a96 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/sidebar/Xowh_sidebar_mgr_tst.java @@ -0,0 +1,257 @@ +/* +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.xowa.html.sidebar; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import org.junit.*; +public class Xowh_sidebar_mgr_tst { + @Before public void init() {fxt.Clear();} private Xowh_sidebar_mgr_fxt fxt = new Xowh_sidebar_mgr_fxt(); + @Test public void Grp() { + fxt + .Init_msg_grp("key", "text", "title") + .Test_parse("* key" + , fxt.nav_grp_("text", "title")) + ; + } + @Test public void Grp_missing_msg() { + fxt + .Test_parse("* key" + , fxt.nav_grp_("key", Null_str)) + ; + } + @Test public void Itm() { + fxt + .Init_msg_itm("href_key", "main_key", "text", "title", "accesskey", "href") + .Test_parse + ( "** href_key|main_key" + , fxt.nav_itm_("text", "title", "accesskey", "/wiki/Href")) + ; + } + @Test public void Itm_missing_msg() { + fxt + .Test_parse + ( "** href_key|main_key" + , fxt.nav_itm_("main_key", Null_str, Null_str, "/wiki/Href_key") + ) + ; + } + @Test public void Itm_text() { // PURPOSE: only text msg exists; EX: ** Portal:Contents|contents; no href, accesskey, title + fxt + .Init_msg_itm("href_key", "main_key", "text", Null_str, Null_str, Null_str) // only define msg for text + .Test_parse + ( "** href_key|main_key" + , fxt.nav_itm_("text", Null_str, Null_str, "/wiki/Href_key")) + ; + } + @Test public void Itm_href_absolute() { + fxt + .Test_parse + ( "** http://a.org|main_key" + , fxt.nav_itm_("main_key", Null_str, Null_str, "http://a.org")) + ; + } + @Test public void Itm_href_manual() { + fxt + .Test_parse + ( "** Help:Contents|main_key" + , fxt.nav_itm_("main_key", Null_str, Null_str, "/wiki/Help:Contents")) + ; + } + @Test public void Itm_href_xwiki() { + Xop_fxt.Reg_xwiki_alias(fxt.Wiki(), "c", "commons.wikimedia.org"); + fxt + .Test_parse + ( "** c:Help:Contents|main_key" + , fxt.nav_itm_("main_key", Null_str, Null_str, "/site/commons.wikimedia.org/wiki/Help:Contents")) + ; + } + @Test public void Itm_err_missing_key() { + fxt + .Test_parse + ( "** no_main_key" + ) + ; + } + @Test public void Itm_ignore() { // PURPOSE: ignore SEARCH, TOOLBOX, LANGUAGES + fxt + .Test_parse(String_.Concat_lines_nl + ( "** SEARCH" + , "** TOOLBOX" + , "** LANGUAGES" + )); + } + @Test public void Itm_comment() { // PURPOSE: ignore comment; EX:de.v:MediaWiki:Sidebar; DATE:2014-03-08 + fxt + .Init_msg_itm("href_key", "main_key", "text", "title", "accesskey", "href") + .Test_parse + ( "** href_key|main_key" + , fxt.nav_itm_("text", "title", "accesskey", "/wiki/Href")) + ; + } + @Test public void Smoke() { + fxt + .Init_msg_grp("navigation", "Grp_0_text", "Grp_0_title") + .Init_msg_itm("mainpage", "mainpage-description", "Itm_0_text", "Itm_0_title [a]", "a", "Itm_0_href") + .Init_msg_itm("Portal:Contents", "contents", "Itm_1_text", Null_str, Null_str, Null_str) + .Test_parse(String_.Concat_lines_nl + ( "* navigation" + , "** mainpage|mainpage-description" + , "** Portal:Contents|contents" + , "* SEARCH" + , "* interaction" + , "** helppage|help" + , "* TOOLBOX" + , "* LANGUAGES" + ) + , fxt.nav_grp_("Grp_0_text", "Grp_0_title").Itms_add + ( fxt.nav_itm_("Itm_0_text", "Itm_0_title [a]", "a", "/wiki/Itm_0_href") + , fxt.nav_itm_("Itm_1_text", Null_str, Null_str, "/wiki/Portal:Contents") + ) + , fxt.nav_grp_("interaction", Null_str).Itms_add + ( fxt.nav_itm_("help", Null_str, Null_str, "/wiki/Helppage") + )); + fxt.Test_html(String_.Concat_lines_nl + ( "
      " + , "

      Grp_0_text

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

      interaction

      " + , "
      " + , "
        " + , "
      • help
      • " + , "
      " + , "
      " + , "
      " + )); + } private static final String Null_str = ""; + @Test public void Itm_template_msg() { + fxt + .Init_msg_itm("href", "main", null, null, null, "{{ns:Special}}:Random") + .Test_parse(String_.Concat_lines_nl + ( "** href|main" + ) + , fxt.nav_itm_("main", Null_str, Null_str, "/wiki/Special:Random") + ); + } + @Test public void Itm_template_key() { + fxt.Test_parse(String_.Concat_lines_nl + ( "** {{ns:Special}}:Random|main" + ) + , fxt.nav_itm_("main", Null_str, Null_str, "/wiki/Special:Random") + ); + } + @Test public void Popups() { + fxt.Init_popups_enabled_(true) + .Test_parse(String_.Concat_lines_nl + ( "* navigation" + , "** mainpage|mainpage-description" + ) + , fxt.nav_grp_("navigation", "").Itms_add + ( fxt.nav_itm_("mainpage-description", Null_str, Null_str, "/wiki/Mainpage") + ) + ); + fxt.Test_html(String_.Concat_lines_nl + ( "
      " + , "

      navigation

      " + , "
      " + , " " + , "
      " + , "
      " + )); + } +} +class Xowh_sidebar_mgr_fxt { + private Xoa_app app; private Xow_wiki wiki; private Xowh_sidebar_mgr sidebar_mgr; private Bry_bfr bfr, comment_bfr; + public Xowh_sidebar_mgr_fxt Clear() { +// if (app == null) { + app = Xoa_app_fxt.app_(); + wiki = Xoa_app_fxt.wiki_tst_(app); + sidebar_mgr = wiki.Html_mgr().Portal_mgr().Sidebar_mgr(); + bfr = Bry_bfr.reset_(Io_mgr.Len_kb); + comment_bfr = Bry_bfr.reset_(Io_mgr.Len_kb); +// } + return this; + } + public Xow_wiki Wiki() {return wiki;} + public Xowh_sidebar_itm nav_grp_(String text, String title, Xowh_sidebar_itm... itms) {return new Xowh_sidebar_itm(Xowh_sidebar_itm.Tid_grp).Text_(Bry_.new_ascii_(text)).Title_(Bry_.new_ascii_(title));} + public Xowh_sidebar_itm nav_itm_(String text, String title, String accesskey, String href) {return new Xowh_sidebar_itm(Xowh_sidebar_itm.Tid_itm).Text_(Bry_.new_ascii_(text)).Title_(Bry_.new_ascii_(title)).Accesskey_(Bry_.new_ascii_(accesskey)).Href_(Bry_.new_ascii_(href));} + public Xowh_sidebar_mgr_fxt Init_popups_enabled_(boolean v) {app.Api_root().Html().Modules().Popups().Enabled_(v); return this;} + public Xowh_sidebar_mgr_fxt Init_msg_grp(String key, String text, String title) { + Init_msg(key, text); + Init_msg("tooltip-n-" + key, title); + return this; + } + public Xowh_sidebar_mgr_fxt Init_msg_itm(String href_key, String main_key, String text, String title, String accesskey, String href) { + if (text != null) Init_msg(main_key, text); + if (href != null) Init_msg(href_key, href); + if (title != null) Init_msg("tooltip-n-" + main_key, title); + if (accesskey != null) Init_msg("accesskey-n-" + main_key, accesskey); + return this; + } + public Xowh_sidebar_mgr_fxt Init_msg(String key, String val) { + Xol_msg_mgr msg_mgr = wiki.Lang().Msg_mgr(); + Xol_msg_itm msg_itm = msg_mgr.Itm_by_key_or_new(Bry_.new_ascii_(key)); + msg_itm.Atrs_set(Bry_.new_ascii_(val), false, String_.Has(val, "{{")); + return this; + } + public void Test_parse(String raw, Xowh_sidebar_itm... expd) { + sidebar_mgr.Parse(bfr, comment_bfr, Bry_.new_ascii_(raw)); + Tfds.Eq_str_lines(Xto_str(expd), Xto_str_grps(sidebar_mgr)); + } + public void Test_html(String expd) { + sidebar_mgr.Bld_html(bfr); + Tfds.Eq_str_lines(expd, bfr.XtoStrAndClear()); + } + String Xto_str_grps(Xowh_sidebar_mgr mgr) { + int len = mgr.Grps_len(); + Xowh_sidebar_itm[] ary = new Xowh_sidebar_itm[len]; + for (int i = 0; i < len; i++) + ary[i] = mgr.Grps_get_at(i); + return Xto_str(ary); + } + + String Xto_str(Xowh_sidebar_itm[] ary) { + int ary_len = ary.length; + String_bldr sb = String_bldr_.new_(); + for (int i = 0; i < ary_len; i++) + sb.Add(Xto_str(ary[i])); + return sb.XtoStrAndClear(); + } + String Xto_str(Xowh_sidebar_itm cur) { + String_bldr sb = String_bldr_.new_(); + boolean tid_is_itm = cur.Tid() == Xowh_sidebar_itm.Tid_itm; + sb.Add(tid_is_itm ? "itm|" : "grp|"); + sb.Add(cur.Text()).Add("|"); + sb.Add(cur.Title()).Add("|"); + if (tid_is_itm) { + sb.Add(cur.Accesskey()).Add("|"); + sb.Add(cur.Href()).Add("|"); + } + sb.Add_char_nl(); + int itms_len = cur.Itms_len(); + for (int i = 0; i< itms_len; i++) + sb.Add(Xto_str(cur.Itms_get_at(i))); + return sb.XtoStrAndClear(); + } +} \ No newline at end of file diff --git a/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_mgr.java b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_mgr.java new file mode 100644 index 000000000..bddf9f71b --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_mgr.java @@ -0,0 +1,85 @@ +/* +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.xowa.html.tidy; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import gplx.html.*; +import gplx.xowa.apps.fsys.*; +public class Xoh_tidy_mgr implements GfoInvkAble { + private Xoh_tidy_wkr wkr = Xoh_tidy_wkr_.Wkr_null; // TEST: set default wkr to null + private Xoh_tidy_wkr_tidy wkr_tidy = new Xoh_tidy_wkr_tidy(); private Xoh_tidy_wkr_jtidy wkr_jtidy = new Xoh_tidy_wkr_jtidy(); + public void Init_by_app(Xoa_app app) { + wkr_tidy.Init_by_app(app); + wkr_jtidy.Init_by_app(app); + Xoa_fsys_eval cmd_eval = app.Url_cmd_eval(); + ProcessAdp.ini_(this, app.Gui_wtr(), wkr_tidy, cmd_eval, ProcessAdp.Run_mode_sync_timeout, 1 * 60, "~{<>bin_plat_dir<>}tidy" + Op_sys.Cur().Fsys_dir_spr_str() + "tidy", Xoh_tidy_wkr_tidy.Args_fmt, "source", "target"); + Wkr_tid_(Xoh_tidy_wkr_.Tid_jtidy); + } + public boolean Enabled() {return enabled;} private boolean enabled = true; + public void Enabled_toggle() {enabled = !enabled;} + public void Wkr_tid_(byte v) { + wkr = v == Xoh_tidy_wkr_.Tid_jtidy + ? (Xoh_tidy_wkr)wkr_jtidy + : (Xoh_tidy_wkr)wkr_tidy + ; + } + public void Run_tidy_html(Xoa_page page, Bry_bfr bfr) { + if (bfr.Len_eq_0()) return; // document is empty; do not exec b/c tidy will never generate files for 0 len files, and previous file will remain; DATE:2014-06-04 + Tidy_wrap(bfr); + wkr.Exec_tidy(page, bfr); + Tidy_unwrap(bfr); + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_enabled)) return Yn.X_to_str(enabled); + else if (ctx.Match(k, Invk_enabled_)) enabled = m.ReadYn("v"); + else if (ctx.Match(k, Invk_enabled_toggle)) enabled = !enabled; + else if (ctx.Match(k, Invk_engine_type)) return Xoh_tidy_wkr_.X_to_key(wkr.Tid()); + else if (ctx.Match(k, Invk_engine_type_)) Wkr_tid_(Xoh_tidy_wkr_.X_to_tid(m.ReadStr("v"))); + else if (ctx.Match(k, Invk_engine_type_list)) return Xoh_tidy_wkr_.Options__list; + else if (ctx.Match(k, Invk_lib)) return wkr_tidy; + else return GfoInvkAble_.Rv_unhandled; + return this; + } + private static final String + Invk_enabled = "enabled", Invk_enabled_ = "enabled_", Invk_enabled_toggle = "enabled_toggle", Invk_lib = "lib" + , Invk_engine_type = "engine_type", Invk_engine_type_ = "engine_type_", Invk_engine_type_list = "engine_type_list" + ; + public static void Tidy_wrap(Bry_bfr bfr) { + bfr.Insert_at(0, Wrap_bgn); + bfr.Add(Wrap_end); + } + public static boolean Tidy_unwrap(Bry_bfr bfr) { + byte[] bfr_bry = bfr.Bfr(); + int find = Bry_finder.Find_fwd(bfr_bry, Html_tags.Body_lhs); if (find == Bry_finder.Not_found) return false; + bfr.Delete_rng_to_bgn(find + Html_tags.Body_lhs.length); + find = Bry_finder.Find_bwd(bfr_bry, Html_tags.Body_rhs, bfr.Len()); if (find == Bry_finder.Not_found) return false; + bfr.Delete_rng_to_end(find); + return true; + } + private static final byte[] // MW:includes/parser/Tidy.php|getWrapped + Wrap_bgn = Bry_.new_ascii_ + ( "" + + "" + + "" + + "test" + + "" + + "" + ) + , Wrap_end = Bry_.new_ascii_ + ( "" + + "" + ); +} diff --git a/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_mgr_tst.java b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_mgr_tst.java new file mode 100644 index 000000000..957fb0b25 --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_mgr_tst.java @@ -0,0 +1,92 @@ +/* +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.xowa.html.tidy; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import org.junit.*; +public class Xoh_tidy_mgr_tst { + @Before public void init() {fxt.Clear();} private Xoh_tidy_mgr_fxt fxt = new Xoh_tidy_mgr_fxt(); + @Test public void Wrap() { + fxt.Test_wrap("a" + , "" + + "" + + "" + + "test" + + "" + + "a" + + "" + + "" + ); + } + @Test public void Unwrap_pass() { + fxt.Test_unwrap + ( "" + + "" + + "" + + "test" + + "" + + "a" + + "" + + "" + , Bool_.Y, "a" + ); + } + @Test public void Unwrap_fail_bgn() { + fxt.Test_unwrap + ( "" + + "" + + "" + + "test" + + "" + + "a" + + "" + + "" + , Bool_.N, "" + ); + } + @Test public void Unwrap_fail_end() { + fxt.Test_unwrap + ( "" + + "" + + "" + + "test" + + "" + + "a" + + "" + + "" + , Bool_.N, "" + ); + } +} +class Xoh_tidy_mgr_fxt { + private Bry_bfr bfr = Bry_bfr.reset_(255); + public void Clear() { + bfr.Clear(); + } + public void Test_wrap(String val, String expd) { + bfr.Add_str(val); + Xoh_tidy_mgr.Tidy_wrap(bfr); + Tfds.Eq(expd, bfr.XtoStrAndClear()); + } + public void Test_unwrap(String val, boolean expd_pass, String expd) { + bfr.Add_str(val); + boolean actl_pass = Xoh_tidy_mgr.Tidy_unwrap(bfr); + if (actl_pass != expd_pass) Tfds.Fail("expd={0} actl={1}", expd_pass, actl_pass); + else if (expd_pass) { + Tfds.Eq(expd, bfr.XtoStrAndClear()); + } + } +} diff --git a/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr.java b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr.java new file mode 100644 index 000000000..f3b4554ad --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr.java @@ -0,0 +1,22 @@ +/* +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.xowa.html.tidy; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public interface Xoh_tidy_wkr { + byte Tid(); + void Exec_tidy(Xoa_page page, Bry_bfr bfr); +} diff --git a/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr_.java b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr_.java new file mode 100644 index 000000000..528dc03ad --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr_.java @@ -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 . +*/ +package gplx.xowa.html.tidy; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +public class Xoh_tidy_wkr_ { + public static final byte Tid_null = 0, Tid_tidy = 1, Tid_jtidy = 2; + public static final String Key_null = "null", Key_tidy = "tidy", Key_jtidy = "jtidy"; + public static final Xoh_tidy_wkr Wkr_null = new Xoh_tidy_wkr_null(); + public static String X_to_key(byte v) { + switch (v) { + case Tid_null: return Key_null; + case Tid_tidy: return Key_tidy; + case Tid_jtidy: return Key_jtidy; + default: throw Err_.not_implemented_(); + } + } + public static byte X_to_tid(String s) { + if (String_.Eq(s, Key_tidy)) return Tid_tidy; + else if (String_.Eq(s, Key_jtidy)) return Tid_jtidy; + else if (String_.Eq(s, Key_null)) return Tid_null; + else throw Err_.not_implemented_(); + } + public static KeyVal[] Options__list = KeyVal_.Ary(KeyVal_.new_(Key_tidy), KeyVal_.new_(Key_jtidy)); +} +class Xoh_tidy_wkr_null implements Xoh_tidy_wkr { + public byte Tid() {return Xoh_tidy_wkr_.Tid_null;} + public void Exec_tidy(Xoa_page page, Bry_bfr bfr) {} +} diff --git a/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr_jtidy.java b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr_jtidy.java new file mode 100644 index 000000000..463149e5a --- /dev/null +++ b/400_xowa/src/gplx/xowa/html/tidy/Xoh_tidy_wkr_jtidy.java @@ -0,0 +1,77 @@ +/* +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.xowa.html.tidy; import gplx.*; import gplx.xowa.*; import gplx.xowa.html.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import org.w3c.tidy.Configuration; +import org.w3c.tidy.Tidy; +class Xoh_tidy_wkr_jtidy implements Xoh_tidy_wkr { + private Tidy tidy; + private ByteArrayOutputStream wtr; + public void tidy_init() { + long bgn = Env_.TickCount(); + wtr = new ByteArrayOutputStream(); + tidy = new Tidy(); // obtain a new Tidy instance + tidy.setInputEncoding("UTF-8"); // -utf8 + tidy.setOutputEncoding("UTF-8"); // -utf8 + tidy.setDocType("\"\""); // --doctype \"\"; set to empty else some wikis will show paragraph text with little vertical gap; PAGE:tr.b: + tidy.setForceOutput(true); // --force-output y + tidy.setQuiet(true); // --quiet y + tidy.setTidyMark(false); // --tidy-mark n + tidy.setWraplen(0); // --wrap 0 + tidy.setIndentContent(true); // --indent y; NOTE: true indents all content in edit box + tidy.setQuoteNbsp(true); // --quote-nbsp y + tidy.setLiteralAttribs(true); // --literal-attributes y + tidy.setWrapAttVals(false); // --wrap-attributes n + tidy.setFixUri(false); // --fix-url n + tidy.setFixBackslash(false); // --fix-backslash n + tidy.setEncloseBlockText(true); // --enclose-block-text y; NOTE: true creates extra

      ; very noticeable in sidebar + tidy.setNumEntities(false); // NOTE: true will convert all UTF-8 chars to &#val; which ruins readability + tidy.setTrimEmptyElements(true); // NOTE: tidy always trims (not even an option) + tidy.setShowWarnings(false); // NOTE: otherwise warnings printed to output window + tidy.setShowErrors(0); // NOTE: otherwise errors printed to output window; EX: Error:

      +// , " --show-body-only y" // prevent tidy from surrounding input with // removed; strips " + ), String_.Concat_lines_nl + ( "

      <style>"
      +		, "</style>"
      +		, "
      " + )); + } + @Test public void Nl_only() { // PURPOSE: wiki_pre with \n only was being dropped; PAGE:en.w:Preferred_number DATE:2014-06-24 + fxt.Test_html_full_str(String_.Concat_lines_nl_skip_last + ( " a" + , " " // was being dropped + , " b" + ), String_.Concat_lines_nl + ( "
      a"
      +		, ""	// make sure it's still there
      +		, "b"
      +		, "
      " + )); + } + @Test public void Nl_w_ws() { // PURPOSE: based on Nl_only; make sure that 1 or more spaces does not add extra \n; PAGE:en.w:Preferred_number DATE:2014-06-24 + fxt.Test_html_full_str(String_.Concat_lines_nl_skip_last + ( " a" + , " " // 2 spaces + , " b" + ), String_.Concat_lines_nl + ( "
      a"
      +		, " "	// 1 space
      +		, "b"
      +		, "
      " + )); + } + @Test public void Nl_many() { // PURPOSE: handle alternating \n\s; PAGE:en.w:Preferred_number DATE:2014-06-24 + fxt.Test_html_full_str(String_.Concat_lines_nl_skip_last + ( " a" + , " " + , " b" + , " " + , " c" + ), String_.Concat_lines_nl + ( "
      a"
      +		, ""
      +		, "b"
      +		, ""
      +		, "c"
      +		, "
      " + )); + } + @Test public void Source() { // PURPOSE: " " in pre has issues; PAGE:en.w:Comment_(computer_programming) DATE:2014-06-23 + fxt.Init_para_y_(); + fxt.Test_html_wiki_str(String_.Concat_lines_nl + ( " " + , " " + , " a" + , " " + , " " + ), String_.Concat_lines_nl + ( "

      " // this is wrong, but will be stripped by tidy + , "

      " + , "
      "
      +		, " a"
      +		, "
      " + , "" + , "


      " // also wrong, but leave for now + , "

      " + )); + } +} diff --git a/400_xowa/src_460_para/gplx/xowa/Xop_pre_lxr.java b/400_xowa/src_460_para/gplx/xowa/Xop_pre_lxr.java new file mode 100644 index 000000000..f0cae0fe6 --- /dev/null +++ b/400_xowa/src_460_para/gplx/xowa/Xop_pre_lxr.java @@ -0,0 +1,103 @@ +/* +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.xowa; import gplx.*; +class Xop_pre_lxr implements Xop_lxr { + public byte Lxr_tid() {return Xop_lxr_.Tid_pre;} + public void Init_by_wiki(Xow_wiki wiki, ByteTrieMgr_fast core_trie) {core_trie.Add(Hook_space, this);} // NOTE: do not treat \n\t as shorthand pre; EX:pl.w:Main_Page; DATE:2014-05-06 + public void Init_by_lang(Xol_lang lang, ByteTrieMgr_fast core_trie) {} + public int Make_tkn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) { + if (!ctx.Para().Enabled()) { // para disabled; "\n\s" should just be "\n\s"; NOTE: para disabled in + if (bgn_pos != Xop_parser_.Doc_bgn_bos) // don't add \n if BOS; EX: " a" should be " ", not "\n " + ctx.Subs_add(root, tkn_mkr.NewLine(bgn_pos, bgn_pos + 1, Xop_nl_tkn.Tid_char, 1)); + ctx.Subs_add(root, tkn_mkr.Space(root, cur_pos - 1, cur_pos)); + return cur_pos; + } + int txt_pos = Bry_finder.Find_fwd_while(src, cur_pos, src_len, Byte_ascii.Space); // NOTE: was Find_fwd_while_tab_or_space, which incorrectly converted tabs to spaces; PAGE:en.w:Cascading_Style_Sheets; DATE:2014-06-23 + if (txt_pos == src_len) return cur_pos; // "\n\s" at EOS; treat as \n only; EX: "a\n " -> ""; also bounds check + byte b = src[txt_pos]; + if (bgn_pos == Xop_parser_.Doc_bgn_bos) { // BOS; gobble up all \s\t; EX: "BOS\s\s\sa" -> "BOSa" + if (b == Byte_ascii.NewLine) { // next char is nl + cur_pos = txt_pos; // position at nl; NOTE: do not position after nl, else may break hdr, tblw, list, etc; EX: "\s\n{|" needs to preserve "\n" for tblw + ctx.Subs_add(root, tkn_mkr.Ignore(bgn_pos, cur_pos, Xop_ignore_tkn.Ignore_tid_pre_at_bos)); + return cur_pos; // ignore pre if blank line at bos; EX: "BOS\s\s\n" -> "BOS\n" + } + if (b == Byte_ascii.Lt) // next char is <; possible xnde; flag so that xnde can escape; DATE:2013-11-28; moved outside Doc_bgn_bos block above; PAGE:en.w:Comment_(computer_programming); DATE:2014-06-23 + ctx.Xnde().Pre_at_bos_(true); + } + switch (ctx.Cur_tkn_tid()) { // close tblw attrs; NOTE: after BOS (since no tblw at BOS) but before "\n !" check + case Xop_tkn_itm_.Tid_tblw_tb: case Xop_tkn_itm_.Tid_tblw_tr: case Xop_tkn_itm_.Tid_tblw_th: + Xop_tblw_wkr.Atrs_close(ctx, src, root); + break; + case Xop_tkn_itm_.Tid_list: + if (Close_list(ctx, root, src, src_len, bgn_pos, cur_pos, txt_pos)) { + // ctx.Para().Process_nl(ctx, root, src, bgn_pos, new_pos, true); // add blank line for truncated "\n\s"; DATE:2013-07-12; DELETE: DATE:2014-02-18; doesn't seem necessary; doesn't break tests; devised for www.mediawiki.org/wiki/MediaWiki which loads fine + return txt_pos; // must exit early; do not process pre + } + break; + } + switch (b) { // handle "\n !" which can be tbl + case Byte_ascii.Bang: + switch (ctx.Cur_tkn_tid()) { + case Xop_tkn_itm_.Tid_tblw_tb: + case Xop_tkn_itm_.Tid_tblw_tc: + case Xop_tkn_itm_.Tid_tblw_tr: + case Xop_tkn_itm_.Tid_tblw_th: + case Xop_tkn_itm_.Tid_tblw_td: + case Xop_tkn_itm_.Tid_tblw_te: + int new_cur_pos = txt_pos + 1; // +1 to skip Byte_ascii.Bang + Xop_tblw_lxr_ws.Make(ctx, tkn_mkr, root, src, src_len, bgn_pos, new_cur_pos, Xop_tblw_wkr.Tblw_type_th, true); + return new_cur_pos; + } + break; + } + return ctx.Para().Process_pre(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, txt_pos); + } + private static boolean Close_list(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, int txt_pos) {// SEE:NOTE_4; EX.en.w:SHA-2 + if (Bry_finder.Find_fwd(src, Xop_tkn_.Lnki_bgn, txt_pos, src_len) == txt_pos) { // look for "[[" + txt_pos += Xop_tkn_.Lnki_bgn.length; + if (Bry_finder.Find_fwd(src, ctx.Wiki().Ns_mgr().Ns_category().Name_db_w_colon(), txt_pos, src_len) == txt_pos) // look for "Category:" + return false; // "[[Category:" found; "\n\s[[Category:" should not close list; note that [[Category]] is invisible + } + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_list), true, bgn_pos, cur_pos); // "* a\n\sb" found; close *a + if ( txt_pos < src_len // bounds check + && src[txt_pos] == Byte_ascii.NewLine) { // NOTE: handle "*a\n\s\n" between lists; DATE:2013-07-12 + Xop_list_wkr_.Close_list_if_present(ctx, root, src, bgn_pos, cur_pos); // NOTE: above line only closes one list; should probably change to close all lists, but for now, close all lists only if "\n\s", not "\n"; DATE:2013-07-12 + return true; + } + return false; + } + public static final Xop_pre_lxr _ = new Xop_pre_lxr(); Xop_pre_lxr() {} + private static final byte[] + Hook_space = new byte[] {Byte_ascii.NewLine, Byte_ascii.Space} + ; +} +/* +NOTE_4: Close_list +PURPOSE: \n should ordinarily close list. However, if \n[[Category:A]], then don't close list since [[Category:A]] will trim preceding \n +REASON: occurs b/c MW does separate passes for list and Category while XO does one pass. + +EX: closes *a list +*a + +*b + +EX: does not close +*a +[[Category:A]] +*b +*/ \ No newline at end of file diff --git a/400_xowa/src_460_para/gplx/xowa/Xop_pre_tkn.java b/400_xowa/src_460_para/gplx/xowa/Xop_pre_tkn.java new file mode 100644 index 000000000..7a4b2e473 --- /dev/null +++ b/400_xowa/src_460_para/gplx/xowa/Xop_pre_tkn.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +public class Xop_pre_tkn extends Xop_tkn_itm_base { + public Xop_pre_tkn(int bgn, int end, byte pre_tid, Xop_tkn_itm pre_bgn_tkn) { + this.Tkn_ini_pos(false, bgn, end); + this.pre_tid = pre_tid; + } + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_pre;} + public byte Pre_tid() {return pre_tid;} private byte pre_tid = Pre_tid_null; + public static final byte Pre_tid_null = 0, Pre_tid_bgn = 1, Pre_tid_end = 2; +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_lxr.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_lxr.java new file mode 100644 index 000000000..15f7a6d21 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_lxr.java @@ -0,0 +1,26 @@ +/* +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.xowa; import gplx.*; +class Xop_list_lxr implements Xop_lxr {//20111222 + public byte Lxr_tid() {return Xop_lxr_.Tid_list;} + public void Init_by_wiki(Xow_wiki wiki, ByteTrieMgr_fast core_trie) {Add_ary(core_trie, this, Xop_list_tkn_.Hook_ul, Xop_list_tkn_.Hook_ol, Xop_list_tkn_.Hook_dt, Xop_list_tkn_.Hook_dd);} + public void Init_by_lang(Xol_lang lang, ByteTrieMgr_fast core_trie) {} + private void Add_ary(ByteTrieMgr_fast core_trie, Object val, byte[]... ary) {for (byte[] itm : ary) core_trie.Add(itm, val);} + public int Make_tkn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) {return ctx.List().MakeTkn_bgn(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos);} + public static final Xop_list_lxr _ = new Xop_list_lxr(); Xop_list_lxr() {} +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_tkn.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_tkn.java new file mode 100644 index 000000000..2e5fd13c2 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_tkn.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +public class Xop_list_tkn extends Xop_tkn_itm_base { + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_list;} + public int List_uid() {return list_uid;} public Xop_list_tkn List_uid_(int v) {list_uid = v; return this;} private int list_uid = -1; + public byte List_bgn() {return list_bgn;} private byte list_bgn; + public byte List_itmTyp() {return list_itmTyp;} public Xop_list_tkn List_itmTyp_(byte v) {list_itmTyp = v; return this;} private byte list_itmTyp = Xop_list_tkn_.List_itmTyp_null; + public int[] List_path() {return path;} public Xop_list_tkn List_path_(int... v) {path = v; return this;} private int[] path = Int_.Ary_empty; + public int List_path_idx() {return path[path.length - 1];} + public boolean List_sub_first() {return List_path_idx() == 0;} + public byte List_sub_last() {return list_sub_last;} public Xop_list_tkn List_sub_last_(byte v) {list_sub_last = v; return this;} private byte list_sub_last = Bool_.__byte; + public static Xop_list_tkn bgn_(int bgn, int end, byte list_itmTyp, int symLen) {return new Xop_list_tkn(bgn, end, Bool_.Y_byte, list_itmTyp);} + public static Xop_list_tkn end_(int pos, byte list_itmTyp) {return new Xop_list_tkn(pos, pos, Bool_.N_byte, list_itmTyp);} + public Xop_list_tkn(int bgn, int end, byte bgnEndType, byte list_itmTyp) {this.Tkn_ini_pos(false, bgn, end); this.list_bgn = bgnEndType; this.list_itmTyp = list_itmTyp;} + public static final Xop_list_tkn Null = new Xop_list_tkn(); Xop_list_tkn() {} +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_tkn_.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_tkn_.java new file mode 100644 index 000000000..05b3da753 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_tkn_.java @@ -0,0 +1,54 @@ +/* +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.xowa; import gplx.*; +public class Xop_list_tkn_ { + public static final byte[] + Hook_ul = new byte[] {Byte_ascii.NewLine, Byte_ascii.Asterisk}, Hook_ol = new byte[] {Byte_ascii.NewLine, Byte_ascii.Hash} + , Hook_dt = new byte[] {Byte_ascii.NewLine, Byte_ascii.Semic} , Hook_dd = new byte[] {Byte_ascii.NewLine, Byte_ascii.Colon}; + public static final byte List_itmTyp_null = 0, List_itmTyp_ul = Byte_ascii.Asterisk, List_itmTyp_ol = Byte_ascii.Hash, List_itmTyp_dt = Byte_ascii.Semic, List_itmTyp_dd = Byte_ascii.Colon; + public static final String Str_li = "li", Str_ol = "ol", Str_ul = "ul", Str_dl = "dl", Str_dt = "dt", Str_dd = "dd"; + public static final byte[] Byt_li = Bry_.new_ascii_(Str_li), Byt_ol = Bry_.new_ascii_(Str_ol), Byt_ul = Bry_.new_ascii_(Str_ul) + , Byt_dl = Bry_.new_ascii_(Str_dl), Byt_dt = Bry_.new_ascii_(Str_dt), Byt_dd = Bry_.new_ascii_(Str_dd); + public static byte[] XmlTag_lst(byte b) { + switch (b) { + case List_itmTyp_ul: return Byt_ul; + case List_itmTyp_ol: return Byt_ol; + case List_itmTyp_dt: + case List_itmTyp_dd: return Byt_dl; + default: throw Err_.unhandled(b); + } + } + public static byte[] XmlTag_itm(byte b) { + switch (b) { + case List_itmTyp_ul: + case List_itmTyp_ol: return Byt_li; + case List_itmTyp_dt: return Byt_dt; + case List_itmTyp_dd: return Byt_dd; + default: throw Err_.unhandled(b); + } + } + public static byte Char_lst(byte b) { + switch (b) { + case List_itmTyp_ul: return Byte_ascii.Asterisk; + case List_itmTyp_ol: return Byte_ascii.Hash; + case List_itmTyp_dt: return Byte_ascii.Semic; + case List_itmTyp_dd: return Byte_ascii.Colon; + default: throw Err_.unhandled(b); + } + } +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr.java new file mode 100644 index 000000000..5936dc581 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr.java @@ -0,0 +1,261 @@ +/* +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.xowa; import gplx.*; +public class Xop_list_wkr implements Xop_ctx_wkr { + private int listId = 0; byte[] curSymAry = new byte[Max_list_depth]; int curSymLen = 0; byte[] prvSymAry = Bry_.Empty; + private HierPosAryBldr posBldr = new HierPosAryBldr(Max_list_depth); + private boolean SymAry_fill_overflow; + public void Ctor_ctx(Xop_ctx ctx) {} + public void Page_bgn(Xop_ctx ctx, Xop_root_tkn root) {Reset(0);} + public void Page_end(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int src_len) {} + public boolean List_dirty() {return posBldr.Dirty();} + private void Dd_colon_hide(Xop_root_tkn root) { // : seen, but nl encountered; mark ":" as invisible (i.e.: consume for
      ; don't let it show as ":"); DATE:2013-11-07 + int subs_len = root.Subs_len(); + for (int i = subs_len - 1; i > -1; i--) { + Xop_tkn_itm colon_tkn = root.Subs_get(i); + if (colon_tkn.Tkn_tid() == Xop_tkn_itm_.Tid_colon) { + colon_tkn.Ignore_y_(); + break; + } + } + } + public boolean Dd_chk() {return dd_chk;} public Xop_list_wkr Dd_chk_(boolean v) {dd_chk = v; return this;} private boolean dd_chk; + public void AutoClose(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, Xop_tkn_itm tkn) { + if (dd_chk) Dd_colon_hide(root); + // NOTE: list_tkns can not be explicitly closed, so auto-close will happen for all items + MakeTkn_end(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, (Xop_list_tkn)tkn, Bool_.Y_byte); + Reset(listId + 1); + ctx.Para().Process_block__bgn_n__end_y(Xop_xnde_tag_.Tag_ul); + } + public int MakeTkn_bgn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) {// REF.MW: Parser|doBlockLevels + if (bgn_pos == Xop_parser_.Doc_bgn_bos) bgn_pos = 0; // do not allow -1 pos + if (dd_chk) Dd_colon_hide(root); + + // pop hdr if exists; EX: \n== a ==\n*b; \n* needs to close hdr + int acsPos = ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_hdr); + if (acsPos != -1) ctx.Stack_pop_til(root, src, acsPos, true, bgn_pos, cur_pos); + + // close apos + ctx.Apos().EndFrame(ctx, root, src, bgn_pos, false); + byte symByt = src[cur_pos - 1]; // -1 b/c symByt is byte before curByt; EX: \n*a; cur_pos is at a; want to get * + int prvSymLen = curSymLen; + cur_pos = SymAry_fill(src, cur_pos, src_len, symByt); + symByt = src[cur_pos - 1]; // NOTE: get symByt again b/c cur_pos may have changed; EX: "#*"; # may have triggered list, but last symByt should be * + if (SymAry_fill_overflow) return ctx.Lxr_make_txt_(cur_pos); + int trim_line_end = Trim_empty_item(src, src_len, cur_pos); + if (trim_line_end != Bry_.NotFound) { + curSymLen = prvSymLen; + ctx.Tkn_mkr().Ignore(bgn_pos, trim_line_end, Xop_ignore_tkn.Ignore_tid_empty_li); + return trim_line_end; + } + PrvItm_compare(); + ctx.Para().Process_block__bgn__nl_w_symbol(ctx, root, src, bgn_pos, cur_pos - 1, Xop_xnde_tag_.Tag_li); // -1 b/c cur_pos includes sym_byte; EX: \n*; pass li; should pass correct tag, but for purposes of para_wkr,
    • doesn't matter + if (prvSymMatch) { + PopTil(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, Bool_.N_byte); + posBldr.MoveNext(); + prvSymAry = Xop_list_wkr_.MakeSymAry(curSymAry, curSymLen); + Xop_list_tkn prvItm = tkn_mkr.List_bgn(bgn_pos, cur_pos, curSymAry[curSymLen - 1], curSymLen).List_path_(posBldr.XtoIntAry()).List_uid_(listId); + ctx.Subs_add_and_stack(root, prvItm); + ctx.Empty_ignored_y_(); + } + else { + for (int i = prvSymLen; i > commonSymLen; i--) { // close all discontinued itms: EX: ##\n#\n + PopTil(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, Bool_.Y_byte); + posBldr.MoveUp(); + } + if (commonSymLen == 0 && prvSymLen != 0) { // nothing in common; reset list + listId++; + posBldr.Init(); + } + if (curSymLen == commonSymLen) { // add another itm if continuing; EX: #\n#\n + PopTil(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, Bool_.N_byte); + if ((prvSymLen - curSymLen) > 0 // moving up many levels; do not open new list; just MoveNext; EX: #1\n###3\n##2 + && curSymLen != 1) { // do not moveNext if at level 1; this has to do with strange incrementing logic in posBldr at rootLvl + posBldr.MoveNext(); + } + else { + posBldr.MoveUp(); posBldr.MoveDown(); + } + prvSymAry = Xop_list_wkr_.MakeSymAry(curSymAry, curSymLen); + symByt = src[cur_pos - 1]; + Xop_list_tkn prvItm = tkn_mkr.List_bgn(bgn_pos, cur_pos, symByt, curSymLen).List_path_(posBldr.XtoIntAry()).List_uid_(listId); + ctx.Subs_add_and_stack(root, prvItm); + ctx.Empty_ignored_y_(); + } + for (int i = commonSymLen; i < curSymLen; i++) { // open new itms; EX: #\n##\n + posBldr.MoveDown(); + symByt = curSymAry[i]; + prvSymAry = Xop_list_wkr_.MakeSymAry(curSymAry, curSymLen); + Xop_list_tkn prvItm = tkn_mkr.List_bgn(bgn_pos, cur_pos, symByt, i + ListAdp_.Base1).List_path_(posBldr.XtoIntAry()).List_uid_(listId); + ctx.Subs_add_and_stack(root, prvItm); + ctx.Empty_ignored_y_(); + } + } + if (allDd && cur_pos < src_len - 2 && src[cur_pos] == '{' && src[cur_pos + 1] == '|') // NOTE: if indent && next == {| then invoke table; EX: ":::{|" + return ctx.Tblw().Make_tkn_bgn(ctx, tkn_mkr, root, src, src_len, cur_pos, cur_pos + 2, false, Xop_tblw_wkr.Tblw_type_tb, Xop_tblw_wkr.Called_from_list, -1, -1); // NOTE: ws_enabled must be set to true; see test for Adinkras; Cato the Elder + else { + dd_chk = symByt == Xop_list_tkn_.List_itmTyp_dt; + return cur_pos; + } + } + public void MakeTkn_end(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, Xop_list_tkn bgn, byte sub_last) { + // boolean empty_ignored = ctx.Empty_ignored(); // commented; see below; DATE:2014-06-24 + Xop_tkn_itm end_tkn = tkn_mkr.List_end(bgn_pos, bgn.List_itmTyp()).List_path_(bgn.List_path()).List_uid_(listId).List_sub_last_(sub_last); + ctx.Subs_add(root, end_tkn); + // if (empty_ignored) ctx.Empty_ignore(root, bgn.Tkn_sub_idx()); // commented; code was incorrectly deactivating "*a" when "
    • " encountered; PAGE:en.w:Bristol_Bullfinch DATE:2014-06-24 + ctx.Para().Process_block__bgn_n__end_y(Xop_xnde_tag_.Tag_ul); + } + private int Trim_empty_item(byte[] src, int src_len, int pos) { + while (pos < src_len) { + byte b = src[pos]; + switch (b) { + case Byte_ascii.Tab: case Byte_ascii.CarriageReturn: case Byte_ascii.Space: + ++pos; + break; + case Byte_ascii.NewLine: + return pos; + default: + return Bry_.NotFound; + } + } + return Bry_.NotFound; + } + private Xop_list_tkn PopTil(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, byte subLast) { + int acs_pos = ctx.Stack_idx_find_but_stop_at_tbl(Xop_tkn_itm_.Tid_list); + if (acs_pos == -1) return null; + Xop_list_tkn rv = (Xop_list_tkn)ctx.Stack_pop_til(root, src, acs_pos, false, bgn_pos, cur_pos); + MakeTkn_end(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, rv, subLast); + return rv; + } + private void PrvItm_compare() { + int prvSymLen = prvSymAry.length; + prvSymMatch = curSymLen == prvSymLen; commonSymLen = 0; + for (int i = 0; i < curSymLen; i++) { + if (i < prvSymLen && (Xop_list_wkr_.Compare_normalize(curSymAry[i]) == Xop_list_wkr_.Compare_normalize(prvSymAry[i]))) { + commonSymLen = i + 1; + } + else { + prvSymMatch = false; + break; + } + } + } boolean prvSymMatch; int commonSymLen = 0; boolean allDd = false; + private int SymAry_fill(byte[] src, int cur_pos, int src_len, byte curByt) { + curSymLen = 0; + curSymAry[curSymLen++] = curByt; + allDd = true; + boolean loop = true; + SymAry_fill_overflow = false; + while (loop) { + if (cur_pos == src_len) break; + if (curSymLen == Max_list_depth) { // WORKAROUND: xowa imposes max list depth of 256; MW is unlimited; may change for future release but 256 should accomodate all real-world usages + boolean stop = false; + for (int i = cur_pos; i < src_len; i++) { + curByt = src[i]; + switch (curByt) { + case Byte_ascii.Asterisk: + case Byte_ascii.Hash: + case Byte_ascii.Semic: + case Byte_ascii.Colon: + cur_pos = i; + break; + default: + stop = true; + break; + } + if (stop) break; + } + for (int i = 0; i < Max_list_depth; i++) + curSymAry[i] = Byte_ascii.Nil; + curSymLen = 0; + SymAry_fill_overflow = true; + return cur_pos; + } + curByt = src[cur_pos]; + switch (curByt) { + case Byte_ascii.Asterisk: + case Byte_ascii.Hash: + case Byte_ascii.Semic: + curSymAry[curSymLen++] = curByt; + cur_pos++; + allDd = false; + break; + case Byte_ascii.Colon: + curSymAry[curSymLen++] = curByt; + cur_pos++; + break; + default: + loop = false; + break; + } + } + return cur_pos; + } + private void Reset(int newListId) { + posBldr.Init(); + curSymLen = 0; + prvSymAry = Bry_.Empty; + dd_chk = false; + listId = newListId; + } + public static final int Max_list_depth = 256; +} +class Xop_list_wkr_ { + public static byte[] MakeSymAry(byte[] curSymAry, int curSymLen) { + byte[] rv = new byte[curSymLen]; + for (int i = 0; i < curSymLen; i++) + rv[i] = curSymAry[i]; + return rv; + } + public static byte Compare_normalize(byte b) { // convert : to ; for sake of determining levels; EX: ";:" is actually same group + switch (b) { + case Byte_ascii.Asterisk: + case Byte_ascii.Hash: + case Byte_ascii.Semic: return b; + case Byte_ascii.Colon: return Byte_ascii.Semic; + default: throw Err_.unhandled(b); + } + } + public static void Close_list_if_present(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int bgn_pos, int cur_pos) { + if (ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tmpl_invk) != Xop_ctx.Stack_not_found) return; // list is inside template; do not close; + while (true) { // close all list tkns on stack; EX: *** n should close all 3 stars; used to only close 1 + int acs_pos = -1, acs_len = ctx.Stack_len(); + for (int i = acs_len - 1; i > -1; i--) { // find auto-close pos + byte cur_acs_tid = ctx.Stack_get(i).Tkn_tid(); + switch (cur_acs_tid) { + case Xop_tkn_itm_.Tid_tblw_tb: // do not bypass tbl_elem; EX: ": {| |- *a |b }" should not close ":" + case Xop_tkn_itm_.Tid_tblw_tc: + case Xop_tkn_itm_.Tid_tblw_te: + case Xop_tkn_itm_.Tid_tblw_td: + case Xop_tkn_itm_.Tid_tblw_th: + case Xop_tkn_itm_.Tid_tblw_tr: + i = -1; // force break; + break; + case Xop_tkn_itm_.Tid_list: + acs_pos = i; + break; + default: + break; + } + } +// int acs_idx = ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_list); + if (acs_pos == Xop_ctx.Stack_not_found) break; // no more list tokens found + ctx.Stack_pop_til(root, src, acs_pos, true, bgn_pos, cur_pos); + } + } +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_basic_tst.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_basic_tst.java new file mode 100644 index 000000000..6c8553f49 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_basic_tst.java @@ -0,0 +1,337 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_list_wkr_basic_tst { + private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void List_1() { + fxt.Test_parse_page_wiki("\n*a" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_end_(3).List_path_(0).List_uid_(0) + ); + } + @Test public void Bos() { + fxt.Test_parse_page_wiki("*a" + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2).List_path_(0).List_uid_(0) + ); + } + @Test public void List_1_2() { + fxt.Test_parse_page_wiki("\n*a\n**b" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_bgn_(3, 6, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(6, 7) + , fxt.tkn_list_end_(7).List_path_(0, 0) + , fxt.tkn_list_end_(7).List_path_(0) + ); + } + @Test public void List_1_2_2() { + fxt.Test_parse_page_wiki("\n*a\n**b\n**c" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_bgn_(3, 6, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(6, 7) + , fxt.tkn_list_end_(7).List_path_(0, 0) + , fxt.tkn_list_bgn_(7, 10, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 1).List_uid_(0) + , fxt.tkn_txt_(10, 11) + , fxt.tkn_list_end_(11).List_path_(0, 1) + , fxt.tkn_list_end_(11).List_path_(0) + ); + } + @Test public void List_1_2_3() { + fxt.Test_parse_page_wiki("\n*a\n**b\n***c" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_bgn_(3, 6, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(6, 7) + , fxt.tkn_list_bgn_(7, 11, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0, 0).List_uid_(0) + , fxt.tkn_txt_(11, 12) + , fxt.tkn_list_end_(12).List_path_(0, 0, 0) + , fxt.tkn_list_end_(12).List_path_(0, 0) + , fxt.tkn_list_end_(12).List_path_(0) + ); + } + @Test public void List_2() { + fxt.Test_parse_page_wiki("\n**a" + , fxt.tkn_list_bgn_(0, 3, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_list_bgn_(0, 3, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(3, 4) + , fxt.tkn_list_end_(4).List_path_(0, 0) + , fxt.tkn_list_end_(4).List_path_(0) + ); + } + @Test public void List_1_3() { + fxt.Test_parse_page_wiki("\n*a\n***b" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_bgn_(3, 7, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_list_bgn_(3, 7, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0, 0).List_uid_(0) + , fxt.tkn_txt_(7, 8) + , fxt.tkn_list_end_(8).List_path_(0, 0, 0) + , fxt.tkn_list_end_(8).List_path_(0, 0) + , fxt.tkn_list_end_(8).List_path_(0) + ); + } + @Test public void List_1_2_1() { + fxt.Test_parse_page_wiki("\n*a\n**b\n*c" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_bgn_(3, 6, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(6, 7) + , fxt.tkn_list_end_(7).List_path_(0, 0) + , fxt.tkn_list_end_(7).List_path_(0) + , fxt.tkn_list_bgn_(7, 9, Xop_list_tkn_.List_itmTyp_ul).List_path_(1).List_uid_(0) + , fxt.tkn_txt_(9, 10) + , fxt.tkn_list_end_(10).List_path_(1) + ); + } + @Test public void List_1_1_1() { + fxt.Test_parse_page_wiki("\n*a\n*b\n*c" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_end_(3).List_path_(0) + , fxt.tkn_list_bgn_(3, 5, Xop_list_tkn_.List_itmTyp_ul).List_path_(1).List_uid_(0) + , fxt.tkn_txt_(5, 6) + , fxt.tkn_list_end_(6).List_path_(1) + , fxt.tkn_list_bgn_(6, 8, Xop_list_tkn_.List_itmTyp_ul).List_path_(2).List_uid_(0) + , fxt.tkn_txt_(8, 9) + , fxt.tkn_list_end_(9).List_path_(2) + ); + } + @Test public void List_1___1() { + fxt.Test_parse_page_wiki("\n*a\n\n*b" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_end_(3).List_path_(0) + , fxt.tkn_nl_char_len1_(3) + , fxt.tkn_list_bgn_(4, 6, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(1) + , fxt.tkn_txt_(6, 7) + , fxt.tkn_list_end_(7).List_path_(0) + ); + } + @Test public void List_1_3_1() { + fxt.Test_parse_page_wiki("\n*a\n***b\n*c" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_bgn_(3, 7, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_list_bgn_(3, 7, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0, 0).List_uid_(0) + , fxt.tkn_txt_(7, 8) + , fxt.tkn_list_end_(8).List_path_(0, 0, 0) + , fxt.tkn_list_end_(8).List_path_(0, 0) + , fxt.tkn_list_end_(8).List_path_(0) + , fxt.tkn_list_bgn_(8, 10, Xop_list_tkn_.List_itmTyp_ul).List_path_(1).List_uid_(0) + , fxt.tkn_txt_(10, 11) + , fxt.tkn_list_end_(11).List_path_(1) + ); + } + @Test public void Mix_2o_2u() { + fxt.Test_parse_page_wiki("\n**a\n##b" + , fxt.tkn_list_bgn_(0, 3, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_list_bgn_(0, 3, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(3, 4) + , fxt.tkn_list_end_(4).List_path_(0, 0) + , fxt.tkn_list_end_(4).List_path_(0) + , fxt.tkn_list_bgn_(4, 7, Xop_list_tkn_.List_itmTyp_ol).List_path_(0).List_uid_(1) + , fxt.tkn_list_bgn_(4, 7, Xop_list_tkn_.List_itmTyp_ol).List_path_(0, 0).List_uid_(1) + , fxt.tkn_txt_(7, 8) + , fxt.tkn_list_end_(8).List_path_(0, 0) + , fxt.tkn_list_end_(8).List_path_(0) + ); + } + @Test public void Dt_dd() { + fxt.Test_parse_page_wiki(";a\n:b" + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_dt).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2).List_path_(0) + , fxt.tkn_list_bgn_(2, 4, Xop_list_tkn_.List_itmTyp_dd).List_path_(1).List_uid_(0) + , fxt.tkn_txt_(4, 5) + , fxt.tkn_list_end_(5).List_path_(1) + ); + } + @Test public void Dt_dd_inline() { + fxt.Test_parse_page_wiki(";a:b" // NOTE: no line break + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_dt).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2).List_path_(0) + , fxt.tkn_list_bgn_(2, 3, Xop_list_tkn_.List_itmTyp_dd).List_path_(1).List_uid_(0) + , fxt.tkn_txt_(3, 4) + , fxt.tkn_list_end_(4).List_path_(1) + ); + } + @Test public void Mix_1dd_1ul() { + fxt.Test_parse_page_wiki(":*a" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_dd).List_path_(0).List_uid_(0) + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_end_(3).List_path_(0, 0) + , fxt.tkn_list_end_(3).List_path_(0) + ); + } + @Test public void Mix_1ul__1dd_1ul() { + fxt.Test_parse_page_wiki("*a\n:*b" + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2).List_path_(0).List_uid_(0) + , fxt.tkn_list_bgn_(2, 5, Xop_list_tkn_.List_itmTyp_dd).List_path_(0).List_uid_(1) + , fxt.tkn_list_bgn_(2, 5, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(1) + , fxt.tkn_txt_(5, 6) + , fxt.tkn_list_end_(6).List_path_(0, 0) + , fxt.tkn_list_end_(6).List_path_(0) + ); + } + @Test public void Mix_1dd_1ul__1dd_1ul() { + fxt.Test_parse_page_wiki(":*a\n:*b" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_dd).List_path_(0).List_uid_(0) + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 0).List_uid_(0) + , fxt.tkn_txt_(2, 3) + , fxt.tkn_list_end_(3).List_path_(0, 0) + , fxt.tkn_list_bgn_(3, 6, Xop_list_tkn_.List_itmTyp_ul).List_path_(0, 1).List_uid_(0) + , fxt.tkn_txt_(6, 7) + , fxt.tkn_list_end_(7).List_path_(0, 1) + , fxt.tkn_list_end_(7).List_path_(0) + ); + } + @Test public void Mix_1ul_1hdr() { + fxt.Test_parse_page_wiki("*a\n==a==\n" + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2).List_path_(0).List_uid_(0) + , fxt.tkn_hdr_(2, 9, 2).Hdr_ws_trailing_(1).Subs_ + ( fxt.tkn_txt_(5, 6) + ) + ); + } + @Test public void Mix_1ul_1hdr_1ul() { + fxt.Test_parse_page_wiki("*a\n==a==\n*b" + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2).List_path_(0).List_uid_(0) + , fxt.tkn_hdr_(2, 8, 2).Subs_ + ( fxt.tkn_txt_(5, 6) + ) + , fxt.tkn_list_bgn_(8, 10, Xop_list_tkn_.List_itmTyp_ul).List_path_(0).List_uid_(1) + , fxt.tkn_txt_(10, 11) + , fxt.tkn_list_end_(11).List_path_(0) + ); + } + @Test public void Mix_1ol_1hr_1ol() { + fxt.Test_parse_page_wiki("#a\n----\n#b" + , fxt.tkn_list_bgn_(0, 1, Xop_list_tkn_.List_itmTyp_ol).List_path_(0).List_uid_(0) + , fxt.tkn_txt_(1, 2) + , fxt.tkn_list_end_(2) + , fxt.tkn_para_blank_(2) + , fxt.tkn_hr_(2, 7) + , fxt.tkn_list_bgn_(7, 9, Xop_list_tkn_.List_itmTyp_ol).List_path_(0).List_uid_(1) + , fxt.tkn_txt_(9, 10) + , fxt.tkn_list_end_(10) + ); + } + @Test public void Mix_tblw() { + fxt.Test_parse_page_wiki("::{|\n|a\n|}" + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_dd).List_path_(0).List_uid_(0) + , fxt.tkn_list_bgn_(0, 2, Xop_list_tkn_.List_itmTyp_dd).List_path_(0, 0).List_uid_(0) + , fxt.tkn_tblw_tb_(2, 10).Subs_ + ( fxt.tkn_tblw_tr_(4, 7).Subs_ + ( fxt.tkn_tblw_td_(4, 7).Subs_(fxt.tkn_txt_(6, 7), fxt.tkn_para_blank_(8))) + + ) + , fxt.tkn_list_end_(10).List_path_(0, 0) + , fxt.tkn_list_end_(10).List_path_(0) + ); + } + @Test public void Dif_lvls_1_3_1() { + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "*1" + , "***3" + , "*1" + ) , String_.Concat_lines_nl_skip_last + ( "
        " + , "
      • 1" + , "
          " + , "
        • " + , "
            " + , "
          • 3" + , "
          • " + , "
          " + , "
        • " + , "
        " + , "
      • " + , "
      • 1" + , "
      • " + , "
      " + )); + } + @Test public void Dif_lvls_1_3_2() {// uneven lists + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "*1" + , "***3" + , "**2" + ) , String_.Concat_lines_nl_skip_last + ( "
        " + , "
      • 1" + , "
          " + , "
        • " + , "
            " + , "
          • 3" + , "
          • " + , "
          " + , "
        • " + , "
        • 2" + , "
        • " + , "
        " + , "
      • " + , "
      " + )); + } + @Test public void New_lines() { + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "*a" + , "" + , "**b" + , "" + , "**c" + ) , String_.Concat_lines_nl_skip_last + ( "
        " + , "
      • a" + , "
      • " + , "
      " + , "" + , "
        " + , "
      • " + , "
          " + , "
        • b" + , "
        • " + , "
        " + , "
      • " + , "
      " + , "" + , "
        " + , "
      • " + , "
          " + , "
        • c" + , "
        • " + , "
        " + , "
      • " + , "
      " + )); + } +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_para_tst.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_para_tst.java new file mode 100644 index 000000000..a53e89db0 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_para_tst.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +import org.junit.*; +public class Xop_list_wkr_para_tst { + @Before public void init() {fxt.Reset(); fxt.Init_para_y_();} private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void Basic() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "*a" + ) , String_.Concat_lines_nl_skip_last + ( "
        " + , "
      • a" + , "
      • " + , "
      " + , "" + ) + ); + } + @Test public void Multiple() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "*a" + , "*b" + ) , String_.Concat_lines_nl_skip_last + ( "
        " + , "
      • a" + , "
      • " + , "
      • b" + , "
      • " + , "
      " + ) + ); + } + @Test public void Multiple_w_1_nl() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "*a" + , "" + , "*b" + ) , String_.Concat_lines_nl_skip_last + ( "
        " + , "
      • a" + , "
      • " + , "
      " + , "" + , "
        " + , "
      • b" + , "
      • " + , "
      " + ) + ); + } + @Test public void Pre_between_lists() { // PURPOSE: list should close pre; EX:en.b:Knowing Knoppix/Other applications; DATE:2014-02-18 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "#a" + , " b" + , "#c" // should close
       opened by b
      +			) ,	String_.Concat_lines_nl_skip_last
      +			(	"
        " + , "
      1. a" + , "
      2. " + , "
      " + , "" + , "
      b"
      +			,	"
      " + , "" + , "
        " + , "
      1. c" + , "
      2. " + , "
      " + ) + ); + } +} diff --git a/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_uncommon_tst.java b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_uncommon_tst.java new file mode 100644 index 000000000..54db71ac4 --- /dev/null +++ b/400_xowa/src_470_list/gplx/xowa/Xop_list_wkr_uncommon_tst.java @@ -0,0 +1,351 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_list_wkr_uncommon_tst { + private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void Bug_specified_div() { // FIX:
    was not clearing state for lnki; EX.WP:Ananke (moon) + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "
    " + , "#a" + , "
    " + , "*b" + ) , String_.Concat_lines_nl_skip_last + ( "
    " + , "
      " + , "
    1. a" + , "" + , "
    2. " + , "
    " + , "
      " + , "
    • b" + , "
    • " + , "
    " + )); + } + @Test public void Bug_mismatched() { // FIX:
    was not clearing state for lnki; EX.WP:Ananke (moon) + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "::a" + , ":::1" + , "::::11" + , ":::::111" + , "::b" + ) , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , "
    " + , "
    a" + , "
    " + , "
    1" + , "
    " + , "
    11" + , "
    " + , "
    111" + , "
    " + , "
    " + , "
    " + , "
    " + , "
    " + , "
    " + , "
    " + , "
    b" + , "
    " + , "
    " + , "
    " + , "
    " + )); + } + @Test public void Empty_li_ignored() { // PURPOSE: inner template can cause dupe li; EX.WP: any Calendar day and NYT link; EX: 1/1 + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "*a" + , "* " + , "*b" + , "*c" + ) , String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a" + , "
    • " + , "
    • b" + , "
    • " + , "
    • c" + , "
    • " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void List_in_tblw() { // PURPOSE: list inside table should not be close outer list; EX.WP: Cato the Elder + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "*a" + , "{|" + , "|b" + , "::c" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a" + , "
    • " + , "
    " + , "" + , " " + , " " + , " " + , "
    b" + , "" + , "
    " + , "
    " + , "
    " + , "
    c" + , "
    " + , "
    " + , "
    " + , "
    " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Dt_dd_colon_at_eol() { // PURPOSE: dangling ":" should not put next line in
    ; EX.WP: Stein; b was being wrapped in
    b
    + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( ";a:" + , "*b" + , "" + , ";c" + , "*d" + ) , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    a" + , "
    " + , "
    " + , "
      " + , "
    • b" + , "
    • " + , "
    " + , "" + , "
    " + , "
    c" + , "
    " + , "
    " + , "
      " + , "
    • d" + , "
    • " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Dd_should_not_print_colon() {// PURPOSE: ;a:\n should show as ";a" not ";a:". colon should still be considered as part of empty list; DATE:2013-11-07 + fxt.Test_parse_page_all_str(";a:\nb" + , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    a" + , "
    " + , "
    " + , "b" + )); + } + @Test public void Dt_dd_colon_in_lnki() { // PURPOSE: "; [[Portal:a]]" should not split lnki; EX.WP: Wikipedia:WikiProject Military history/Operation Majestic Titan; "; [[Wikipedia:WikiProject Military history/Operation Majestic Titan/Phase I|Phase I]]: a b" + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( ";[[Portal:a]]" + ) , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    Portal:A" + , "
    " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Max_list_depth() { // PURPOSE: 256+ * caused list parser to fail; ignore; EX.WP:Bariatric surgery + String multiple = String_.Repeat("*", 300); + fxt.Test_parse_page_all_str(multiple + , String_.Concat_lines_nl_skip_last + ( multiple + )); + } + @Test public void Numbered_list_resets_incorrectly() { // PURPOSE: as description + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "#A" + , "#*Aa" + , "#**Aaa" + , "#*Ab" + , "#B" + ) , String_.Concat_lines_nl_skip_last + ( "
      " + , "
    1. A" + , "" + , "
        " + , "
      • Aa" + , "" + , "
          " + , "
        • Aaa" + , "
        • " + , "
        " + , "
      • " + , "
      • Ab" + , "
      • " + , "
      " // was showing as
    + , " " + , "
  • B" + , "
  • " + , "" + , "" + )); + fxt.Init_para_n_(); + } + @Test public void List_should_not_end_indented_table() {// PURPOSE: :{| was being closed by \n*; EX:w:Maxwell's equations; DATE:20121231 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( ":{|" + , "|-" + , "|" + , "*a" + , "|b" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    " + , " " + , " " + , " " + , " " + , " " + , "
    " + , "
      " + , "
    • a" + , "
    • " + , "
    " + , "
    b" + , "
    " + , "
    " + , "
    " + )); + } + @Test public void Dt_dd_broken_by_xnde() { // PURPOSE.fix: xnde was resetting dl incorrectly; EX:w:Virus; DATE:2013-01-31 + fxt.Test_parse_page_all_str(";a:c" + , String_.Concat_lines_nl_skip_last + ( "
    " + , "
    a" + , "
    " + , "
    c" + , "
    " + , "
    " + )); + } + @Test public void Trim_empty_list_items() { // PURPOSE: empty list items should be ignored; DATE:2013-07-02 + fxt.Test_parse_page_all_str("*** \n" + , String_.Concat_lines_nl_skip_last + ( "" + )); + } + @Test public void Trim_empty_list_items_error() { // PURPOSE.fix: do not add empty itm's nesting to current list; DATE:2013-07-07 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl + ( "* a" + , "** " // do not add ** to nest + , "*** b" + , "* c" + ) , String_.Concat_lines_nl + ( "
      " + , "
    • a" + , "
        " + , "
      • " + , "
          " + , "
        • b" + , "
        • " + , "
        " + , "
      • " + , "
      " + , "
    • " + , "
    • c" + , "
    • " + , "
    " + )); + } + @Test public void Tblw_should_autoclose() {// PURPOSE: tblw should auto-close open list + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "#a" + , "{|" + , "|b" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "
      " + , "
    1. a" + , "
    2. " + , "
    " + , "" + , " " + , " " + , " " + , "
    b" + , "
    " + , "" + )); + } + @Test public void Tblx_should_not_autoclose() { // PURPOSE: do not auto-close list if table is xnde; DATE:2014-02-05 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl + ( "#a" + , "#
    b
    " + , "c" + ) , String_.Concat_lines_nl + ( "
      " + , "
    1. a" + , "
    2. " + , "
    3. " + , " " + , " " + , " " + , " " + , "
      b" + , "
      " + , "
    4. " + , "
    " + , "c" + )); + } + @Test public void Li_disappears() { // PURPOSE: "\n*" disappears when followed by "
  • "; PAGE:en.w:Bristol_Bullfinch; DATE:2014-06-24 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl + ( "a" + , "*b
  • " + ), String_.Concat_lines_nl_skip_last // NOTE: tag sequence matches MW output + ( "a" + , "
      " + , "
    • b" + , "
    • " + , "
    • " + , " " + , "
    " + )); + } + @Test public void Ul_should_end_wlst() { // PURPOSE: should end wiki_list; PAGE:en.w:Bristol_Bullfinch; DATE:2014-06-24 + fxt.Test_parse_page_all_str + ( "*ab" + , String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a" + , "
    • " + , "
    b" + , "
  • " + , "" + )); + } +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_lxr.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_lxr.java new file mode 100644 index 000000000..61e268e81 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_lxr.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +class Xop_tblw_lxr implements Xop_lxr { + public byte Lxr_tid() {return Xop_lxr_.Tid_tblw;} + public int Make_tkn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) { + int rv = Handle_bang(wlxr_type, ctx, ctx.Tkn_mkr(), root, src, src_len, bgn_pos, cur_pos); + if (rv != Continue) return rv; + rv = Handle_lnki(wlxr_type, ctx, ctx.Tkn_mkr(), root, src, src_len, bgn_pos, cur_pos); + if (rv != Continue) return rv; + return ctx.Tblw().Make_tkn_bgn(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, false, wlxr_type, Xop_tblw_wkr.Called_from_general, -1, -1); + } + public static final int Continue = -2; // -2 b/c -1 used by Called_from_pre + public static int Handle_bang(int wlxr_type, Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) { + // standalone "!" should be ignored if no tblw present; EX: "a b! c" should not trigger ! for header + switch (wlxr_type) { + case Xop_tblw_wkr.Tblw_type_th: // \n! + case Xop_tblw_wkr.Tblw_type_th2: // !! + case Xop_tblw_wkr.Tblw_type_td: // \n| + Xop_tkn_itm owner_tblw_tb = ctx.Stack_get_typ(Xop_tkn_itm_.Tid_tblw_tb); // check entire stack for tblw; DATE:2014-03-11 + if ( owner_tblw_tb == null // no tblw in stack; highly probably that current sequence is not tblw tkn + || ctx.Cur_tkn_tid() == Xop_tkn_itm_.Tid_lnki // cur tid is lnki; PAGE:en.w:Pink_(singer); DATE:2014-06-25 + ) { + int lnki_pos = ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_lnki); + if (lnki_pos != Xop_ctx.Stack_not_found && wlxr_type == Xop_tblw_wkr.Tblw_type_td) {// lnki present;// NOTE: added Xop_tblw_wkr.Tblw_type_td b/c th should not apply when tkn_mkr.Pipe() is called below; DATE:2013-04-24 + Xop_tkn_itm lnki_tkn = ctx.Stack_pop_til(root, src, lnki_pos, false, bgn_pos, cur_pos); // pop any intervening nodes until lnki + ctx.Stack_add(lnki_tkn); // push lnki back onto stack; TODO: combine these 2 lines into 1 + // NOTE: this is a "\n|" inside a [[ ]]; must create two tokens for lnki to build correctly; + ctx.Subs_add(root, tkn_mkr.NewLine(bgn_pos, bgn_pos + 1, Xop_nl_tkn.Tid_char, 1)); + return Xop_pipe_lxr._.Make_tkn(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos); // NOTE: need to call pipe_lxr in order to invalidate if lnki; DATE:2014-06-06 + } + else { // \n| or \n! but no tbl + if ( bgn_pos != Xop_parser_.Doc_bgn_bos // avoid ! at BOS + && src[bgn_pos] == Byte_ascii.NewLine) // handle "!" etc. + return Xop_tblw_wkr.Handle_false_tblw_match(ctx, root, src, bgn_pos, cur_pos, tkn_mkr.Txt(bgn_pos + 1, cur_pos), true); // +1 to ignore \n of "\n!", "\n!!", "\n|"; DATE:2014-02-19 + else // handle "!!" only + return ctx.Lxr_make_txt_(cur_pos); + } + } + break; + } + return Continue; + } + public static int Handle_lnki(int wlxr_type, Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) { + Xop_tkn_itm last_tkn = ctx.Stack_get_last(); + if ( last_tkn != null + && last_tkn.Tkn_tid() == Xop_tkn_itm_.Tid_lnki) { + Xop_lnki_tkn lnki = (Xop_lnki_tkn)last_tkn; + if ( lnki.Pipe_count_is_zero()) { // 1st pipe; EX: [[A\n|+B]] + boolean invalidate = false; + switch (wlxr_type) { // tblw found; check if in lnki and validate ttl; DATE:2014-03-29 + case Xop_tblw_wkr.Tblw_type_tb: // \n{| + case Xop_tblw_wkr.Tblw_type_tc: // \n|+ + case Xop_tblw_wkr.Tblw_type_tr: // \n|- + case Xop_tblw_wkr.Tblw_type_te: // \n|} + invalidate = true; // always invalidate + break; + case Xop_tblw_wkr.Tblw_type_td2: // ||; EX: [[A||B]] + if (ctx.Tid_is_image_map()) { // if in ImageMap, then treat "||" as "pipe" (not "pipe_text"); note that outer tbl is ignored; EX:w:United_States_presidential_election,_1992 + ctx.Subs_add(root, tkn_mkr.Pipe(bgn_pos, cur_pos)); + return cur_pos; + } + invalidate = !Xop_lnki_wkr_.Parse_ttl(ctx, src, lnki, bgn_pos); // check if invalid; EX: "[[A<||]]" would be invalid b/c of < + if (!invalidate) { // "valid" title, but "||" must be converted to pipe inside lnki; EX:cs.w:Main_Page; DATE:2014-05-09 + ctx.Subs_add(root, tkn_mkr.Pipe(bgn_pos, cur_pos)); // NOTE: technically need to check if pipe or pipe_text; for now, do pipe as pipe_text could break [[File:A.png||20px]]; DATE:2014-05-06 + return cur_pos; + } + break; + } + if (invalidate) { + ctx.Stack_pop_last(); + return Xop_lnki_wkr_.Invalidate_lnki(ctx, src, root, lnki, bgn_pos); + } + } + else { // nth pipe; no need to check for invalidate + switch (wlxr_type) { + case Xop_tblw_wkr.Tblw_type_tc: // \n|+ + case Xop_tblw_wkr.Tblw_type_tr: // \n|- + case Xop_tblw_wkr.Tblw_type_td: // \n| + //case Xop_tblw_wkr.Tblw_type_te: // |} // NOTE: ignore "|}"; needed for incomplete lnkis; EX: |[[a\n|}; EX:w:Wikipedia:Changing_attribution_for_an_edit; DATE:2014-03-16 + ctx.Subs_add(root, tkn_mkr.NewLine(bgn_pos, bgn_pos + 1, Xop_nl_tkn.Tid_char, 1)); + ctx.Subs_add(root, tkn_mkr.Pipe(bgn_pos + 1, bgn_pos + 2)); + return bgn_pos + 2; // +2 to skip "\n|", but still look at 3rd char; ("+", "-", or "}") + case Xop_tblw_wkr.Tblw_type_td2: // || + ctx.Subs_add(root, tkn_mkr.Pipe(bgn_pos, cur_pos)); + return cur_pos; + case Xop_tblw_wkr.Tblw_type_th2: // !! + case Xop_tblw_wkr.Tblw_type_th: // ! + ctx.Subs_add(root, tkn_mkr.Txt(bgn_pos, cur_pos)); // NOTE: cur_pos should handle ! and !! + return cur_pos; + } + } + } + return Continue; + } + public Xop_tblw_lxr(byte wlxr_type) {this.wlxr_type = wlxr_type;} private byte wlxr_type; + public static final Xop_tblw_lxr _ = new Xop_tblw_lxr(); Xop_tblw_lxr() {} + public void Init_by_wiki(Xow_wiki wiki, ByteTrieMgr_fast core_trie) { + core_trie.Add(Hook_tb, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_tb)); + core_trie.Add(Hook_te, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_te)); + core_trie.Add(Hook_tr, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_tr)); + core_trie.Add(Hook_td, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_td)); + core_trie.Add(Hook_th, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_th)); + core_trie.Add(Hook_tc, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_tc)); + core_trie.Add(Hook_td2, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_td2)); + core_trie.Add(Hook_th2, new Xop_tblw_lxr(Xop_tblw_wkr.Tblw_type_th2)); + } + public void Init_by_lang(Xol_lang lang, ByteTrieMgr_fast core_trie) {} + public static final byte[] Hook_tb = Bry_.new_ascii_("\n{|"), Hook_te = Bry_.new_ascii_("\n|}"), Hook_tr = Bry_.new_ascii_("\n|-") + , Hook_td = Bry_.new_ascii_("\n|"), Hook_th = Bry_.new_ascii_("\n!"), Hook_tc = Bry_.new_ascii_("\n|+") + , Hook_td2 = Bry_.new_ascii_("||"), Hook_th2 = Bry_.new_ascii_("!!"); +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_lxr_ws.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_lxr_ws.java new file mode 100644 index 000000000..abd608284 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_lxr_ws.java @@ -0,0 +1,110 @@ +/* +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.xowa; import gplx.*; +public class Xop_tblw_lxr_ws { + public static int Make(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, byte wlxr_type, boolean called_from_pre) { + int rv = Xop_tblw_lxr.Handle_bang(wlxr_type, ctx, ctx.Tkn_mkr(), root, src, src_len, bgn_pos, cur_pos); + if (rv != Xop_tblw_lxr.Continue) return rv; + rv = Xop_tblw_lxr.Handle_lnki(wlxr_type, ctx, ctx.Tkn_mkr(), root, src, src_len, bgn_pos, cur_pos); + if (rv != Xop_tblw_lxr.Continue) return rv; + if (!called_from_pre) { // skip if called from pre, else will return text, since pre_lxr has not created \n tkn yet; EX: "\n ! a"; DATE:2014-02-14 + // find first non-ws tkn; check if nl or para + int root_subs_len = root.Subs_len(); + int tkn_idx = root_subs_len - 1; + boolean loop = true, nl_found = false; + while (loop) { + if (tkn_idx < 0) break; + Xop_tkn_itm tkn = root.Subs_get(tkn_idx); + switch (tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_space: case Xop_tkn_itm_.Tid_tab: // ws: keep moving backwards + tkn_idx--; + break; + case Xop_tkn_itm_.Tid_newLine: + case Xop_tkn_itm_.Tid_para: + loop = false; + nl_found = true; + break; + default: + loop = false; + break; + } + } + if (tkn_idx == -1) { // bos reached; all tkns are ws; + if (wlxr_type == Xop_tblw_wkr.Tblw_type_tb) { // wlxr_type is {|; + root.Subs_del_after(0); // trim + return ctx.Tblw().Make_tkn_bgn(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, false, wlxr_type, Xop_tblw_wkr.Called_from_general, -1, -1); // process {| + } + else // wlxr_type is something else, but invalid since no containing {| + return ctx.Lxr_make_txt_(cur_pos); + } + + if (!nl_found && wlxr_type == Xop_tblw_wkr.Tblw_type_td) // | but no nl; return control to pipe_lxr for further processing + return Tblw_ws_cell_pipe; + if (nl_found) + root.Subs_del_after(tkn_idx); + } + return ctx.Tblw().Make_tkn_bgn(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, false, wlxr_type, Xop_tblw_wkr.Called_from_general, -1, -1); + } + public static final byte[] Hook_tb = Bry_.new_ascii_("{|"), Hook_te = Bry_.new_ascii_("|}"), Hook_tr = Bry_.new_ascii_("|-") + , Hook_th = Bry_.new_ascii_("!"), Hook_tc = Bry_.new_ascii_("|+"); + public static final int Tblw_ws_cell_pipe = -1; +} +class Xop_tblw_ws_itm { + public byte Tblw_type() {return tblw_type;} private byte tblw_type; + public int Hook_len() {return hook_len;} private int hook_len; + public Xop_tblw_ws_itm(byte tblw_type, int hook_len) {this.tblw_type = tblw_type; this.hook_len = hook_len;} + + public static final byte Type_tb = Xop_tblw_wkr.Tblw_type_tb, Type_te = Xop_tblw_wkr.Tblw_type_te, Type_tr = Xop_tblw_wkr.Tblw_type_tr, Type_tc = Xop_tblw_wkr.Tblw_type_tc + , Type_th = Xop_tblw_wkr.Tblw_type_th, Type_td = Xop_tblw_wkr.Tblw_type_td, Type_nl = 16, Type_xnde = 17; + public static ByteTrieMgr_slim trie_() {// MW.REF:Parser.php|doBlockLevels + ByteTrieMgr_slim rv = ByteTrieMgr_slim.cs_(); + trie_itm(rv, Type_tb, Xop_tblw_lxr_ws.Hook_tb); + trie_itm(rv, Type_te, Xop_tblw_lxr_ws.Hook_te); + trie_itm(rv, Type_tr, Xop_tblw_lxr_ws.Hook_tr); + trie_itm(rv, Type_th, Xop_tblw_lxr_ws.Hook_th); + trie_itm(rv, Type_tc, Xop_tblw_lxr_ws.Hook_tc); + trie_itm(rv, Type_td, Bry_.bytes_(Byte_ascii.Pipe)); + trie_itm(rv, Type_nl, Bry_.bytes_(Byte_ascii.NewLine)); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_table); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_tr); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_td); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_th); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_blockquote); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_h1); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_h2); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_h3); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_h4); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_h5); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_h6); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_pre); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_p); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_div); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_hr); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_li); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_ul); + trie_itm_xnde(rv, Xop_xnde_tag_.Tag_ol); + return rv; + } + private static void trie_itm(ByteTrieMgr_slim trie, byte type, byte[] bry) {trie.Add(bry, new Xop_tblw_ws_itm(type, bry.length));} + private static void trie_itm_xnde(ByteTrieMgr_slim trie, Xop_xnde_tag tag) { + byte[] tag_name = tag.Name_bry(); + int tag_name_len = tag_name.length; + trie.Add(Bry_.Add(Bry_xnde_bgn, tag_name), new Xop_tblw_ws_itm(Type_xnde, tag_name_len)); + trie.Add(Bry_.Add(Bry_xnde_end, tag_name), new Xop_tblw_ws_itm(Type_xnde, tag_name_len + 1)); + } static byte[] Bry_xnde_bgn = new byte[] {Byte_ascii.Lt, Byte_ascii.Slash}, Bry_xnde_end = new byte[] {Byte_ascii.Lt}; +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tb_tkn.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tb_tkn.java new file mode 100644 index 000000000..222ca3b42 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tb_tkn.java @@ -0,0 +1,37 @@ +/* +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.xowa; import gplx.*; +public class Xop_tblw_tb_tkn extends Xop_tkn_itm_base implements Xop_tblw_tkn { + public Xop_tblw_tb_tkn(int bgn, int end, boolean tblw_xml, boolean auto_created) { + this.tblw_xml = tblw_xml; this.Tkn_ini_pos(false, bgn, end); + if (auto_created) // auto-created should be marked as having no attributes, else text may get gobbled up incorrectly; EX:Paris#Demographics DATE:2014-03-18 + atrs_bgn = atrs_end = bgn; + } + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_tblw_tb;} + public int Tblw_tid() {return Xop_xnde_tag_.Tid_table;} + public int Atrs_bgn() {return atrs_bgn;} private int atrs_bgn = Xop_tblw_wkr.Atrs_null; + public int Atrs_end() {return atrs_end;} private int atrs_end = -1; + public void Atrs_rng_set(int bgn, int end) {this.atrs_bgn = bgn; this.atrs_end = end;} + public Xop_xatr_itm[] Atrs_ary() {return atrs_ary;} public Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} private Xop_xatr_itm[] atrs_ary; + public boolean Tblw_xml() {return tblw_xml;} private boolean tblw_xml; + public void Tblw_xml_(boolean v) {tblw_xml = v;} + public int Tblw_subs_len() {return tblw_subs_len;} public void Tblw_subs_len_add_() {++tblw_subs_len;} private int tblw_subs_len; + public int Caption_count() {return caption_count;} public Xop_tblw_tb_tkn Caption_count_(int v) {caption_count = v; return this;} private int caption_count = 0; + public Xop_tblw_tb_tkn Caption_count_add_1() {++caption_count; return this;} + public Xop_tblw_tb_tkn Subs_add_ary(Xop_tkn_itm... ary) {for (Xop_tkn_itm itm : ary) super.Subs_add(itm); return this;} +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tc_tkn.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tc_tkn.java new file mode 100644 index 000000000..9a1037285 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tc_tkn.java @@ -0,0 +1,30 @@ +/* +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.xowa; import gplx.*; +public class Xop_tblw_tc_tkn extends Xop_tkn_itm_base implements Xop_tblw_tkn { + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_tblw_tc;} + public int Tblw_tid() {return Xop_xnde_tag_.Tid_caption;} + public int Atrs_bgn() {return atrs_bgn;} private int atrs_bgn = Xop_tblw_wkr.Atrs_null; + public int Atrs_end() {return atrs_end;} private int atrs_end = -1; + public void Atrs_rng_set(int bgn, int end) {this.atrs_bgn = bgn; this.atrs_end = end;} + public Xop_xatr_itm[] Atrs_ary() {return atrs_ary;} public Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} private Xop_xatr_itm[] atrs_ary; + public boolean Tblw_xml() {return tblw_xml;} private boolean tblw_xml; + public int Tblw_subs_len() {return tblw_subs_len;} public void Tblw_subs_len_add_() {++tblw_subs_len;} private int tblw_subs_len; + public Xop_tblw_tc_tkn Subs_add_ary(Xop_tkn_itm... ary) {for (Xop_tkn_itm itm : ary) super.Subs_add(itm); return this;} + public Xop_tblw_tc_tkn(int bgn, int end, boolean tblw_xml) {this.tblw_xml = tblw_xml; this.Tkn_ini_pos(false, bgn, end);} +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_td_tkn.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_td_tkn.java new file mode 100644 index 000000000..3026c68b7 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_td_tkn.java @@ -0,0 +1,30 @@ +/* +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.xowa; import gplx.*; +public class Xop_tblw_td_tkn extends Xop_tkn_itm_base implements Xop_tblw_tkn { + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_tblw_td;} + public int Tblw_tid() {return Xop_xnde_tag_.Tid_td;} + public int Atrs_bgn() {return atrs_bgn;} private int atrs_bgn = Xop_tblw_wkr.Atrs_null; + public int Atrs_end() {return atrs_end;} private int atrs_end = -1; + public void Atrs_rng_set(int bgn, int end) {this.atrs_bgn = bgn; this.atrs_end = end;} + public Xop_xatr_itm[] Atrs_ary() {return atrs_ary;} public Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} private Xop_xatr_itm[] atrs_ary; + public boolean Tblw_xml() {return tblw_xml;} private boolean tblw_xml; + public int Tblw_subs_len() {return tblw_subs_len;} public void Tblw_subs_len_add_() {++tblw_subs_len;} private int tblw_subs_len; + public Xop_tblw_td_tkn Subs_add_ary(Xop_tkn_itm... ary) {for (Xop_tkn_itm itm : ary) super.Subs_add(itm); return this;} + public Xop_tblw_td_tkn(int bgn, int end, boolean tblw_xml) {this.tblw_xml = tblw_xml; this.Tkn_ini_pos(false, bgn, end);} +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_th_tkn.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_th_tkn.java new file mode 100644 index 000000000..9457f429a --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_th_tkn.java @@ -0,0 +1,30 @@ +/* +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.xowa; import gplx.*; +public class Xop_tblw_th_tkn extends Xop_tkn_itm_base implements Xop_tblw_tkn { + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_tblw_th;} + public int Tblw_tid() {return Xop_xnde_tag_.Tid_th;} + public int Atrs_bgn() {return atrs_bgn;} private int atrs_bgn = Xop_tblw_wkr.Atrs_null; + public int Atrs_end() {return atrs_end;} private int atrs_end = -1; + public void Atrs_rng_set(int bgn, int end) {this.atrs_bgn = bgn; this.atrs_end = end;} + public Xop_xatr_itm[] Atrs_ary() {return atrs_ary;} public Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} private Xop_xatr_itm[] atrs_ary; + public boolean Tblw_xml() {return tblw_xml;} private boolean tblw_xml; + public int Tblw_subs_len() {return tblw_subs_len;} public void Tblw_subs_len_add_() {++tblw_subs_len;} private int tblw_subs_len; + public Xop_tblw_th_tkn Subs_add_ary(Xop_tkn_itm... ary) {for (Xop_tkn_itm itm : ary) super.Subs_add(itm); return this;} + public Xop_tblw_th_tkn(int bgn, int end, boolean tblw_xml) {this.tblw_xml = tblw_xml; this.Tkn_ini_pos(false, bgn, end);} +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tkn.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tkn.java new file mode 100644 index 000000000..3570e5b97 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tkn.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +public interface Xop_tblw_tkn extends Xop_tkn_itm { + int Tblw_tid(); + boolean Tblw_xml(); + int Tblw_subs_len(); void Tblw_subs_len_add_(); + int Atrs_bgn(); + int Atrs_end(); + void Atrs_rng_set(int bgn, int end); + Xop_xatr_itm[] Atrs_ary(); Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v); +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tr_tkn.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tr_tkn.java new file mode 100644 index 000000000..d6e1429a9 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_tr_tkn.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +public class Xop_tblw_tr_tkn extends Xop_tkn_itm_base implements Xop_tblw_tkn { + public Xop_tblw_tr_tkn(int bgn, int end, boolean tblw_xml, boolean auto_created) { + this.tblw_xml = tblw_xml; this.Tkn_ini_pos(false, bgn, end); + if (auto_created) // auto-created should be marked as having no attributes, else text may get gobbled up incorrectly; EX:Paris#Demographics DATE:2014-03-18 + atrs_bgn = atrs_end = bgn; + } + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_tblw_tr;} + public int Tblw_tid() {return Xop_xnde_tag_.Tid_tr;} + public int Atrs_bgn() {return atrs_bgn;} private int atrs_bgn = Xop_tblw_wkr.Atrs_null; + public int Atrs_end() {return atrs_end;} private int atrs_end = -1; + public void Atrs_rng_set(int bgn, int end) {this.atrs_bgn = bgn; this.atrs_end = end;} + public Xop_xatr_itm[] Atrs_ary() {return atrs_ary;} public Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} private Xop_xatr_itm[] atrs_ary; + public boolean Tblw_xml() {return tblw_xml;} private boolean tblw_xml; + public int Tblw_subs_len() {return tblw_subs_len;} public void Tblw_subs_len_add_() {++tblw_subs_len;} private int tblw_subs_len; + public Xop_tblw_tr_tkn Subs_add_ary(Xop_tkn_itm... ary) {for (Xop_tkn_itm itm : ary) super.Subs_add(itm); return this;} +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr.java new file mode 100644 index 000000000..dc9dfbb1b --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr.java @@ -0,0 +1,531 @@ +/* +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.xowa; import gplx.*; +public class Xop_tblw_wkr implements Xop_ctx_wkr { + private int tblw_te_ignore_count = 0; + public boolean Cell_pipe_seen() {return cell_pipe_seen;} public Xop_tblw_wkr Cell_pipe_seen_(boolean v) {cell_pipe_seen = v; return this;} private boolean cell_pipe_seen; // status of 1st cell pipe; EX: \n| a | b | c || -> flag pipe between a and b but ignore b and c + public void Ctor_ctx(Xop_ctx ctx) {} + public void Page_bgn(Xop_ctx ctx, Xop_root_tkn root) {cell_pipe_seen = false; tblw_te_ignore_count = 0;} + public void Page_end(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int src_len) {} + public void AutoClose(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, Xop_tkn_itm tkn) { + tkn.Subs_move(root); + tkn.Src_end_(cur_pos); + } + public static final byte Called_from_general = 0, Called_from_list = 1, Called_from_pre = 2; + public int Make_tkn_bgn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, boolean tbl_is_xml, byte wlxr_type, byte called_from, int atrs_bgn, int atrs_end) {// REF.MW: Parser|doTableStuff + if (bgn_pos == Xop_parser_.Doc_bgn_bos) { + bgn_pos = 0; // do not allow -1 pos + } + + int list_tkn_idx = ctx.Stack_idx_find_but_stop_at_tbl(Xop_tkn_itm_.Tid_list); + if ( list_tkn_idx != -1 // list is in effect; DATE:2014-05-05 + && !tbl_is_xml // tbl is wiki-syntax; ie: auto-close if "{|" but do not close if ""; DATE:2014-02-05 + && called_from != Called_from_list // do not close if called from list; EX: consider "{|"; "* a {|" is called from list_wkr, and should not close; "* a\n{|" is called from tblw_lxr and should close; DATE:2014-02-14 + ) { + if (wlxr_type == Tblw_type_td2) { // if in list, treat "||" as lnki, not tblw; EX: es.d:casa; es.d:tres; DATE:2014-02-15 + ctx.Subs_add(root, ctx.Tkn_mkr().Pipe(bgn_pos, cur_pos)); // NOTE: technically need to check if pipe or pipe_text; for now, do pipe as pipe_text could break [[File:A.png||20px]]; DATE:2014-05-06 + return cur_pos; + } + else { + Xop_list_wkr_.Close_list_if_present(ctx, root, src, bgn_pos, cur_pos); + } + } + if (ctx.Apos().Stack_len() > 0) // open apos; note that apos keeps its own stack, as they are not "structural" (not sure about this) + ctx.Apos().EndFrame(ctx, root, src, cur_pos, true); // close it + + Xop_tblw_tkn prv_tkn = ctx.Stack_get_tbl(); + if ( prv_tkn == null // prv_tkn not found; i.e.: no earlier "{|" or "
    " + || ( ctx.Stack_get_tblw_tb() == null // no {| on stack; DATE:2014-05-05 + && !tbl_is_xml // and cur is tblw (i.e.: not xnde); DATE:2014-05-05 + ) + ) { + switch (wlxr_type) { + case Tblw_type_tb: // "{|"; + break; // noop; by definition "{|" does not need to have a previous "{|" + case Tblw_type_td: // "|" + case Tblw_type_td2: // "||" + if (tbl_is_xml) { // " + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "
    should automatically add + ctx.Subs_add_and_stack_tblw(root, prv_tkn, tkn_mkr.Tblw_tb(bgn_pos, bgn_pos, tbl_is_xml, true)); + prv_tkn = tkn_mkr.Tblw_tr(bgn_pos, bgn_pos, tbl_is_xml, true); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, prv_tkn); + break; + } + else { + if (called_from == Called_from_pre) + return -1; + else { // DATE:2014-02-19; NOTE: do not add nl if ||; DATE:2014-04-14 + if (wlxr_type == Tblw_type_td) { // "\n|" + ctx.Subs_add(root, ctx.Tkn_mkr().NewLine(bgn_pos, bgn_pos + 1, Xop_nl_tkn.Tid_char, 1)); + ctx.Subs_add(root, ctx.Tkn_mkr().Pipe(bgn_pos + 1, cur_pos)); + } + else // "||" + ctx.Subs_add(root, ctx.Tkn_mkr().Pipe(bgn_pos, cur_pos)); + return cur_pos; + } + } + case Tblw_type_th: // "!" + case Tblw_type_th2: // "!!" + case Tblw_type_tc: // "|+" + case Tblw_type_tr: // "|-" + if (tbl_is_xml) { // should automatically add
    ; DATE:2014-02-13 + prv_tkn = tkn_mkr.Tblw_tb(bgn_pos, bgn_pos, tbl_is_xml, true); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, prv_tkn); + break; + } + else { + if (called_from == Called_from_pre) + return -1; + else + return Xop_tblw_wkr.Handle_false_tblw_match(ctx, root, src, bgn_pos, cur_pos, ctx.Tkn_mkr().Txt(bgn_pos + 1, cur_pos), true); // DATE:2014-02-19 + } + case Tblw_type_te: // "|}" + if (tblw_te_ignore_count > 0) { + --tblw_te_ignore_count; + return cur_pos; + } + else { + if (called_from == Called_from_pre) + return -1; + else + return Xop_tblw_wkr.Handle_false_tblw_match(ctx, root, src, bgn_pos, cur_pos, tkn_mkr.Txt(bgn_pos + 1, cur_pos), true); // +1 to skip "\n" in "\n|}" (don't convert \n to text); DATE:2014-02-19 + } + default: throw Err_.unhandled(wlxr_type); + } + } + + int prv_tid = prv_tkn == null ? Xop_tkn_itm_.Tid_null : prv_tkn.Tkn_tid(); + if (prv_tkn != null && !prv_tkn.Tblw_xml()) { // note that this logic is same as Atrs_close; repeated here for "perf" + switch (prv_tid) { + case Xop_tkn_itm_.Tid_tblw_tb: case Xop_tkn_itm_.Tid_tblw_tr: + Atrs_make(ctx, src, root, this, prv_tkn); + break; + } + } + if (wlxr_type == Tblw_type_te) + return Make_tkn_end(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, Xop_tkn_itm_.Tid_tblw_te, wlxr_type, prv_tkn, prv_tid, tbl_is_xml); + else + return Make_tkn_bgn_tblw(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos, wlxr_type, tbl_is_xml, atrs_bgn, atrs_end, prv_tkn, prv_tid); + } + private int Make_tkn_bgn_tblw(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, byte wlxr_type, boolean tbl_is_xml, int atrs_bgn, int atrs_end, Xop_tblw_tkn prv_tkn, int prv_tid) { + if (wlxr_type != Tblw_type_tb) // NOTE: do not ignore ws if {|; will cause strange behavior with pre; DATE:2013-02-12 + Ignore_ws(ctx, root); + Xop_tblw_tkn new_tkn = null; + switch (wlxr_type) { + case Tblw_type_tb: //
    + boolean ignore_prv = false, auto_create = false; + switch (prv_tid) { + case Xop_tkn_itm_.Tid_null: // noop;
    + break; + case Xop_tkn_itm_.Tid_tblw_td: // noop; somehow rolling up everything after " + , "|-" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "
    + case Xop_tkn_itm_.Tid_tblw_th: // noop; ; code implicitly relies on td clearing block state, but no td was created + return cur_pos; + } + int acs_typeId = typeId; + if (prv_tid != typeId // NOTE: special logic to handle auto-close of
    + break; + case Xop_tkn_itm_.Tid_tblw_tb: // fix;
    ->
    ; ignore current table; DATE:2014-02-02 + if (prv_tkn.Tblw_xml()) { // fix:
    ->
    ; earlier tbl is xnde; ignore; EX:en.b:Wikibooks:Featured books; DATE:2014-02-08 + ((Xop_tblw_tb_tkn)prv_tkn).Tblw_xml_(false); // if
    {|, discard
    , but mark {| as
    ; needed to handle
    \n{|\n| where "|" must be treated as tblw dlm; DATE:2014-02-22 + ignore_prv = true; + } + // else // fix:
    ->
    ; earlier tbl is tblw; auto-create; EX:it.w:Main_Page; DATE:2014-02-08; TIDY:depend on tidy to fix; PAGE: it.w:Portal:Animali; DATE:2014-05-31 + // auto_create = true; + break; + case Xop_tkn_itm_.Tid_tblw_tr: // noop:
    ->
    ; should probably auto-create td, but MW does not; DATE:2014-03-18 + case Xop_tkn_itm_.Tid_tblw_tc: // noop;
    ; TIDY:was
    ; PAGE: es.w:Savilla DATE:2014-06-29 + break; + } + if (ignore_prv) { + ctx.Subs_add(root, tkn_mkr.Ignore(bgn_pos, cur_pos, Xop_ignore_tkn.Ignore_tid_htmlTidy_tblw)); + ++tblw_te_ignore_count; + cur_pos = Bry_finder.Find_fwd_until(src, cur_pos, src_len, Byte_ascii.NewLine); // NOTE: minor hack; this tblw tkn will be ignored, so ignore any of its attributes as well; gobble up all chars till nl. see: if two consecutive tbs, ignore attributes on 2nd; en.wikibooks.org/wiki/Wikibooks:Featured books + return cur_pos; + } + if (auto_create) { + ctx.Subs_add_and_stack_tblw(root, prv_tkn, tkn_mkr.Tblw_tr(bgn_pos, bgn_pos, tbl_is_xml, true)); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, tkn_mkr.Tblw_td(bgn_pos, bgn_pos, tbl_is_xml)); + } + Xop_tblw_tb_tkn tb_tkn = tkn_mkr.Tblw_tb(bgn_pos, cur_pos, tbl_is_xml, false); + new_tkn = tb_tkn; + break; + case Tblw_type_tr: // + switch (prv_tid) { + case Xop_tkn_itm_.Tid_tblw_tb: break; // noop;
    + case Xop_tkn_itm_.Tid_tblw_tc: // fix; -> + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tc), true, bgn_pos, bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_td: // fix; -> + case Xop_tkn_itm_.Tid_tblw_th: // fix; -> + if (!tbl_is_xml) + ctx.Para().Process_nl(ctx, root, src, bgn_pos, bgn_pos + 1); // simulate "\n"; 2012-12-08 + int stack_pos = ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tr); + if (stack_pos != Xop_ctx.Stack_not_found) // don't pop if none found; EX:en.w:Turks_in_Denmark DATE:2014-03-02 + ctx.Stack_pop_til(root, src, stack_pos, true, bgn_pos, bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_tr: // fix; -> + if (prv_tkn.Tblw_subs_len() == 0) { // NOTE: set prv_row to ignore, but do not pop; see Tr_dupe_xnde and [[Jupiter]]; only invoke if same type; EX: but not |-; DATE:2013-12-09 + Xop_tkn_itm prv_row = ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tr), false, bgn_pos, bgn_pos); + prv_row.Ignore_y_(); + } + else + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tr), true, bgn_pos, bgn_pos); + break; + } + Xop_tblw_tr_tkn tr_tkn = tkn_mkr.Tblw_tr(bgn_pos, cur_pos, tbl_is_xml, false); + new_tkn = tr_tkn; + break; + case Tblw_type_td: //
    + case Tblw_type_td2: + boolean create_th = false; + switch (prv_tid) { + case Xop_tkn_itm_.Tid_tblw_tr: break; // noop;
    + case Xop_tkn_itm_.Tid_tblw_td: // fix; -> + if (!tbl_is_xml) // only for "\n|" not + ctx.Para().Process_nl(ctx, root, src, bgn_pos, bgn_pos + 1); // simulate "\n"; DATE:2014-02-20; ru.w:;[[Help:Download]]; DATE:2014-02-20 + ctx.Para().Process_block__bgn_y__end_n(Xop_xnde_tag_.Tag_td); // + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(prv_tid), true, bgn_pos, bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_th: // fix; -> + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(prv_tid), true, bgn_pos, bgn_pos); + if (wlxr_type == Tblw_type_td2) create_th = true; // !a||b -> ; but !a|b -> + break; + case Xop_tkn_itm_.Tid_tblw_tb: // fix;
    ->
    + if (wlxr_type == Tblw_type_td2) { // NOTE: ignore || if preceded by {|; {|a||b\n + prv_tkn.Atrs_rng_set(-1, -1); // reset atrs_bgn; remainder of line will become part of tb atr + return cur_pos; + } + else { + new_tkn = tkn_mkr.Tblw_tr(bgn_pos, cur_pos, tbl_is_xml, true); + new_tkn.Atrs_rng_set(bgn_pos, bgn_pos); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, new_tkn); + prv_tid = new_tkn.Tkn_tid(); + } + break; + case Xop_tkn_itm_.Tid_tblw_tc: // fix;
    ->
    + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tc), true, bgn_pos, bgn_pos); + new_tkn = tkn_mkr.Tblw_tr(bgn_pos, cur_pos, tbl_is_xml, true); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, new_tkn); + prv_tid = new_tkn.Tkn_tid(); + break; + } +// if (prv_tid == Xop_tkn_itm_.Tid_xnde) +// ctx.Stack_autoClose(root, src, prv_tkn, prv_tkn.Src_bgn(), prv_tkn.Src_end()); + if (create_th) new_tkn = tkn_mkr.Tblw_th(bgn_pos, cur_pos, tbl_is_xml); + else new_tkn = tkn_mkr.Tblw_td(bgn_pos, cur_pos, tbl_is_xml); + cell_pipe_seen = false; + break; + case Tblw_type_th: // + case Tblw_type_th2: + switch (prv_tid) { + case Xop_tkn_itm_.Tid_tblw_tr: break; // noop;
    + case Xop_tkn_itm_.Tid_tblw_th: // fix; -> + if (tbl_is_xml // tbl_is_xml always closes previous token + || (wlxr_type == Tblw_type_th2 || wlxr_type == Tblw_type_th)) // ! always closes; EX: "! !!"; "!! !!"; REMOVE: 2012-05-07; had (&& !ws_enabled) but caused "\n !" to fail; guard is no longer necessary since tblw_ws changed... + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(prv_tid), true, bgn_pos, bgn_pos); + else { + ctx.Subs_add(root, tkn_mkr.Txt(bgn_pos, cur_pos)); + return cur_pos; + } + break; + case Xop_tkn_itm_.Tid_tblw_td: // fix; -> NOTE: common use of using after for formatting + if (tbl_is_xml // tbl_is_xml always closes previous token + || (wlxr_type == Tblw_type_th)) // "| !" closes; "| !!" does not; + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(prv_tid), true, bgn_pos, bgn_pos); + else { + ctx.Subs_add(root, tkn_mkr.Txt(bgn_pos, cur_pos)); + return cur_pos; + } + break; + case Xop_tkn_itm_.Tid_tblw_tb: // fix;
    ->
    + ctx.Subs_add_and_stack_tblw(root, prv_tkn, tkn_mkr.Tblw_tr(bgn_pos, cur_pos, tbl_is_xml, true)); + break; + case Xop_tkn_itm_.Tid_tblw_tc: // fix;
    ->
    + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tc), true, bgn_pos, bgn_pos); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, tkn_mkr.Tblw_tr(bgn_pos, cur_pos, tbl_is_xml, true)); + break; + } + new_tkn = tkn_mkr.Tblw_th(bgn_pos, cur_pos, tbl_is_xml); + cell_pipe_seen = false; + break; + case Tblw_type_tc: //
    + switch (prv_tid) { + case Xop_tkn_itm_.Tid_tblw_tb: break; // noop; in order to close
    + case Xop_tkn_itm_.Tid_tblw_tr: // fix;
    ->
    TODO: caption should be ignored and placed in quarantine + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tr), true, bgn_pos, bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_td: // fix;
    ->
    + case Xop_tkn_itm_.Tid_tblw_th: // fix;
    ->
    + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tr), true, bgn_pos, bgn_pos); // NOTE: closing
    / + ctx.Msg_log().Add_itm_none(Xop_tblw_log.Caption_after_td, src, prv_tkn.Src_bgn(), bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_tc: // fix;
    -> + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tc), true, bgn_pos, bgn_pos); + ctx.Msg_log().Add_itm_none(Xop_tblw_log.Caption_after_tc, src, prv_tkn.Src_bgn(), bgn_pos); + break; + } + new_tkn = tkn_mkr.Tblw_tc(bgn_pos, cur_pos, tbl_is_xml); + Xop_tblw_tb_tkn tblw_tb_tkn = (Xop_tblw_tb_tkn)ctx.Stack_get_typ(Xop_tkn_itm_.Tid_tblw_tb); + tblw_tb_tkn.Caption_count_add_1(); // NOTE: null check is not necessary (impossible to have a caption without a tblw); DATE:2013-12-20 + cell_pipe_seen = false; // NOTE: always mark !seen; see Atrs_tc() + break; + } + ctx.Subs_add_and_stack_tblw(root, prv_tkn, new_tkn); + if (atrs_bgn > Xop_tblw_wkr.Atrs_ignore_check) { + new_tkn.Atrs_rng_set(atrs_bgn, atrs_end); + if (ctx.Parse_tid() == Xop_parser_.Parse_tid_page_wiki) { + Xop_xatr_itm[] atrs = ctx.App().Xatr_parser().Parse(ctx.Msg_log(), src, atrs_bgn, atrs_end); + new_tkn.Atrs_ary_as_tblw_(atrs); + } + } + switch (wlxr_type) { + case Tblw_type_tb: + case Tblw_type_tr: + ctx.Para().Process_block__bgn_y__end_n(Xop_xnde_tag_.Tag_tr); + break; + case Tblw_type_td: + case Tblw_type_th: + ctx.Para().Process_block__bgn_n__end_y(Xop_xnde_tag_.Tag_td); + break; + } + return cur_pos; + } + public int Make_tkn_end(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, int typeId, byte wlxr_type, Xop_tblw_tkn prv_tkn, int prv_tid, boolean tbl_is_xml) { + if (!tbl_is_xml) // only for "\n|}" not
    + ctx.Para().Process_nl(ctx, root, src, bgn_pos, bgn_pos + 1); // simulate "\n"; process para (which will create paras for cells) 2012-12-08 + if (tbl_is_xml && typeId == Xop_tkn_itm_.Tid_tblw_tb // tblx:
    + && prv_tkn != null && !prv_tkn.Tblw_xml()) { // tblw is prv_tkn + ++tblw_te_ignore_count; // suppress subsequent occurrences of "|}"; EX:ru.q:Авель; DATE:2014-02-22 + } + Ignore_ws(ctx, root); + if (wlxr_type == Tblw_type_te) { + switch (prv_tid) { + case Xop_tkn_itm_.Tid_tblw_td: // fix;
    ->
    + case Xop_tkn_itm_.Tid_tblw_th: // fix;
    ->
    + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tr), true, bgn_pos, bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_tc: // fix;
    ->
    + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tc), true, bgn_pos, bgn_pos); + break; + case Xop_tkn_itm_.Tid_tblw_tr: // fix;
    ->
    : tr but no tds; remove tr + boolean blank = true; + for (int j = prv_tkn.Tkn_sub_idx() + 1; j < root.Subs_len(); j++) { + Xop_tkn_itm t = root.Subs_get(j); + switch (t.Tkn_tid()) { + case Xop_tkn_itm_.Tid_newLine: + case Xop_tkn_itm_.Tid_para: + break; + default: + blank = false; + j = root.Subs_len(); + break; + } + } + if (blank) + root.Subs_del_after(prv_tkn.Tkn_sub_idx()); + break; + case Xop_tkn_itm_.Tid_tblw_tb: // fix;
    ->
    + boolean has_subs = false; + for (int i = prv_tkn.Tkn_sub_idx() + 1; i < root.Subs_len(); i++) { + int cur_id = root.Subs_get(i).Tkn_tid(); + switch (cur_id) { + case Xop_tkn_itm_.Tid_tblw_tc: + case Xop_tkn_itm_.Tid_tblw_td: + case Xop_tkn_itm_.Tid_tblw_th: + case Xop_tkn_itm_.Tid_tblw_tr: + has_subs = true; + i = root.Subs_len(); + break; + } + } + if (!has_subs) { + Xop_tkn_itm new_tkn = tkn_mkr.Tblw_tr(bgn_pos, bgn_pos, tbl_is_xml, true); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, new_tkn); + new_tkn = tkn_mkr.Tblw_td(bgn_pos, bgn_pos, tbl_is_xml); + ctx.Subs_add_and_stack_tblw(root, prv_tkn, new_tkn); + ctx.Stack_pop_til(root, src, ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tb), true, bgn_pos, bgn_pos); + return cur_pos; + } + break; + } + int tb_idx = ctx.Stack_idx_typ(Xop_tkn_itm_.Tid_tblw_tb); + if (tb_idx == -1) return cur_pos; // NOTE: tb_idx can be -1 when called from Pipe in Tmpl mode + Xop_tblw_tb_tkn tb = (Xop_tblw_tb_tkn)ctx.Stack_pop_til(root, src, tb_idx, false, bgn_pos, bgn_pos); // NOTE: need to pop manually in order to set all intermediate node ends to bgn_pos, but tb ent to cur_pos; EX: for stack of "tb,tr,td" tr and td get End_() of bgn_pos but tb gets End_() of cur_pos + tb.Subs_move(root); + tb.Src_end_(cur_pos); + ctx.Para().Process_block__bgn_n__end_y(Xop_xnde_tag_.Tag_table); // NOTE: must clear block state that was started by
    or + && ( (prv_tid == Xop_tkn_itm_.Tid_tblw_td && typeId == Xop_tkn_itm_.Tid_tblw_th) + || (prv_tid == Xop_tkn_itm_.Tid_tblw_th && typeId == Xop_tkn_itm_.Tid_tblw_td) + ) + ) + acs_typeId = prv_tid; + + int acs_pos = -1, acs_len = ctx.Stack_len(); + for (int i = acs_len - 1; i > -1; i--) { // find auto-close pos + byte cur_acs_tid = ctx.Stack_get(i).Tkn_tid(); + switch (acs_typeId) { + case Xop_tkn_itm_.Tid_tblw_tb: // if
    , match only; note that it needs to be handled separately b/c of tb logic below + if (acs_typeId == cur_acs_tid) { + acs_pos = i; + i = -1; // force break; + } + break; + default: // if , match but stop at
    ; do not allow to close outside
    + if (cur_acs_tid == Xop_tkn_itm_.Tid_tblw_tb) //
    ; do not allow to close any 's above
    ; EX:w:Enthalpy_of_fusion; {{States of matter}} + i = -1; // this will skip acs_pos != -1 below and discard token + else if (cur_acs_tid == acs_typeId) { // matches + acs_pos = i; + i = -1; // force break + } + break; + } + } + if (acs_pos != -1) { + Xop_tblw_tkn bgn_tkn = (Xop_tblw_tkn)ctx.Stack_pop_til(root, src, acs_pos, false, bgn_pos, cur_pos); + switch (wlxr_type) { + case Tblw_type_tb: + ctx.Para().Process_block__bgn_n__end_y(Xop_xnde_tag_.Tag_table); + break; + case Tblw_type_td: + case Tblw_type_th: + ctx.Para().Process_block__bgn_y__end_n(Xop_xnde_tag_.Tag_td); + break; + } + bgn_tkn.Subs_move(root); + bgn_tkn.Src_end_(cur_pos); + } + return cur_pos; + } + public static void Atrs_close(Xop_ctx ctx, byte[] src, Xop_root_tkn root) { + Xop_tblw_tkn prv_tkn = ctx.Stack_get_tbl(); + if (prv_tkn == null || prv_tkn.Tblw_xml()) return; // no tblw or tblw_xnde (which does not have tblw atrs) + switch (prv_tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_tblw_tb: case Xop_tkn_itm_.Tid_tblw_tr: // only tb and tr have tblw atrs (EX: "{|id=1\n"); td/th use pipes for atrs (EX: "|id=1|a"); tc has no atrs; te is never on stack + Xop_tblw_wkr.Atrs_make(ctx, src, root, ctx.Tblw(), prv_tkn); + break; + } + } + public static boolean Atrs_make(Xop_ctx ctx, byte[] src, Xop_root_tkn root, Xop_tblw_wkr wkr, Xop_tblw_tkn prv_tblw) { + if (prv_tblw.Atrs_bgn() != Xop_tblw_wkr.Atrs_null) return false; // atr_bgn/end is empty or already has explicit value; ignore; + int subs_bgn = prv_tblw.Tkn_sub_idx() + 1, subs_end = root.Subs_len() - 1; + int subs_pos = subs_bgn; + Xop_tkn_itm last_atr_tkn = null; + boolean loop = true; + while (loop) { // loop over tkns after prv_tkn to find last_atr_tkn + if (subs_pos > subs_end) break; + Xop_tkn_itm tmp_tkn = root.Subs_get(subs_pos); + switch (tmp_tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_newLine: // nl stops; EX: "{| a b c \nd"; bgn at {| and pick up " a b c " as atrs + case Xop_tkn_itm_.Tid_hdr: case Xop_tkn_itm_.Tid_hr: // hdr/hr incorporate nl into tkn so include these as well; EX: "{|a\n==b==" becomes tblw,txt,hdr (note that \n is part of hdr + case Xop_tkn_itm_.Tid_list: // list stops; EX: "{| a b c\n* d"; "*d" ends atrs; EX: ru.d: DATE:2014-02-22 + loop = false; + break; + default: + ++subs_pos; + last_atr_tkn = tmp_tkn; + break; + } + } + if (last_atr_tkn == null) { // no atrs found; mark tblw_tkn as Atrs_empty + prv_tblw.Atrs_rng_set(Xop_tblw_wkr.Atrs_empty, Xop_tblw_wkr.Atrs_empty); + return false; + } + root.Subs_del_between(ctx, subs_bgn, subs_pos); + int atrs_bgn = prv_tblw.Src_end(), atrs_end = last_atr_tkn.Src_end(); + if (prv_tblw.Tkn_tid() == Xop_tkn_itm_.Tid_tblw_tr) // NOTE: if "|-" gobble all trailing dashes; REF: Parser.php!doTableStuff; $line = preg_replace( '#^\|-+#', '', $line ); DATE:2013-06-21 + atrs_bgn = Bry_finder.Find_fwd_while(src, atrs_bgn, src.length, Byte_ascii.Dash); + prv_tblw.Atrs_rng_set(atrs_bgn, atrs_end); + if (ctx.Parse_tid() == Xop_parser_.Parse_tid_page_wiki && atrs_bgn != -1) { + Xop_xatr_itm[] atrs = ctx.App().Xatr_parser().Parse(ctx.Msg_log(), src, atrs_bgn, atrs_end); + prv_tblw.Atrs_ary_as_tblw_(atrs); + } + wkr.Cell_pipe_seen_(true); + return true; + } + private void Ignore_ws(Xop_ctx ctx, Xop_root_tkn root) { + int end = root.Subs_len() - 1; + // get last tr, tc, tb; cannot use ctx.Stack_get_tblw b/c this gets last open tblw, and we want last tblw; EX: "
    "; Stack_get_tblw gets
    want + boolean found = false; + Xop_tkn_itm prv_tkn = null; + for (int i = end; i > -1; i--) { + prv_tkn = root.Subs_get(i); + switch (prv_tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_tblw_tr: + case Xop_tkn_itm_.Tid_tblw_tc: + case Xop_tkn_itm_.Tid_tblw_tb: + found = true; + i = -1; + break; + case Xop_tkn_itm_.Tid_tblw_td: // exclude td + case Xop_tkn_itm_.Tid_tblw_th: // exclude th + i = -1; + break; + } + } + if (!found) return; + int bgn = prv_tkn.Tkn_sub_idx() + 1; + int rv = Ignore_ws_rng(ctx, root, bgn, end, true); + if (rv == -1) return; // entire range is ws; don't bother trimming end + Ignore_ws_rng(ctx, root, end, bgn, false); + } + private int Ignore_ws_rng(Xop_ctx ctx, Xop_root_tkn root, int bgn, int end, boolean fwd) { + int cur = bgn, adj = fwd ? 1 : -1; + while (true) { + if (fwd) { + if (cur > end) return -1; + } + else { + if (cur < end) return -1; + } + Xop_tkn_itm ws_tkn = root.Subs_get(cur); + switch (ws_tkn.Tkn_tid()) { + case Xop_tkn_itm_.Tid_space: case Xop_tkn_itm_.Tid_tab: case Xop_tkn_itm_.Tid_newLine: + case Xop_tkn_itm_.Tid_para: + ws_tkn.Ignore_y_grp_(ctx, root, cur); + break; + case Xop_tkn_itm_.Tid_xnde: + if (ws_tkn.Src_bgn() == ws_tkn.Src_end() // NOTE: para_wkr inserts
    . these should be disabled in Ignore_ws_rng; they are identified as having bgn == end; normal
    s will have bgn < end + && ((Xop_xnde_tkn)ws_tkn).Tag().Id() == Xop_xnde_tag_.Tid_br) + ws_tkn.Ignore_y_grp_(ctx, root, cur); + break; + default: + return cur; + } + cur += adj; + } + } + public static int Handle_false_tblw_match(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int bgn_pos, int cur_pos, Xop_tkn_itm tkn, boolean add_nl) { + if (add_nl) + ctx.Para().Process_nl(ctx, root, src, bgn_pos, cur_pos); + ctx.Subs_add(root, tkn); + return cur_pos; + } + public static final int Atrs_null = -1, Atrs_empty = -2, Atrs_ignore_check = -1; + public static final byte Tblw_type_tb = 0, Tblw_type_te = 1, Tblw_type_tr = 2, Tblw_type_td = 3, Tblw_type_th = 4, Tblw_type_tc = 5, Tblw_type_td2 = 6, Tblw_type_th2 = 7; +} +/* +NOTE_1: +Code tries to emulate HTML tidy behavior. Specifically: +- ignore
    when directly under
    +- if tblw, scan to end of line to ignore attributes +- ignore any closing tblws +EX: +{|id=1 +{|id=2 <- ignore id=2 +|} +|} +*/ \ No newline at end of file diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_basic_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_basic_tst.java new file mode 100644 index 000000000..a02f50b01 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_basic_tst.java @@ -0,0 +1,1061 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_basic_tst { + private Xop_fxt fxt = new Xop_fxt(); + @Test public void Td() { // Tb_tr_td_te + fxt.Test_parse_page_wiki("{|\n|-\n|a\n|}" + , fxt.tkn_tblw_tb_(0, 11).Subs_ + ( fxt.tkn_tblw_tr_(2, 8).Subs_ + ( fxt.tkn_tblw_td_(5, 8).Subs_(fxt.tkn_txt_(7, 8), fxt.tkn_para_blank_(9)))) + ); + } + @Test public void Td2() { // Tb_tr_td_td2_te + fxt.Test_parse_page_wiki("{|\n|-\n|a||b\n|}" + , fxt.tkn_tblw_tb_(0, 14).Subs_ + ( fxt.tkn_tblw_tr_(2, 11).Subs_ + ( fxt.tkn_tblw_td_(5, 8).Subs_(fxt.tkn_txt_( 7, 8), fxt.tkn_para_blank_(9)) + , fxt.tkn_tblw_td_(8, 11).Subs_(fxt.tkn_txt_(10, 11), fxt.tkn_para_blank_(12)) + ))); + } + @Test public void Tc() { // Tb_tc_te + fxt.Test_parse_page_wiki("{|\n|+a\n|}" + , fxt.tkn_tblw_tb_(0, 9).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tc_(2, 6).Subs_ + ( fxt.tkn_txt_(5, 6) + , fxt.tkn_para_blank_(7) + ) + ) + ); + } + @Test public void Tc_longer() { // Tb_tc_tr_td_te + fxt.Test_parse_page_wiki("{|\n|+a\n|-\n|b\n|}" + , fxt.tkn_tblw_tb_(0, 15).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tc_(2, 6).Subs_(fxt.tkn_txt_(5, 6)) + , fxt.tkn_tblw_tr_(6, 12).Subs_ + ( fxt.tkn_tblw_td_(9, 12).Subs_(fxt.tkn_txt_(11, 12), fxt.tkn_para_blank_(13)) + ) + )); + } + @Test public void Th() { // Tb_th_te + fxt.Test_parse_page_wiki("{|\n|-\n!a\n|}" + , fxt.tkn_tblw_tb_(0, 11).Subs_ + ( fxt.tkn_tblw_tr_(2, 8).Subs_ + ( fxt.tkn_tblw_th_(5, 8).Subs_(fxt.tkn_txt_(7, 8), fxt.tkn_para_blank_(9)) + ))); + } + @Test public void Th2() { // Tb_th_th2_te + fxt.Test_parse_page_wiki("{|\n|-\n!a!!b\n|}" + , fxt.tkn_tblw_tb_(0, 14).Subs_ + ( fxt.tkn_tblw_tr_(2, 11).Subs_ + ( fxt.tkn_tblw_th_(5, 8).Subs_(fxt.tkn_txt_( 7, 8)) + , fxt.tkn_tblw_th_(8, 11).Subs_(fxt.tkn_txt_(10, 11), fxt.tkn_para_blank_(12)) + ))); + } + @Test public void Th2_td_syntax() { // Tb_th_td; || should be treated as th + fxt.Test_parse_page_wiki("{|\n|-\n!a||b\n|}" + , fxt.tkn_tblw_tb_(0, 14).Subs_ + ( fxt.tkn_tblw_tr_(2, 11).Subs_ + ( fxt.tkn_tblw_th_(5, 8).Subs_(fxt.tkn_txt_( 7, 8)) + , fxt.tkn_tblw_th_(8, 11).Subs_(fxt.tkn_txt_(10, 11), fxt.tkn_para_blank_(12)) + ))); + } + @Test public void Tb_td2() { // EX.WP: Hectare; {| class="wikitable" || style="border: 1px solid #FFFFFF;" + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|id='1' || class='a'" + , "|-" + , "|a" + , "|}") + , String_.Concat_lines_nl_skip_last + ( "
    " + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + } + @Test public void Atrs_tr() { // Tb_tr_td_te + fxt.Test_parse_page_wiki("{|\n|-style='a'\n|b\n|}" + , fxt.tkn_tblw_tb_(0, 20).Subs_ + ( fxt.tkn_tblw_tr_(2, 17).Atrs_rng_(5, 14).Subs_ + ( fxt.tkn_tblw_td_(14, 17).Subs_(fxt.tkn_txt_(16, 17), fxt.tkn_para_blank_(18)) + )) + ); + } + @Test public void Atrs_td() { // Tb_tr_td_te + fxt.Test_parse_page_wiki("{|\n|-\n|style='a'|b\n|}" + , fxt.tkn_tblw_tb_(0, 21).Subs_ + ( fxt.tkn_tblw_tr_(2, 18).Subs_ + ( fxt.tkn_tblw_td_(5, 18).Atrs_rng_(7, 16).Subs_(fxt.tkn_txt_(17, 18), fxt.tkn_para_blank_(19)) + )) + ); + } + @Test public void Atrs_td_mult() { + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , " {|" + , " |-" + , " | id='1'|" + , " | id='2'|a" + , " | id='3'|" + , " |}" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    " + , " a" + , " " + , "
    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Atrs_tc() { // REF:WP:[[1920 Palm Sunday tornado outbreak]] + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|id='1'" + , "|+id='2'|a" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , "
    a" + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Atrs_td_mixed() { // Tb_tr_td_td2_te + fxt.Test_parse_page_wiki("{|\n|-\n|style='a'|b||c\n|}" + , fxt.tkn_tblw_tb_(0, 24).Subs_ + ( fxt.tkn_tblw_tr_(2, 21).Subs_ + ( fxt.tkn_tblw_td_( 5, 18).Atrs_rng_(7, 16).Subs_(fxt.tkn_txt_(17, 18), fxt.tkn_para_blank_(19)) + , fxt.tkn_tblw_td_(18, 21).Subs_(fxt.tkn_txt_(20, 21), fxt.tkn_para_blank_(22)) + )) + ); + } + @Test public void Atrs_th() { + fxt.Test_parse_page_wiki("{|\n|-\n!style='a'|b\n|}" + , fxt.tkn_tblw_tb_(0, 21).Subs_ + ( fxt.tkn_tblw_tr_(2, 18).Subs_ + ( fxt.tkn_tblw_th_(5, 18).Atrs_rng_(7, 16).Subs_(fxt.tkn_txt_(17, 18), fxt.tkn_para_blank_(19)) + )) + ); + } + @Test public void Atrs_th_cap() { + fxt.Test_parse_page_wiki("{|\n|+b\n!style='a'|b\n|}" + , fxt.tkn_tblw_tb_(0, 22).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tc_(2, 6).Subs_(fxt.tkn_txt_( 5, 6)) + , fxt.tkn_tblw_tr_(6, 19).Subs_ + ( fxt.tkn_tblw_th_(6, 19).Atrs_rng_(8, 17).Subs_(fxt.tkn_txt_(18, 19), fxt.tkn_para_blank_(20)) + ) + )); + } + @Test public void Atrs_skip_hdr() { + fxt.Test_parse_page_wiki("{|\n|+b\n!style='a'|b\n|}" + , fxt.tkn_tblw_tb_(0, 22).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tc_(2, 6).Subs_(fxt.tkn_txt_( 5, 6)) + , fxt.tkn_tblw_tr_(6, 19).Subs_ + ( fxt.tkn_tblw_th_(6, 19).Atrs_rng_(8, 17).Subs_(fxt.tkn_txt_(18, 19), fxt.tkn_para_blank_(20)) + ) + )); + } + @Test public void Atrs_td_bg_color() { // PURPOSE: atr_parser should treat # as valid character in unquoted val; EX.WP:UTF8; |bgcolor=#eeeeee|Indic
    0800*
    '''''224''''' + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|bgcolor=#eeeeee|a" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void AutoClose_td_when_new_tr() { // retain; needed for de.w:Main_Page; DATE:2013-12-09 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "==a==" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , "" + , "

    a

    " // NOTE: malformed html matches MW + , " " + , " " + , " " + , "
    " + , "
    " + , "" + ) + ); + fxt.Test_parse_page_wiki("{|\n==b==\n|}" + , fxt.tkn_tblw_tb_(0, 8).Subs_ + ( fxt.tkn_hdr_(2, 8, 2).Subs_ + ( fxt.tkn_txt_(5, 6) + ) + , fxt.tkn_para_blank_(9) + , fxt.tkn_tblw_tr_(8, 8).Subs_ + ( fxt.tkn_tblw_td_( 8, 8)) + ) + ); + } + @Test public void Atr_close() { // PURPOSE: issue with
    ; EX.WP: 20th century; {{Decades and years}} + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "
    a" + , "{|id=1" + , "|-" + , "|b" + , "|}
    " + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , " " + , " " + , " " + , " " + , "
    b" + , "
    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Err_row_empty() { + fxt.Test_parse_page_wiki("{|\n|-\n|-\n|a\n|}" + , fxt.tkn_tblw_tb_(0, 14).Subs_ + ( fxt.tkn_tblw_tr_(2, 5) + , fxt.tkn_tblw_tr_(5, 11).Subs_ + ( fxt.tkn_tblw_td_(8, 11).Subs_(fxt.tkn_txt_(10, 11), fxt.tkn_para_blank_(12)) + )) + ); + } + @Test public void Err_row_trailing() { + fxt.Test_parse_page_wiki("{|\n|-\n|a\n|-\n|}" + , fxt.tkn_tblw_tb_(0, 14).Subs_ + ( fxt.tkn_tblw_tr_(2, 8).Subs_ + ( fxt.tkn_tblw_td_(5, 8).Subs_(fxt.tkn_txt_(7, 8), fxt.tkn_para_blank_(9)) + )) + ); + } + @Test public void Err_caption_after_tr() { + fxt.Test_parse_page_wiki("{|\n|-\n|+a\n|}" + , fxt.tkn_tblw_tb_(0, 12).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tr_(2, 5) + , fxt.tkn_tblw_tc_(5, 9).Subs_(fxt.tkn_txt_(8, 9), fxt.tkn_para_blank_(10))) + ); + } + @Test public void Err_caption_after_td() { + fxt.Init_log_(Xop_tblw_log.Caption_after_td).Test_parse_page_wiki("{|\n|-\n|a\n|+b\n|}" + , fxt.tkn_tblw_tb_(0, 15).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tr_(2, 8).Subs_ + ( fxt.tkn_tblw_td_(5, 8).Subs_(fxt.tkn_txt_(7, 8))) + , fxt.tkn_tblw_tc_(8, 12).Subs_(fxt.tkn_txt_(11, 12), fxt.tkn_para_blank_(13))) + ); + } + @Test public void Err_caption_after_tc() { + fxt.Init_log_(Xop_tblw_log.Caption_after_tc).Test_parse_page_wiki("{|\n|+a\n|+b\n|}" + , fxt.tkn_tblw_tb_(0, 13).Caption_count_(2).Subs_ + ( fxt.tkn_tblw_tc_(2, 6).Subs_(fxt.tkn_txt_( 5, 6)) + , fxt.tkn_tblw_tc_(6, 10).Subs_(fxt.tkn_txt_( 9, 10), fxt.tkn_para_blank_(11))) + ); + } + @Test public void Err_row_auto_opened() { + fxt.Test_parse_page_wiki("{|\n|a\n|}" + , fxt.tkn_tblw_tb_(0, 8).Subs_ + ( fxt.tkn_tblw_tr_(2, 5).Subs_ + ( fxt.tkn_tblw_td_(2, 5).Subs_(fxt.tkn_txt_(4, 5), fxt.tkn_para_blank_(6)) + ))); + } + @Test public void Err_caption_auto_closed() { + fxt.Test_parse_page_wiki("{|\n|+a\n|b\n|}" + , fxt.tkn_tblw_tb_(0, 12).Caption_count_(1).Subs_ + ( fxt.tkn_tblw_tc_(2, 6).Subs_(fxt.tkn_txt_(5, 6)) + , fxt.tkn_tblw_tr_(6, 9).Subs_ + ( fxt.tkn_tblw_td_(6, 9).Subs_(fxt.tkn_txt_(8, 9),fxt.tkn_para_blank_(10)) + ))); + } + @Test public void Td_lnki() { + fxt.Test_parse_page_wiki("{|\n|-\n|[[a|b]]\n|}" + , fxt.tkn_tblw_tb_(0, 17).Subs_ + ( fxt.tkn_tblw_tr_(2, 14).Subs_ + ( fxt.tkn_tblw_td_(5, 14).Subs_(fxt.tkn_lnki_(7, 14), fxt.tkn_para_blank_(15)))) + ); + } + @Test public void Err_Atrs_dumped_into_text() { // PURPOSE: [[Prawn]] and {{Taxobox}} was dumping text + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|-id='a'" + , "|b" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    b" + , "
    " + , "" + ) + ); + } + @Test public void Tr_dupe_xnde() { // PURPOSE: redundant tr should not be dropped; see [[Jupiter]] + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "
    a
    " + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + ) + ); + } + @Test public void Tr_dupe_xnde_2() { //
    causes problems + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "
    a
    " + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + ) + ); + } + @Test public void Bang_should_not_make_cell_td_1_bang() { // PURPOSE: "| a! b" ! should not separate cell + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last("{|", "|-", "|a!b", "|}"), String_.Concat_lines_nl_skip_last("", " ", " ", " ", "
    a!b" , "
    ", "")); + } + @Test public void Bang_should_not_make_cell_td_2_bang() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last("{|", "|-", "|a!!b", "|}"), String_.Concat_lines_nl_skip_last("", " ", " ", " ", "
    a!!b" , "
    ", "")); + } + @Test public void Bang_should_not_make_cell_th_1_bang() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last("{|", "|-", "!a!b", "|}"), String_.Concat_lines_nl_skip_last("", " ", " ", " ", "
    a!b" , "
    ", "")); + } + @Test public void Bang_should_not_make_cell_th_2_bang() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last("{|", "|-", "!a!!b", "|}") + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    a" + , " b" + , "
    " + , "" + )); + } + @Test public void Bang_should_not_make_cell_th_mult_line() { // FIX: make sure code does not disable subsequent bangs + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last("{|", "|-", "!a", "!b", "|}") + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    a" + , " b" + , "
    " + , "" + )); + } + @Test public void Fix_extra_cell() { // PURPOSE: trim should not affect td; WP:Base32 + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "!id='1'|a" + , "|" + , "!id='2'|b" + , "|-" + , "|a1|| ||b1" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , " " + , " b" + , "
    a1" + , " " + , " b1" + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Nl_td() { // PURPOSE:

    inside does not get enclosed + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "" + , "" + , "" + , "" + , "
    " + , "" + , "" + , "a" + , "" + , "" + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    " + , "" + , "


    " + , "a" + , "

    " + , "" + , "


    " + , "

    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Trim_ws() { // PURPOSE: trim should be done from both sides + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "" + , "" + , "" + , "" + , "" + , "" + , "a" + , "" + , "" + , "
    " + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "a" + , "
    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Trim_ws_tr() { // PURPOSE: trim should be done from both sides + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "" + , "
    " + , "
    " + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , "
    " + , "
    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Trim_ws_td() { // PURPOSE: trim should not affect td + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "" + , "" + , "" + , "" + , "
    " + , "" + , "" + , "a" + , "" + , "" + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    " + , "" + , "


    " + , "a" + , "

    " + , "" + , "


    " + , "

    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Atr_xnde() { // PURPOSE: xnde should close any open xatrs; EX.WP: Western Front (World War I); stray > after == Dramatizations == + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|id='1'

    " + , "|a" + , "|}"), String_.Concat_lines_nl_skip_last + ( "

    " + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + } + @Test public void No_wiki_3() { + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|style='a[b]c'|d" + , "|}" + ), String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    d" + , "
    " + , "" + )); + } + @Test public void Trailing_tr_breaks_para_mode() {// PURPOSE.fix: empty trailing tr breaks para mode; EX:w:Sibelius + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|a" + , "|-" // causes lines below not to be put in paras + , "|}" + , "b" + , "" + , "c" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + , "

    b" + , "

    " + , "" + , "

    c" + , "

    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Blank_line_should_be_own_para() {// PURPOSE.fix: caption does not begin on own line; EX:w:Old St. Peter's Basilica + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|a" + , "b" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "" + , "

    b" + , "

    " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Blank_line_should_be_own_para_2() {// PURPOSE.fix: caption does not begin on own line; EX:w:Old St. Peter's Basilica + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|a" + , "b" + , "|-" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "" + , "

    b" + , "

    " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Bold_stops_at_table() { // PURPOSE: do not allow unclosed bold to extend over tables; + fxt.Test_parse_page_all_str("'''
    a
    ", String_.Concat_lines_nl_skip_last + ( "" + , "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + fxt.Init_defn_clear(); + } + @Test public void Orphaned_tr_breaks_nested_tables() { // PUPRPOSE: should not match outside scope; EX:w:Enthalpy_of_fusion; {{States of matter}} + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "" + , "" + , "" + , "" + , "" + , "
    " + , "" + , "" + , "
    " + , "
    a" + , "
    " + ), + String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    " + , " " + , "
    " + , "
    a" + , "
    " + , "" + ) + ); + } + @Test public void Space_causes_extra_p() {// PURPOSE: "\n\s" should be equivalent to "\n"; EX: w:Earth + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "
    " + , "b" + , "
    c" + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    " + , "" + , "

    b" // used to close

    here;

    b

    + , "
    c" + , "

    " + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Br_should_not_be_ignored() {// PURPOSE: document
    's should not be ignored between tables; 20121226 + fxt.Init_para_y_(); + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|a" + , "|}" + , "
    " + , "{|" + , "|-" + , "|b" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "
    " // was being ignored + , "" + , " " + , " " + , " " + , "
    b" + , "
    " + , "" + )); + fxt.Init_para_n_(); + } + @Test public void Auto_create_table() {// PURPOSE: should create table; EX:w:Hatfield-McCoy_feud; DATE:20121226 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "a" + , "" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + } + @Test public void List_and_orphaned_td2_should_not_create_tblw() {// PURPOSE: !! was creating table; DATE:2013-04-28 + fxt.Test_parse_page_all_str("*a !! b", String_.Concat_lines_nl_skip_last + ( "
      " + , "
    • a !! b" + , "
    • " + , "
    " + )); + } + @Test public void Tr_trailing_dashes_should_be_stripped() {// PURPOSE: trailing dashes should be stripped; |--- -> |-; EX: |--style="x" was being ignored; DATE:2013-06-21 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-----style='a'" + , "|b" + , "|}" + ), String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    b" + , "
    " + )); + } + @Test public void Th_without_tr() { // PURPOSE: !! without preceding ! should not create table-cell; DATE:2013-12-18 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , "a!!b" + , "|}" + ), String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    " + , "a!!b" + , "
    " + )); + } + @Test public void Td_at_eos() {// PURPOSE.fix: !! at eos fails; EX:es.s:Si_mis_manos_pudieran_deshojar; DATE:2014-02-11 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "| !!" // note that "!!" is eos inside the src + , "|}" + ), String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    " + , "

    " + , "!!" + , "

    " + , "
    " + , "
    " + )); + } + @Test public void Tr_without_tb_should_start_tb() {// PURPOSE: orphaned tr should automatically start table; EX: pl.w:Portal:Technika; DATE:2014-02-13 + fxt.Test_parse_page_all_str("a" + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + )); + } + @Test public void Tblx_should_not_close_tblw() {// PURPOSE: should not close {|; EX:fr.w:Exp%C3%A9dition_Endurance; DATE:2014-02-13 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , "" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    " + , "
    " + )); + } + @Test public void Tblx_should_not_close_tblw_2() {// PURPOSE: should close {|; ignore latter |}; EX:ru.q:Авель; DATE:2014-02-22 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|a" + , "" + , "{|" + , "|-" + , "|b" + , "" + , "{|" + , "|-" + , "|c" + , "" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + , " " + , " " + , " " + , "
    b" + , "
    " + , "" + , " " + , " " + , " " + , "
    c" + , "
    " + )); + } + @Test public void Td_in_list_in_tblw_should_be_ignored() {// PURPOSE: || should be ignored if in list; EX:es.d:casa; DATE:2014-02-15 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , "* a || b" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    " + , "
      " + , "
    • a || b" + , "
    • " + , "
    " + , "
    " + )); + } + @Test public void List_in_tblw() {// PURPOSE: list should close previous cell; EX: ru.d:Викисловарь:Условные_сокращения; DATE:2014-02-22 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , "{|" + , "*a" + , "|}" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , "
    " + , " " + , "
      " // NOTE: this should probably be inside
    , but this matches MW behavior; DATE:2014-02-22 + , "
  • a" + , "
  • " + , " " + , " " + , " " + , " " + , "
    " + , "
    " + , "
    " + )); + } +} +// @Test public void Tb_under_tr_is_ignored() { // PURPOSE: table directly under tr is ignored; EX.WP:Category:Dessert stubs; TODO: complicated, especially to handle 2nd |} +// fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last +// ( "{|" +// , "|-id='a'" +// , "{|style='border:1px;'" +// , "|-id='b'" +// , "|b" +// , "|}" +// , "|}" +// ), String_.Concat_lines_nl_skip_last +// ( "" +// , " " +// , " " +// , " " +// , "
    b" +// , "
    " +// , "" +// )); +// } +// @Test public void Leading_ws() { // EX.WP:Corneal dystrophy (human) +// fxt.Test_parse_page_wiki(String_.Concat_lines_nl_skip_last +// ( " {|" +// , " |-" +// , " |a" +// , " |}" +// ) +// , fxt.tkn_tblw_tb_(1, 15).Subs_ +// ( fxt.tkn_tblw_tr_(3, 11).Subs_ +// ( fxt.tkn_tblw_td_(7, 11).Subs_ +// ( fxt.tkn_txt_()) +// ) +// ) +// ); +// } +// @Test public void Atrs_tb() { // Tb_te // FUTURE: reinstate; WHEN: Template +// fxt.Init_log_(Xop_tblw_log.Tbl_empty).Test_parse_page_wiki("{|style='a'\n|}" +// , fxt.tkn_tblw_tb_(0, 14).Atrs_rng_(2, 11).Subs_ +// ( fxt.tkn_tblw_tr_(11, 11).Subs_ +// ( fxt.tkn_tblw_td_(11, 11) +// ))); +// } +// @Test public void Td_p() { // PURPOSE:

    not being closed correctly +// fxt.Init_para_y_(); +// fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last +// ( "{|" +// , "|-" +// , "|" +// , "a" +// , "|}"), String_.Concat_lines_nl_skip_last +// ( "" +// , " " +// , " " +// , " " +// , "
    " +// , "" +// , "

    a" +// , "

    " +// , "
    " +// , "" +// )); +// fxt.Init_para_n_(); +// } +// @Test public void Tb_tb() { +// fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last +// ( "{|id='1'" +// , "{|id='2'" +// , "|-id='3'" +// , "|a" +// , "|}" +// , "|}"), String_.Concat_lines_nl_skip_last +// ( "" +// , " " +// , " " +// , " " +// , "
    a" +// , "
    " +// , "" +// )); +// } +// @Test public void Tb_tb_2() { +// fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last +// ( "{|id='1'" +// , "{|id='2' " +// , "|a" +// , "
    " +// , "|}" +// , "|}"), String_.Concat_lines_nl_skip_last +// ( "" +// , " " +// , " " +// , " " +// , "
    a" +// , "
    " +// , "" +// )); +// } diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_dangling_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_dangling_tst.java new file mode 100644 index 000000000..72ae09276 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_dangling_tst.java @@ -0,0 +1,56 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_dangling_tst { + @Before public void init() {fxt.Reset(); fxt.Init_para_y_();} private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void Dangling_tb_in_xnde() {// PURPOSE: dangling tblw incorrectly auto-closed by ; NOTE: this test is not correct; needs HTML tidy to close

    earlier; EX:w:Atlanta_Olympics; DATE:2014-03-18 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "
    " + , "{|" + , "|-" + , "|" + , "{|" + , "|-" + , "|a" + , "|}" + , "
    " + , "b" + ) + , String_.Concat_lines_nl + ( "
    " + , "" + , " " + , " " + , " " + , "
    " + , " " + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + , "

    b" + , "

    " + , "
    " + , "

    " + )); + } +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_double_pipe_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_double_pipe_tst.java new file mode 100644 index 000000000..6afc9d9ed --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_double_pipe_tst.java @@ -0,0 +1,109 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_double_pipe_tst { + @Before public void init() {fxt.Reset(); fxt.Init_para_y_();} private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void No_tblw() { // PURPOSE: if || has no tblw, treat as lnki; none; DATE:2014-05-06 + fxt.Test_parse_page_all_str("[[A||b|c]]", String_.Concat_lines_nl_skip_last + ( "

    b|c" // NOTE: technically this should be "|b|c", but difficult to implement; DATE:2014-05-06 + , "

    " + , "" + )); + } + @Test public void Image_map() {// PURPOSE: if || is inside table and imagemap, treat as lnki; EX:w:United_States_presidential_election,_1992; DATE:2014-03-29; DATE:2014-05-06 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "| z" + , "" + , "File:A.png||123px|b" // NOTE: "||" should not be tblw; also should not be pipe + text; if it is pipe + text, then caption will be "|123px" and width will be -1; DATE:2014-05-06 + , "" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    z" + , "\"b\"" // NOTE: width must be 123, not 0 + , "
    " + , "" + ) + ); + } + @Test public void Tblw_lnki_nth() { // PURPOSE: if || is nth pipe, then treat as lnki; EX:en.w:Main_Page;de.w:Main_Page; DATE:2014-05-06 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|[[File:A.png|b||c]]" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    \"c\"" + , "
    " + , "" + ) + ); + } + @Test public void Tblw_lnki_list_1st() { // PURPOSE: if || is 1st pipe, but inside list, then treat as lnki; EX:w:Second_Boer_War; DATE:2014-05-05 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|" + , "*[[A||b]]" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    " + , "" + , "
      " + , "
    • b" // NOTE: technically this should be "|b", but difficult to implement; DATE:2014-05-06 + , "
    • " + , "
    " + , "
    " + , "" + ) + ); + } + @Test public void Tblw_lnki_double_bang() { // PURPOSE: do not treat !! as tblw; PAGE:en.w:Pink_(singer); DATE:2014-06-25 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|" + , "[[A!!b]]" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    " + , "" + , "

    A!!b" + , "

    " + , "
    " + , "" + ) + ); + } +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_nested_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_nested_tst.java new file mode 100644 index 000000000..77d32d374 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_nested_tst.java @@ -0,0 +1,200 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_nested_tst { + private Xop_fxt fxt = new Xop_fxt(); + @Test public void Basic() { + fxt.Test_parse_page_wiki(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , "{|" + , "|-" + , "|a" + , "|}" + , "|b" + , "|}" + ) + , fxt.tkn_tblw_tb_(0, 25).Subs_ + ( fxt.tkn_tblw_tr_(2, 22).Subs_ + ( fxt.tkn_tblw_td_(5, 19).Subs_ + ( fxt.tkn_tblw_tb_(7, 19).Subs_ + ( fxt.tkn_tblw_tr_(10, 16).Subs_ + ( fxt.tkn_tblw_td_(13, 16).Subs_(fxt.tkn_txt_(15, 16), fxt.tkn_para_blank_(17)) + ) + ) + , fxt.tkn_para_blank_(20) + ) + , fxt.tkn_tblw_td_(19, 22).Subs_(fxt.tkn_txt_(21, 22), fxt.tkn_para_blank_(23)) + ) + ) + ); + } + @Test public void Leading_ws() { + fxt.Init_para_y_(); + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|id='a'" + , "|-" + , "|a" + , "|-" + , "|id='b'|" + , " {|id='c'" + , " |-" + , " |d" + , " |}" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , "
    " + , " " + , " " + , " " + , " " + , "
    d" + , "
    " + , "
    " + , "" + ) + ); + fxt.Init_para_n_(); + } + @Test public void Tblx_tblw() { // PURPOSE: if followed by {|, ignore 2nd table; EX: en.b:Wikibooks:Featured_books; DATE:2014-02-08 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "
    " + , "{| cellspacing=\"0\"" + , "|a" + , "|}" + , "
    " + ), String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + )); + } + @Test public void Caption_and_tblw() { // TIDY: don't try to fix sequence; PAGE:es.w:Sevilla; DATE:2014-06-29 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|+" + , "{|" + , "|}" + , "|}"), String_.Concat_lines_nl_skip_last + ( "
    " + , " " + , "
    " + , " " + , " " + , " " + , " " + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Tb_tr_tb() { // PURPOSE: if , auto-create " + , "
    ; EX:w:Paris; DATE:2014-03-18 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "{|" + , "|}" + , "|}"), String_.Concat_lines_nl_skip_last + ( "" + , " " + , "
    " + , " " + , " " + , " " + , "
    " + , "
    " + , "
    " + , "" + )); + } + @Test public void Tblw_tblx_tblw_fails() { // PURPOSE: {| -> -> \n| was not rendering as " + , "" + , " " + , " " + , "
    ; EX:en.w:Paris#Demographics; DATE:2014-03-18 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|a" + , "
    " + , "" + , "" + , "|c" + , "
    b
    " + , "|}" + ), String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , "
    " + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    b" + , "
    c" + , "
    " + , "
    " + )); + } +// @Test public void Nested_tbl_missing() { // PURPOSE: nested table not rendering properly; EX:ar.s:; DATE:2014-03-18 +// fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last +// ( "{|" +// , "|-" +// , "{|" +// , "|-" +// , "|}" +// , "| width='50%' | a" +// , "|}" +// ), String_.Concat_lines_nl_skip_last +// ( "" +// , " " +// , " " +// , " " +// , " " +// , "
    a" +// , " [[b|c" +// , "
    " +// , "" +// , "

    d" +// , "

    " +// )); +// } +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_para_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_para_tst.java new file mode 100644 index 000000000..c94cc25b9 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_para_tst.java @@ -0,0 +1,156 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_para_tst { + @Before public void init() {fxt.Reset(); fxt.Init_para_y_();} private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void Para() { // PURPOSE: para causing strange breaks; SEE:[[John F. Kennedy]] and "two Supreme Court appointments" + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "

    " + , "|a" + , "

    " + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "

    " + , " " + , " " + , " " + , "
    a" + , "

    " + , "
    " + , "" + ) + ); + } + @Test public void Nl() { // PURPOSE: para causing strange breaks; SEE:[[John F. Kennedy]] and "two Supreme Court appointments" + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "!a" + , "" + , "|-" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "
    " + , "" + ) + ); + } + @Test public void Unnecessary_para() { // PURPOSE: tblw causes unnecessary

    ; [[Help:Download]]; DATE:2014-02-20 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|" + , "a
    " + , "b" + , "|" + , "c
    " + , "d" + , "|}" + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    " + , "" + , "

    a
    " + , "b" + , "

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

    c
    " + , "d" + , "

    " + , "
    " + , "" + ) + ); + } + @Test public void Ws_leading() { // EX.WP: AGPLv3 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , " !a" + , " !b" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    a" + , " b" + , "
    " + , "" + ) + ); + } + @Test public void Ws_th_2() { // "\n\s!" should still be interpreted as tblw; s.w:Manchester; DATE:2014-02-14 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|!style='color:red'|a" + , " !style=\"color:blue\"|b" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    a" + , " b" + , "
    " + , "" + ) + ); + } + @Test public void Ws_th_3() { // "\n\s!" and "!!" breaks tblw; ru.w:Храмы_Санкт-Петербурга (List of churches in St Petersburg); DATE:2014-02-20 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , " ! id='1' | a !! id='2' | b" + , "|}" + ) + , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    a " + , " b" + , "
    " + , "" + ) + ); + } + @Test public void Tblw_td2_should_not_create_ws() { // PURPOSE: a||b -> a\n||b; EX:none;discovered during luaj test; DATE:2014-04-14 + fxt.Test_parse_page_wiki_str("a||b", "

    a||b\n

    "); + } +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_tblx_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_tblx_tst.java new file mode 100644 index 000000000..c6a2bfcca --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_tblx_tst.java @@ -0,0 +1,71 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_tblx_tst { + @Before public void init() {fxt.Reset(); fxt.Init_para_y_();} private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void Ignore_td() { // PURPOSE: do not parse pipe as td if in ; EX:ru.w:Сочи; DATE:2014-02-22 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "
    " + , " " + , " " + , " " + , "
    a" + , "| b" + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "| b" + , "
    " + , "" + ) + ); + } + @Test public void Ignore_tr() { // PURPOSE: do not parse "\n|-", "\n!" if in ; EX:s.w:Uranus; DATE:2014-05-05 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "
    " + , " " + , " " + , " " + , "
    a" + , "|-" + , "! b" + , "| c" + , "
    " + ) , String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , "
    a" + , "" + , "

    |-" + , "! b" + , "| c" + , "

    " + , "
    " + , "" + ) + ); + } +} diff --git a/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_uncommon_tst.java b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_uncommon_tst.java new file mode 100644 index 000000000..35f282e62 --- /dev/null +++ b/400_xowa/src_480_tblw/gplx/xowa/Xop_tblw_wkr_uncommon_tst.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +import org.junit.*; +public class Xop_tblw_wkr_uncommon_tst { + @Before public void init() {fxt.Reset(); fxt.Init_para_y_();} private Xop_fxt fxt = new Xop_fxt(); + @After public void term() {fxt.Init_para_n_();} + @Test public void Tr_pops_entire_stack() { // PURPOSE: in strange cases, tr will pop entire stack; EX:en.w:Turks_in_Denmark; DATE:2014-03-02 + fxt.Test_parse_page_all_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "
    a" + , "|b" + , "|-" + , "|c" + , "|}" + ) + , String_.Concat_lines_nl + ( "" + , " " + , " " + , " " + , " " + , " " + , " " + , " " + , "
    a" + , "
    b" + , "
    c" + , "
    " + )); + } + @Test public void Atrs_defect() { // PURPOSE: < in atrs was causing premature termination; EX:en.w:Wikipedia:List of hoaxes on Wikipedia + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|id=\"a" + , "
    a" + , "
    " + , "" + )); + } + @Test public void Broken_lnki() { // PURPOSE: broken lnki was not closing table properly; EX:en.w:Wikipedia:Changing_attribution_for_an_edit; DATE:2014-03-16 + fxt.Test_parse_page_wiki_str(String_.Concat_lines_nl_skip_last + ( "{|" + , "|-" + , "|a" + , "|[[b|c" + , "|}" + , "d" + ), String_.Concat_lines_nl_skip_last + ( "" + , " " + , " " + , " " + , " " + , "
    a" + , " [[b|c" + , "
    " + , "" + , "

    d" + , "

    " + )); + } +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_itm.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_itm.java new file mode 100644 index 000000000..0c3b2402d --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_itm.java @@ -0,0 +1,63 @@ +/* +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.xowa; import gplx.*; +public class Xop_xatr_itm { + public static final byte Tid_null = 0, Tid_invalid = 1, Tid_repeat = 2, Tid_keyVal = 3, Tid_keyOnly = 4; // NOTE: id order is important; see below; + public byte Tid() {return tid;} private byte tid; + public void Tid_to_repeat_() {tid = Tid_repeat;} + public void Tid_to_invalid_() {tid = Tid_invalid;} + public int Key_bgn() {return key_bgn;} private int key_bgn; + public int Key_end() {return key_end;} private int key_end; + public byte[] Key_bry() {return key_bry;} public Xop_xatr_itm Key_bry_(byte[] v) {key_bry = v; return this;} private byte[] key_bry; + public byte[] Val_bry() {return val_bry;} public Xop_xatr_itm Val_bry_(byte[] v) {val_bry = v; return this;} private byte[] val_bry; + public void Key_rng_(int key_bgn, int key_end) {this.key_bgn = key_bgn; this.key_end = key_end;} + public byte Key_tid() {return key_tid;} public Xop_xatr_itm Key_tid_(byte v) {key_tid = v; return this;} private byte key_tid; + public int Val_bgn() {return val_bgn;} private int val_bgn; + public int Val_end() {return val_end;} private int val_end; + public int Atr_bgn() {return atr_bgn;} private int atr_bgn; + public int Atr_end() {return atr_end;} private int atr_end; + public int Eq_pos() {return eq_pos;} private int eq_pos; + public boolean Invalid() {return tid < Tid_keyVal;} // NOTE: Tid order is important + public byte Quote_byte() {return quote_byte;} private byte quote_byte; + public byte[] Val_as_bry(byte[] src) {if (val_bry == null) val_bry = Bry_.Mid(src, val_bgn, val_end); return val_bry;} // NOTE: val_bry is cached + public byte[] Val_as_bry__blank_to_null(byte[] src) {byte[] rv = Val_as_bry(src); return Bry_.Len_eq_0(rv) ? null : rv;} + public int Val_as_int_or(byte[] src, int or) {return val_bry == null ? Bry_.X_to_int_or_lax(src, val_bgn, val_end, or) : Bry_.X_to_int_or(val_bry, or);} + public boolean Val_as_bool_by_int(byte[] src) {return Val_as_int_or(src, 0) == 1;} + public boolean Val_as_bool(byte[] src) {return Bry_.Eq(Bry_.Lower_ascii(Val_as_bry(src)), Bool_.True_bry);} + public static Xop_xatr_itm[] Xatr_parse(Xoa_app app, Xop_xnde_atr_parser parser, Hash_adp_bry hash, Xow_wiki wiki, byte[] src, Xop_xnde_tkn xnde) { + Xop_xatr_itm[] xatr_ary = app.Xatr_parser().Parse(app.Msg_log(), src, xnde.Atrs_bgn(), xnde.Atrs_end()); + for (int i = 0; i < xatr_ary.length; i++) { + Xop_xatr_itm xatr = xatr_ary[i]; + if (xatr.Invalid()) continue; + Object xatr_key_obj = hash.Get_by_mid(src, xatr.Key_bgn(), xatr.Key_end()); + parser.Xatr_parse(wiki, src, xatr, xatr_key_obj); + } + return xatr_ary; + } + public Xop_xatr_itm(int atr_bgn, int atr_end) { + this.tid = Tid_invalid; this.atr_bgn = atr_bgn; this.atr_end = atr_end; + } + public Xop_xatr_itm(byte quote_byte, int atr_bgn, int atr_end, int key_bgn, int key_end) { + this.tid = Tid_keyOnly; this.quote_byte = quote_byte; this.atr_bgn = atr_bgn; this.atr_end = atr_end; this.key_bgn = key_bgn; this.key_end = key_end; this.val_bgn = key_bgn; this.val_end = key_end; + } + public Xop_xatr_itm(byte quote_byte, int atr_bgn, int atr_end, int key_bgn, int key_end, int val_bgn, int val_end, int eq_pos) { + this.tid = Tid_keyVal; this.quote_byte = quote_byte; this.atr_bgn = atr_bgn; this.atr_end = atr_end; this.key_bgn = key_bgn; this.key_end = key_end; this.val_bgn = val_bgn; this.val_end = val_end; this.eq_pos = eq_pos; + } + public static final Xop_xatr_itm[] Ary_empty = new Xop_xatr_itm[0]; + public static final byte Key_tid_generic = 0, Key_tid_id = 1, Key_tid_style = 2, Key_tid_role = 3; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_parser.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_parser.java new file mode 100644 index 000000000..a6ad8f5f1 --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_parser.java @@ -0,0 +1,400 @@ +/* +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.xowa; import gplx.*; +public class Xop_xatr_parser { // REF.MW:Sanitizer.php|decodeTagAttributes;MW_ATTRIBS_REGEX + private ListAdp xatrs = ListAdp_.new_(); + private static final byte Mode_atr_bgn = 1, Mode_invalid = 2, Mode_key = 3, Mode_eq = 4, Mode_val_bgn = 5, Mode_val_quote = 6, Mode_val_raw = 7; + private byte mode = Mode_atr_bgn; + private int atr_bgn = -1, key_bgn = -1, key_end = -1, eq_pos = -1, val_bgn = -1, val_end = -1; boolean valid = true; + private byte quote_byte = Byte_ascii.Nil; + private Hash_adp_bry xnde_hash = Hash_adp_bry.ci_().Add_bry_bry(Xop_xnde_tag_.Tag_nowiki.Name_bry()).Add_bry_bry(Xop_xnde_tag_.Tag_noinclude.Name_bry()).Add_bry_bry(Xop_xnde_tag_.Tag_includeonly.Name_bry()).Add_bry_bry(Xop_xnde_tag_.Tag_onlyinclude.Name_bry()); + private Hash_adp_bry repeated_atrs_hash = Hash_adp_bry.ci_(); + private Bry_bfr key_bfr = Bry_bfr.new_(), val_bfr = Bry_bfr.new_(); boolean key_bfr_on = false, val_bfr_on = false; + public Bry_obj_ref Bry_obj() {return bry_ref;} private Bry_obj_ref bry_ref = Bry_obj_ref.null_(); + public int Xnde_find_gt_find(byte[] src, int pos, int end) { + bry_ref.Val_(null); + byte b = src[pos]; + if (b == Byte_ascii.Slash && pos + 1 < end) { // if end) + break; + byte b = src[i]; + switch (mode) { + case Mode_atr_bgn: + switch (b) { + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.Tab: // skip any ws at bgn; note that once a non-ws char is encountered, it will immediately go into another mode + break; + 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.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + case Byte_ascii.Colon: + if (atr_bgn == -1) atr_bgn = i; + mode = Mode_key; + key_bgn = i; + break; + case Byte_ascii.Lt: + int gt_pos = Xnde_find_gt(log_mgr, src, i, end); + if (gt_pos == String_.Find_none) { + valid = false; mode = Mode_invalid; if (atr_bgn == -1) atr_bgn = i; + } + else { + i = gt_pos; // note that there is ++i below and loop will continue at gt_pos + 1 (next character after) + } + break; + default: + valid = false; mode = Mode_invalid; if (atr_bgn == -1) atr_bgn = i; + break; + } + break; + case Mode_invalid: + switch (b) { + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.Tab: + Make(log_mgr, src, i); + mode = Mode_atr_bgn; + break; + default: + break; + } + break; + case Mode_key: + switch (b) { + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + case Byte_ascii.Colon: case Byte_ascii.Dash: case Byte_ascii.Dot: case Byte_ascii.Underline: + if (key_bfr_on) key_bfr.Add_byte(b); + break; + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.Tab: + if (valid) { + key_end = i; + mode = Mode_eq; + } + else + Make(log_mgr, src, i); + break; + case Byte_ascii.Eq: + if (valid) { + key_end = i; + mode = Mode_val_bgn; + eq_pos = i; + } + break; + case Byte_ascii.Lt: + int gt_pos = Xnde_find_gt(log_mgr, src, i, end); + if (gt_pos == String_.Find_none) { + valid = false; mode = Mode_invalid; + } + else { + if (!key_bfr_on) key_bfr.Add_mid(src, key_bgn, i); + i = gt_pos; // note that there is ++i below and loop will continue at gt_pos + 1 (next character after) + key_bfr_on = true; + } + break; + default: + valid = false; mode = Mode_invalid; + break; + } + break; + case Mode_eq: + switch (b) { + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.Tab: // skip ws + if (key_end == -1) { // EX: "a = b"; key_end != -1 b/c 1st \s sets key_end; EX: "a b = c"; key_end + val_end = i - 1; + Make(log_mgr, src, i); + mode = Mode_atr_bgn; + continue; + } + break; + case Byte_ascii.Eq: + eq_pos = i; + mode = Mode_val_bgn; + break; + case Byte_ascii.Quote: case Byte_ascii.Apos: // FUTURE: previous word was key + default: // NOTE: added this late; xml_parser was not handling "line start=3" DATE:2013-07-03 + val_end = i - 1; + Make(log_mgr, src, i); + mode = Mode_atr_bgn; + continue; + } + break; + case Mode_val_bgn: + switch (b) { + case Byte_ascii.Space: case Byte_ascii.NewLine: case Byte_ascii.Tab: // skip-ws + break; + case Byte_ascii.Quote: case Byte_ascii.Apos: + mode = Mode_val_quote; quote_byte = b; prv_is_ws = false; + break; + 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.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + case Byte_ascii.Colon: + case Byte_ascii.Hash: + mode = Mode_val_raw; + val_bgn = i; + break; + case Byte_ascii.Lt: + int gt_pos = Xnde_find_gt(log_mgr, src, i, end); + if (gt_pos == String_.Find_none) { + valid = false; mode = Mode_invalid; + } + else { + i = gt_pos; // note that there is ++i below and loop will continue at gt_pos + 1 (next character after) + } + break; + default: + break; + } + break; + case Mode_val_quote: + if (val_bgn == -1) val_bgn = i; + switch (b) { + case Byte_ascii.Quote: case Byte_ascii.Apos: + if (quote_byte == b) { + val_end = i; + Make(log_mgr, src, i + 1); // NOTE: set atr_end *after* quote + } + prv_is_ws = false; if (val_bfr_on) val_bfr.Add_byte(b); // INLINE: add char +// else { +// if (!val_bfr_on) val_bfr.Add_mid(src, val_bgn, i + 1); // +1 to include ' +// val_bfr_on = true; +// } + break; + case Byte_ascii.Lt: // "<" try to find nowiki inside atr + int gt_pos = Xnde_find_gt(log_mgr, src, i, end); + if (gt_pos == String_.Find_none) { +// valid = false; mode = Mode_invalid; // DELETE: 2012-11-13; unpaired < should not mark atr invalid; EX: style='margin:1em. +*/ +package gplx.xowa; import gplx.*; +import org.junit.*; +public class Xop_xatr_parser_tst { + @Test public void Kv_quote_double() {fxt.tst_("a=\"b\"", fxt.new_atr_("a", "b"));} private Xop_xatr_parser_fxt fxt = new Xop_xatr_parser_fxt(); + @Test public void Kv_quote_single() {fxt.tst_("a='b'", fxt.new_atr_("a", "b"));} + @Test public void Kv_quote_none() {fxt.tst_("a=b", fxt.new_atr_("a", "b"));} + @Test public void Kv_empty() {fxt.tst_("a=''", fxt.new_atr_("a", ""));} + @Test public void Kv_key_has_underline() {fxt.tst_("a_b=c", fxt.new_atr_("a_b", "c"));} + @Test public void Val_quote_none() {fxt.tst_("b", fxt.new_atr_("b", "b"));} + @Test public void Invalid_key_plus() {fxt.tst_("a+b", fxt.new_invalid_(0, 3));} + @Test public void Invalid_key_plus_many() {fxt.tst_("a+b c=d", fxt.new_invalid_(0, 3), fxt.new_atr_("c", "d"));} + @Test public void Invalid_val_plus() {fxt.tst_("a=b+c", fxt.new_invalid_(0, 5));} + @Test public void Invalid_recover() {fxt.tst_("* a=b", fxt.new_invalid_(0, 1), fxt.new_atr_("a", "b"));} // PURPOSE: * is invalid, but should not stop parsing of a=b + @Test public void Nowiki_val() {fxt.tst_("a='b'", fxt.new_atr_("a", "b").Expd_atr_rng_(0, 13).Expd_key_("a").Expd_val_("b"));} + @Test public void Nowiki_key() {fxt.tst_("a=b", fxt.new_atr_("a", "b").Expd_atr_rng_(8, 11));} + @Test public void Nowiki_key_2() {fxt.tst_("abc=d", fxt.new_atr_("abc", "d").Expd_atr_rng_(0, 22));} + @Test public void Nowiki_key_3() {fxt.tst_("a=\"b\"", fxt.new_atr_("a", "b").Expd_atr_rng_(0, 22));} // EX:fr.w:{{Portail|Transpédia|Californie}} + @Test public void Nowiki_quote() {fxt.tst_("a=\"bcdef\"", fxt.new_atr_("a", "bcdef"));} + @Test public void Int_value() {fxt.tst_int("a='-123'", -123);} + @Test public void Many_apos() {fxt.tst_("a='b' c='d' e='f'", fxt.new_atr_("a", "b"), fxt.new_atr_("c", "d"), fxt.new_atr_("e", "f"));} + @Test public void Many_raw() {fxt.tst_("a=b c=d e=f", fxt.new_atr_("a", "b"), fxt.new_atr_("c", "d"), fxt.new_atr_("e", "f"));} + @Test public void Ws_ini() {fxt.tst_(" a='b'", fxt.new_atr_("a", "b").Expd_atr_rng_(1, 6));} + @Test public void Ws_end() {fxt.tst_(" a='b' c='d'", fxt.new_atr_("a", "b").Expd_atr_rng_(1, 6), fxt.new_atr_("c", "d").Expd_atr_rng_(7, 12));} + @Test public void Quote_ws_nl() {fxt.tst_("a='b\nc'", fxt.new_atr_("a", "b c"));} + @Test public void Quote_ws_mult() {fxt.tst_("a='b c'", fxt.new_atr_("a", "b c"));} + @Test public void Quote_ws_mult_mult() {fxt.tst_("a='b c d'", fxt.new_atr_("a", "b c d"));} // PURPOSE: fix wherein 1st-gobble gobbled rest of spaces (was b cd) + @Test public void Quote_apos() {fxt.tst_("a=\"b c'd\"", fxt.new_atr_("a", "b c'd"));} // PURPOSE: fix wherein apos was gobbled up; EX: s:Alice's_Adventures_in_Wonderland; DATE:2013-11-22 + @Test public void Quote_apos_2() {fxt.tst_("a=\"b'c d\"", fxt.new_atr_("a", "b'c d"));} // PURPOSE: fix wherein apos was causing "'b'c d"; EX:s:Grimm's_Household_Tales,_Volume_1; DATE:2013-12-22 + @Test public void Multiple() {fxt.tst_("a b1 c", fxt.new_atr_("a", "a"), fxt.new_atr_("b1", "b1"), fxt.new_atr_("c", "c"));} + @Test public void Ws() {fxt.tst_("a = 'b'", fxt.new_atr_("a", "b"));} // PURPOSE: fix wherein multiple space was causing "a=a"; EX:fr.s:La_Sculpture_dans_les_cimetières_de_Paris/Père-Lachaise; DATE:2014-01-18 + @Test public void Dangling_eos() {fxt.tst_("a='b' c='d", fxt.new_atr_("a", "b"), fxt.new_invalid_(6, 10));} // PURPOSE: handle dangling quote at eos; PAGE:en.w:Aubervilliers DATE:2014-06-25 + @Test public void Dangling_bos() {fxt.tst_("a='b c=d", fxt.new_invalid_(0, 4), fxt.new_atr_("c", "d"));} // PURPOSE: handle dangling quote at bos; resume at next valid atr; PAGE:en.w:Aubervilliers DATE:2014-06-25 +/* +TODO: +change ws to be end; EX: "a=b c=d" atr1 ends at 4 (not 3) +*/ +// @Test public void Val_quote_none_many() { +// fxt.tst_("a b", fxt.new_atr_("", "a"), fxt.new_atr_("", "b")); +//// fxt.tst_("a='b' c d e='f'", fxt.new_atr_("a", "b"), fxt.new_atr_("", "c"), fxt.new_atr_("", "d"), fxt.new_atr_("e", "f")); +// } +} +class Xop_xatr_parser_fxt { + Xop_xatr_parser parser = new Xop_xatr_parser(); + Tst_mgr tst_mgr = new Tst_mgr(); + public Xop_xatr_itm_chkr new_invalid_(int bgn, int end) {return new Xop_xatr_itm_chkr().Expd_atr_rng_(bgn, end).Expd_typeId_(Xop_xatr_itm.Tid_invalid);} + public Xop_xatr_itm_chkr new_atr_(String key, String val) {return new Xop_xatr_itm_chkr().Expd_key_(key).Expd_val_(val);} + public void tst_(String src_str, Xop_xatr_itm_chkr... expd) { + byte[] src = Bry_.new_utf8_(src_str); + Gfo_msg_log msg_log = new Gfo_msg_log(Xoa_app_.Name); + Xop_xatr_itm[] actl = parser.Parse(msg_log, src, 0, src.length); + tst_mgr.Vars().Clear().Add("raw_bry", src); + tst_mgr.Tst_ary("xatr:", expd, actl); + } + public void tst_int(String src_str, int... expd) { + byte[] src = Bry_.new_utf8_(src_str); + Gfo_msg_log msg_log = new Gfo_msg_log(Xoa_app_.Name); + Xop_xatr_itm[] actl_atr = parser.Parse(msg_log, src, 0, src.length); + int[] actl = new int[actl_atr.length]; + + for (int i = 0; i < actl.length; i++) + actl[i] = actl_atr[i].Val_as_int_or(src, 0); + Tfds.Eq_ary(expd, actl); + } +} +class Xop_xatr_itm_chkr implements Tst_chkr { + public Class TypeOf() {return Xop_xatr_itm.class;} + public Xop_xatr_itm_chkr Expd_atr_rng_(int bgn, int end) {expd_atr_bgn = bgn; expd_atr_end = end; return this;} private int expd_atr_bgn = -1, expd_atr_end = -1; + public Xop_xatr_itm_chkr Expd_key_rng_(int bgn, int end) {expd_key_bgn = bgn; expd_key_end = end; return this;} private int expd_key_bgn = -1, expd_key_end = -1; + public Xop_xatr_itm_chkr Expd_key_(String v) {expd_key = v; return this;} private String expd_key; + public Xop_xatr_itm_chkr Expd_val_(String v) {expd_val = v; return this;} private String expd_val; + public Xop_xatr_itm_chkr Expd_typeId_(byte v) {expd_typeId = v; return this;} private byte expd_typeId = Xop_xatr_itm.Tid_null; + public int Chk(Tst_mgr mgr, String path, Object actl_obj) { + Xop_xatr_itm actl = (Xop_xatr_itm)actl_obj; + int err = 0; + err += mgr.Tst_val(expd_typeId == Xop_xatr_itm.Tid_null, path, "atr_typeId", expd_typeId, actl.Tid()); + err += mgr.Tst_val(expd_atr_bgn == -1, path, "atr_bgn", expd_atr_bgn, actl.Atr_bgn()); + err += mgr.Tst_val(expd_atr_end == -1, path, "atr_end", expd_atr_end, actl.Atr_end()); + err += mgr.Tst_val(expd_key_bgn == -1, path, "key_bgn", expd_key_bgn, actl.Key_bgn()); + err += mgr.Tst_val(expd_key_end == -1, path, "key_end", expd_key_end, actl.Key_end()); + if (actl.Key_bry() == null) + err += mgr.Tst_val(expd_key == null, path, "key", expd_key, mgr.Vars_get_bry_as_str("raw_bry", actl.Key_bgn(), actl.Key_end())); + else + err += mgr.Tst_val(expd_key == null, path, "key", expd_key, String_.new_utf8_(actl.Key_bry())); + if (actl.Val_bry() == null) + err += mgr.Tst_val(expd_val == null, path, "val", expd_val, mgr.Vars_get_bry_as_str("raw_bry", actl.Val_bgn(), actl.Val_end())); + else + err += mgr.Tst_val(expd_val == null, path, "val", expd_val, String_.new_utf8_(actl.Val_bry())); + return err; + } +} +/* +*/ diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_whitelist_mgr.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_whitelist_mgr.java new file mode 100644 index 000000000..b78bcdb55 --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_whitelist_mgr.java @@ -0,0 +1,261 @@ +/* +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.xowa; import gplx.*; +public class Xop_xatr_whitelist_mgr { + public boolean Chk(int tag_id, byte[] src, Xop_xatr_itm xatr) { + byte[] key_bry = xatr.Key_bry(); + byte[] chk_bry; int chk_bgn, chk_end; + if (key_bry == null) { + chk_bry = src; + chk_bgn = xatr.Key_bgn(); + chk_end = xatr.Key_end(); + if (chk_end - chk_bgn == 0) return true; // no key; nothing to whitelist; return true + } + else { // key_bry specified manually; EX: "id=1" has a manual key_bry of "id" + chk_bry = key_bry; + chk_bgn = 0; + chk_end = key_bry.length; + } + Object o = key_trie.MatchAtCur(chk_bry, chk_bgn, chk_end); + if (o == null) return false;// unknown atr_key; EX: + Xop_xatr_whitelist_itm itm = (Xop_xatr_whitelist_itm)o; + byte itm_key_tid = itm.Key_tid(); + xatr.Key_tid_(itm_key_tid); + boolean rv = itm.Tags()[tag_id] == 1 // is atr allowed for tag + && (itm.Exact() ? key_trie.Match_pos() == chk_end : true) // if exact, check for exact; else always true + ; + switch (itm_key_tid) { + case Xop_xatr_itm.Key_tid_style: + if (!Scrub_style(xatr, src)) return false; + break; + case Xop_xatr_itm.Key_tid_role: + if (!Bry_.Eq(Val_role_presentation, xatr.Val_as_bry(src))) return false; // MW: For now we only support role="presentation"; DATE:2014-04-05 + break; + } + return rv; + } + public Xop_xatr_whitelist_mgr Ini() { // REF.MW:Sanitizer.php|setupAttributeWhitelist + Ini_grp("common" , null , "id", "class", "lang", "dir", "title", "style", "role"); + Ini_grp("block" , "common" , "align"); + Ini_grp("tablealign" , null , "align", "char", "charoff", "valign"); + Ini_grp("tablecell" , null , "abbr", "axis", "headers", "scope", "rowspan", "colspan", "nowrap", "width", "height", "bgcolor"); + + Ini_nde(Xop_xnde_tag_.Tid_div , "block"); + Ini_nde(Xop_xnde_tag_.Tid_center , "common"); + Ini_nde(Xop_xnde_tag_.Tid_span , "block"); + Ini_nde(Xop_xnde_tag_.Tid_h1 , "block"); + Ini_nde(Xop_xnde_tag_.Tid_h2 , "block"); + Ini_nde(Xop_xnde_tag_.Tid_h3 , "block"); + Ini_nde(Xop_xnde_tag_.Tid_h4 , "block"); + Ini_nde(Xop_xnde_tag_.Tid_h5 , "block"); + Ini_nde(Xop_xnde_tag_.Tid_h6 , "block"); + Ini_nde(Xop_xnde_tag_.Tid_em , "common"); + Ini_nde(Xop_xnde_tag_.Tid_strong , "common"); + Ini_nde(Xop_xnde_tag_.Tid_cite , "common"); + Ini_nde(Xop_xnde_tag_.Tid_dfn , "common"); + Ini_nde(Xop_xnde_tag_.Tid_code , "common"); + Ini_nde(Xop_xnde_tag_.Tid_samp , "common"); + Ini_nde(Xop_xnde_tag_.Tid_kbd , "common"); + Ini_nde(Xop_xnde_tag_.Tid_var , "common"); + Ini_nde(Xop_xnde_tag_.Tid_abbr , "common"); + Ini_nde(Xop_xnde_tag_.Tid_blockquote , "common", "cite"); + Ini_nde(Xop_xnde_tag_.Tid_sub , "common"); + Ini_nde(Xop_xnde_tag_.Tid_sup , "common"); + Ini_nde(Xop_xnde_tag_.Tid_p , "block"); + Ini_nde(Xop_xnde_tag_.Tid_br , "id", "class", "title", "style", "clear"); + Ini_nde(Xop_xnde_tag_.Tid_pre , "common", "width"); + Ini_nde(Xop_xnde_tag_.Tid_ins , "common", "cite", "datetime"); + Ini_nde(Xop_xnde_tag_.Tid_del , "common", "cite", "datetime"); + Ini_nde(Xop_xnde_tag_.Tid_ul , "common", "type"); + Ini_nde(Xop_xnde_tag_.Tid_ol , "common", "type", "start"); + Ini_nde(Xop_xnde_tag_.Tid_li , "common", "type", "value"); + Ini_nde(Xop_xnde_tag_.Tid_dl , "common"); + Ini_nde(Xop_xnde_tag_.Tid_dd , "common"); + Ini_nde(Xop_xnde_tag_.Tid_dt , "common"); + Ini_nde(Xop_xnde_tag_.Tid_table , "common", "summary", "width", "border", "frame", "rules", "cellspacing", "cellpadding", "align", "bgcolor"); + Ini_nde(Xop_xnde_tag_.Tid_caption , "common", "align"); + Ini_nde(Xop_xnde_tag_.Tid_thead , "common", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_tfoot , "common", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_tbody , "common", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_colgroup , "common", "span", "width", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_col , "common", "span", "width", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_tr , "common", "bgcolor", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_td , "common", "tablecell", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_th , "common", "tablecell", "tablealign"); + Ini_nde(Xop_xnde_tag_.Tid_a , "common", "href", "rel", "rev"); + Ini_nde(Xop_xnde_tag_.Tid_img , "common", "alt", "src", "width", "height"); + Ini_nde(Xop_xnde_tag_.Tid_tt , "common"); + Ini_nde(Xop_xnde_tag_.Tid_b , "common"); + Ini_nde(Xop_xnde_tag_.Tid_i , "common"); + Ini_nde(Xop_xnde_tag_.Tid_big , "common"); + Ini_nde(Xop_xnde_tag_.Tid_small , "common"); + Ini_nde(Xop_xnde_tag_.Tid_strike , "common"); + Ini_nde(Xop_xnde_tag_.Tid_s , "common"); + Ini_nde(Xop_xnde_tag_.Tid_u , "common"); + Ini_nde(Xop_xnde_tag_.Tid_font , "common", "size", "color", "face"); + Ini_nde(Xop_xnde_tag_.Tid_hr , "common", "noshade", "size", "width"); + Ini_nde(Xop_xnde_tag_.Tid_ruby , "common"); + Ini_nde(Xop_xnde_tag_.Tid_rb , "common"); + Ini_nde(Xop_xnde_tag_.Tid_rt , "common"); + Ini_nde(Xop_xnde_tag_.Tid_rp , "common"); + Ini_nde(Xop_xnde_tag_.Tid_math , "class", "style", "id", "title"); + Ini_nde(Xop_xnde_tag_.Tid_time , "class", "datetime"); + Ini_nde(Xop_xnde_tag_.Tid_bdi , "common"); + Ini_nde(Xop_xnde_tag_.Tid_data , "common", "value"); + Ini_nde(Xop_xnde_tag_.Tid_mark , "common"); + Ini_all_loose("data"); + return this; + } + private Hash_adp_bry grp_hash = Hash_adp_bry.cs_(); + private void Ini_grp(String key_str, String base_grp, String... cur_itms) { + byte[][] itms = Bry_.Ary(cur_itms); + if (base_grp != null) + itms = Bry_.Ary_add(itms, (byte[][])grp_hash.Get_by_bry(Bry_.new_ascii_(base_grp))); + byte[] key = Bry_.new_ascii_(key_str); + grp_hash.Add_bry_obj(key, itms); + } + private void Ini_nde(int tag_tid, String... key_strs) { + ListAdp keys = ListAdp_.new_(); + int len = key_strs.length; + for (int i = 0; i < len; i++) { + byte[] key = Bry_.new_ascii_(key_strs[i]); + Object grp_obj = grp_hash.Get_by_bry(key); // is the key a grp? EX: "common" + if (grp_obj == null) + keys.Add(key); + else { + byte[][] grp_keys = (byte[][])grp_obj; + int grp_keys_len = grp_keys.length; + for (int j = 0; j < grp_keys_len; j++) + keys.Add(grp_keys[j]); + } + } + len = keys.Count(); + for (int i = 0; i < len; i++) { + byte[] key_bry = (byte[])keys.FetchAt(i); + Xop_xatr_whitelist_itm itm = (Xop_xatr_whitelist_itm)key_trie.MatchAtCurExact(key_bry, 0, key_bry.length); + if (itm == null) { + itm = Ini_key_trie_add(key_bry, true); + key_trie.Add(key_bry, itm); + } + itm.Tags()[tag_tid] = 1; + } + } + private void Ini_all_loose(String key_str) { + byte[] key_bry = Bry_.new_ascii_(key_str); + Ini_key_trie_add(key_bry, false); + Xop_xatr_whitelist_itm itm = Ini_key_trie_add(key_bry, false); + key_trie.Add(key_bry, itm); + int len = Xop_xnde_tag_._MaxLen; + for (int i = 0; i < len; i++) + itm.Tags()[i] = 1; + } + private Xop_xatr_whitelist_itm Ini_key_trie_add(byte[] key, boolean exact) { + Object key_tid_obj = tid_hash.Fetch(key); + byte key_tid = key_tid_obj == null ? Xop_xatr_itm.Key_tid_generic : ((Byte_obj_val)key_tid_obj).Val(); + Xop_xatr_whitelist_itm rv = new Xop_xatr_whitelist_itm(key, key_tid, exact); + key_trie.Add(key, rv); + return rv; + } + private Hash_adp_bry tid_hash = Hash_adp_bry.ci_() + .Add_str_byte("id", Xop_xatr_itm.Key_tid_id) + .Add_str_byte("style", Xop_xatr_itm.Key_tid_style) + .Add_str_byte("role", Xop_xatr_itm.Key_tid_role) + ; + private ByteTrieMgr_slim key_trie = ByteTrieMgr_slim.ci_ascii_(); // NOTE:ci.ascii:HTML.node_name + public boolean Scrub_style(Xop_xatr_itm xatr, byte[] raw) { // REF:Sanitizer.php|checkCss; '! expression | filter\s*: | accelerator\s*: | url\s*\( !ix'; NOTE: this seems to affect MS IE only; DATE:2013-04-01 + byte[] val_bry = xatr.Val_bry(); + byte[] chk_bry; int chk_bgn, chk_end; + if (val_bry == null) { + chk_bry = raw; + chk_bgn = xatr.Val_bgn(); + chk_end = xatr.Val_end(); + if (chk_end - chk_bgn == 0) return true; // no val; nothing to scrub; return true + } + else { // val_bry specified manually; EX: "id=1" has a manual val_bry of "1" + chk_bry = val_bry; + chk_bgn = 0; + chk_end = val_bry.length; + } + int pos = chk_bgn; + while (pos < chk_end) { + Object o = style_trie.MatchAtCur(chk_bry, pos, chk_end); + if (o == null) + ++pos; + else { + pos = style_trie.Match_pos(); + byte style_tid = ((Byte_obj_val)o).Val(); + switch (style_tid) { + case Style_expression: + xatr.Val_bry_(Bry_.Empty); + return false; + case Style_filter: + case Style_accelerator: + if (Next_non_ws_byte(chk_bry, pos, chk_end) == Byte_ascii.Colon) { + xatr.Val_bry_(Bry_.Empty); + return false; + } + break; + case Style_url: + case Style_urls: + case Style_image: + case Style_image_set: + if (Next_non_ws_byte(chk_bry, pos, chk_end) == Byte_ascii.Paren_bgn) { + xatr.Val_bry_(Bry_.Empty); + return false; + } + break; + } + } + } + return true; + } + byte Next_non_ws_byte(byte[] raw, int bgn, int end) { + for (int i = bgn; i < end; i++) { + byte b = raw[i]; + switch (b) { + case Byte_ascii.Space: + case Byte_ascii.Tab: + case Byte_ascii.CarriageReturn: + case Byte_ascii.NewLine: + break; + default: + return b; + } + } + return Byte_ascii.Nil; + } + static final byte Style_expression = 0, Style_filter = 1, Style_accelerator = 2, Style_url = 3, Style_urls = 4, Style_comment = 5, Style_image = 6, Style_image_set = 7; + private static ByteTrieMgr_slim style_trie = ByteTrieMgr_slim.ci_ascii_() // NOTE:ci.ascii:Javascript + .Add_str_byte("expression" , Style_expression) + .Add_str_byte("filter" , Style_filter) + .Add_str_byte("accelerator" , Style_accelerator) + .Add_str_byte("url" , Style_url) + .Add_str_byte("urls" , Style_urls) + .Add_str_byte("image" , Style_image) + .Add_str_byte("image-set" , Style_image_set) + .Add_str_byte("/*" , Style_comment) + ; + private static final byte[] Val_role_presentation = Bry_.new_ascii_("presentation"); +} +class Xop_xatr_whitelist_itm { + public Xop_xatr_whitelist_itm(byte[] key, byte key_tid, boolean exact) {this.key = key; this.key_tid = key_tid; this.exact = exact;} + public byte[] Key() {return key;} private byte[] key; + public byte Key_tid() {return key_tid;} private byte key_tid; + public boolean Exact() {return exact;} private boolean exact; + public byte[] Tags() {return tags;} private byte[] tags = new byte[Xop_xnde_tag_._MaxLen]; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_whitelist_mgr_tst.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_whitelist_mgr_tst.java new file mode 100644 index 000000000..902460d56 --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xatr_whitelist_mgr_tst.java @@ -0,0 +1,71 @@ +/* +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.xowa; import gplx.*; +import org.junit.*; +public class Xop_xatr_whitelist_mgr_tst { + Xop_xatr_whitelist_fxt fxt = new Xop_xatr_whitelist_fxt(); + @Before public void init() {fxt.Clear();} + @Test public void Basic() { + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "style" , true); + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "xstyle" , false); + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "stylex" , false); + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "styl" , false); + fxt.Whitelist(Xop_xnde_tag_.Tid_img , "alt" , true); + fxt.Whitelist(Xop_xnde_tag_.Tid_img , "span" , false); + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "data-sort-type" , true); + fxt.Whitelist(Xop_xnde_tag_.Tid_data , "value" , true); + fxt.Whitelist(Xop_xnde_tag_.Tid_data , "valuex" , false); + } + @Test public void Role() { + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "role" , "presentation", true); + fxt.Whitelist(Xop_xnde_tag_.Tid_div , "role" , "other", false); + } + @Test public void Scrub() { + fxt.Scrub_style_fail("expression"); + fxt.Scrub_style_fail("filter:a"); + fxt.Scrub_style_fail("filter\t \n:a"); + fxt.Scrub_style_fail("accelerator:a"); + fxt.Scrub_style_fail("url()"); + fxt.Scrub_style_fail("urls()"); + fxt.Scrub_style_pass("filterx"); + } +} +class Xop_xatr_whitelist_fxt { + public void Clear() { + if (whitelist_mgr == null) whitelist_mgr = new Xop_xatr_whitelist_mgr().Ini(); + } private Xop_xatr_whitelist_mgr whitelist_mgr; + public void Whitelist(byte tag_id, String key_str, boolean expd) { + byte[] key_bry = Bry_.new_ascii_(key_str); + atr_itm.Key_rng_(0, key_bry.length); + Tfds.Eq(expd, whitelist_mgr.Chk(tag_id, key_bry, atr_itm), key_str); + } private Xop_xatr_itm atr_itm = new Xop_xatr_itm(0, 0); + public void Whitelist(byte tag_id, String key_str, String val_str, boolean expd) { + byte[] key_bry = Bry_.new_ascii_(key_str); + atr_itm.Key_rng_(0, key_bry.length); + atr_itm.Val_bry_(Bry_.new_ascii_(val_str)); + Tfds.Eq(expd, whitelist_mgr.Chk(tag_id, key_bry, atr_itm), key_str); + } + public void Scrub_style_pass(String style_val_str) {Scrub_style(style_val_str, style_val_str);} + public void Scrub_style_fail(String val_str) {Scrub_style(val_str, "");} + public void Scrub_style(String val_str, String expd) { + byte[] val_bry = Bry_.new_ascii_(val_str); + atr_itm.Val_bry_(val_bry); + whitelist_mgr.Scrub_style(atr_itm, val_bry); + Tfds.Eq(expd, String_.new_ascii_(atr_itm.Val_bry())); + } +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_atr_parser.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_atr_parser.java new file mode 100644 index 000000000..019c7a63d --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_atr_parser.java @@ -0,0 +1,21 @@ +/* +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.xowa; import gplx.*; +public interface Xop_xnde_atr_parser { + void Xatr_parse(Xow_wiki wiki, byte[] src, Xop_xatr_itm xatr, Object xatr_key_obj); +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_lxr.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_lxr.java new file mode 100644 index 000000000..5a805507c --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_lxr.java @@ -0,0 +1,25 @@ +/* +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.xowa; import gplx.*; +class Xop_xnde_lxr implements Xop_lxr { + public byte Lxr_tid() {return Xop_lxr_.Tid_xnde;} + public void Init_by_wiki(Xow_wiki wiki, ByteTrieMgr_fast core_trie) {core_trie.Add(Byte_ascii.Lt, this);} + public void Init_by_lang(Xol_lang lang, ByteTrieMgr_fast core_trie) {} + public int Make_tkn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) {return ctx.Xnde().Make_tkn(ctx, tkn_mkr, root, src, src_len, bgn_pos, cur_pos);} + public static final Xop_xnde_lxr _ = new Xop_xnde_lxr(); Xop_xnde_lxr() {} +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag.java new file mode 100644 index 000000000..af3d502da --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag.java @@ -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 . +*/ +package gplx.xowa; import gplx.*; +public class Xop_xnde_tag { + public Xop_xnde_tag(int id, String name_str) { // NOTE: should only be used by Xop_xnde_tag_ + this.id = id; + this.name_bry = Bry_.new_ascii_(name_str); + this.name_str = name_str; + name_len = name_bry.length; + xtn_end_tag = Bry_.Add(Xop_xnde_tag_.XtnEndTag_bgn, name_bry); // always force endtag; needed for + xtn_end_tag_tmp = new byte[xtn_end_tag.length]; Array_.Copy(xtn_end_tag, xtn_end_tag_tmp); + } + public int Id() {return id;} public Xop_xnde_tag Id_(int v) {id = v; return this;} private int id; + public byte[] Name_bry() {return name_bry;} private byte[] name_bry; + public String Name_str() {return name_str;} private String name_str; + public int Name_len() {return name_len;} private int name_len; + public boolean Xtn() {return xtn;} public Xop_xnde_tag Xtn_() {xtn = true; return this;} private boolean xtn; + public byte[] XtnEndTag() {return xtn_end_tag;} private byte[] xtn_end_tag; + public byte[] XtnEndTag_tmp() {return xtn_end_tag_tmp;} private byte[] xtn_end_tag_tmp; + public int BgnNdeMode() {return bgnNdeMode;} private int bgnNdeMode = Xop_xnde_tag_.BgnNdeMode_normal; + public Xop_xnde_tag BgnNdeMode_inline_() {bgnNdeMode = Xop_xnde_tag_.BgnNdeMode_inline; return this;} + public int EndNdeMode() {return endNdeMode;} private int endNdeMode = Xop_xnde_tag_.EndNdeMode_normal; + public Xop_xnde_tag EndNdeMode_inline_() {endNdeMode = Xop_xnde_tag_.EndNdeMode_inline; return this;} + public Xop_xnde_tag EndNdeMode_escape_() {endNdeMode = Xop_xnde_tag_.EndNdeMode_escape; return this;} + public boolean SingleOnly() {return singleOnly;} public Xop_xnde_tag SingleOnly_() {singleOnly = true; return this;} private boolean singleOnly; + public boolean TblSub() {return tblSub;} public Xop_xnde_tag TblSub_() {tblSub = true; return this;} private boolean tblSub; + public boolean Nest() {return nest;} public Xop_xnde_tag Nest_() {nest = true; return this;} private boolean nest; + public boolean Restricted() {return restricted;} public Xop_xnde_tag Restricted_() {restricted = true; return this;} private boolean restricted; + public boolean NoInline() {return noInline;} public Xop_xnde_tag NoInline_() {noInline = true; return this;} private boolean noInline; + public boolean Inline_by_backslash() {return inline_by_backslash;} public Xop_xnde_tag Inline_by_backslash_() {inline_by_backslash = true; return this;} private boolean inline_by_backslash; + public boolean Section() {return section;} public Xop_xnde_tag Section_() {section = true; return this;} private boolean section; + public boolean Repeat_ends() {return repeat_ends;} public Xop_xnde_tag Repeat_ends_() {repeat_ends = true; return this;} private boolean repeat_ends; + public boolean Repeat_mids() {return repeat_mids;} public Xop_xnde_tag Repeat_mids_() {repeat_mids = true; return this;} private boolean repeat_mids; + public boolean Empty_ignored() {return empty_ignored;} public Xop_xnde_tag Empty_ignored_() {empty_ignored = true; return this;} private boolean empty_ignored; + public boolean Raw() {return raw;} public Xop_xnde_tag Raw_() {raw = true; return this;} private boolean raw; + public static final byte Block_noop = 0, Block_bgn = 1, Block_end = 2; + public byte Block_open() {return block_open;} private byte block_open = Block_noop; + public byte Block_close() {return block_close;} private byte block_close = Block_noop; + public Xop_xnde_tag Block_open_bgn_() {block_open = Block_bgn; return this;} public Xop_xnde_tag Block_open_end_() {block_open = Block_end; return this;} + public Xop_xnde_tag Block_close_bgn_() {block_close = Block_bgn; return this;} public Xop_xnde_tag Block_close_end_() {block_close = Block_end; return this;} + public boolean Xtn_auto_close() {return xtn_auto_close;} public Xop_xnde_tag Xtn_auto_close_() {xtn_auto_close = true; return this;} private boolean xtn_auto_close; + public boolean Ignore_empty() {return ignore_empty;} public Xop_xnde_tag Ignore_empty_() {ignore_empty = true; return this;} private boolean ignore_empty; + public boolean Xtn_skips_template_args() {return xtn_skips_template_args;} public Xop_xnde_tag Xtn_skips_template_args_() {xtn_skips_template_args = true; return this;} private boolean xtn_skips_template_args; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_.java new file mode 100644 index 000000000..e49a192ba --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_.java @@ -0,0 +1,257 @@ +/* +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.xowa; import gplx.*; +public class Xop_xnde_tag_ { + public static final int EndNdeMode_normal = 0, EndNdeMode_inline = 1, EndNdeMode_escape = 2; // escape is for hr which does not support + public static final int BgnNdeMode_normal = 0, BgnNdeMode_inline = 1; + public static final byte[] Name_onlyinclude = Bry_.new_ascii_("onlyinclude"); + public static final byte[] XtnEndTag_bgn = Bry_.new_ascii_(""); + public static final byte + Tid_b = 0 +, Tid_strong = 1 +, Tid_i = 2 +, Tid_em = 3 +, Tid_cite = 4 +, Tid_dfn = 5 +, Tid_var = 6 +, Tid_u = 7 +, Tid_ins = 8 +, Tid_abbr = 9 +, Tid_strike = 10 +, Tid_del = 11 +, Tid_s = 12 +, Tid_sub = 13 +, Tid_sup = 14 +, Tid_big = 15 +, Tid_small = 16 +, Tid_code = 17 +, Tid_tt = 18 +, Tid_kbd = 19 +, Tid_samp = 20 +, Tid_blockquote = 21 +, Tid_pre = 22 +, Tid_font = 23 +, Tid_center = 24 +, Tid_p = 25 +, Tid_span = 26 +, Tid_div = 27 +, Tid_hr = 28 +, Tid_br = 29 +, Tid_h1 = 30 +, Tid_h2 = 31 +, Tid_h3 = 32 +, Tid_h4 = 33 +, Tid_h5 = 34 +, Tid_h6 = 35 +, Tid_li = 36 +, Tid_dt = 37 +, Tid_dd = 38 +, Tid_ol = 39 +, Tid_ul = 40 +, Tid_dl = 41 +, Tid_table = 42 +, Tid_tr = 43 +, Tid_td = 44 +, Tid_th = 45 +, Tid_thead = 46 +, Tid_tfoot = 47 +, Tid_tbody = 48 +, Tid_caption = 49 +, Tid_colgroup = 50 +, Tid_col = 51 +, Tid_a = 52 +, Tid_img = 53 +, Tid_ruby = 54 +, Tid_rt = 55 +, Tid_rb = 56 +, Tid_rp = 57 +, Tid_includeonly = 58 +, Tid_noinclude = 59 +, Tid_onlyinclude = 60 +, Tid_nowiki = 61 +, Tid_xowa_cmd = 62 +, Tid_poem = 63 +, Tid_math = 64 +, Tid_ref = 65 +, Tid_references = 66 +, Tid_source = 67 +, Tid_syntaxHighlight = 68 +, Tid_gallery = 69 +, Tid_imageMap = 70 +, Tid_timeline = 71 +, Tid_hiero = 72 +, Tid_inputBox = 73 +, Tid_pages = 74 +, Tid_section = 75 +, Tid_pagequality = 76 +, Tid_pagelist = 77 +, Tid_categoryList = 78 +, Tid_categoryTree = 79 +, Tid_dynamicPageList = 80 +, Tid_time = 81 +, Tid_input = 82 +, Tid_textarea = 83 +, Tid_score = 84 +, Tid_button = 85 +, Tid_select = 86 +, Tid_option = 87 +, Tid_optgroup = 88 +, Tid_script = 89 +, Tid_style = 90 +, Tid_form = 91 +, Tid_translate = 92 +, Tid_languages = 93 +, Tid_templateData = 94 +, Tid_bdi = 95 +, Tid_data = 96 +, Tid_mark = 97 +, Tid_wbr = 98 +, Tid_bdo = 99 +, Tid_listing_buy = 100 +, Tid_listing_do = 101 +, Tid_listing_drink = 102 +, Tid_listing_eat = 103 +, Tid_listing_listing = 104 +, Tid_listing_see = 105 +, Tid_listing_sleep = 106 +, Tid_rss = 107 +, Tid_xowa_html = 108 +, Tid_xowa_tag_bgn = 109 +, Tid_xowa_tag_end = 110 + ; + public static final int _MaxLen = 111; + public static final Xop_xnde_tag[] Ary = new Xop_xnde_tag[_MaxLen]; + private static Xop_xnde_tag new_(int id, String name) { + Xop_xnde_tag rv = new Xop_xnde_tag(id, name); + Ary[id] = rv; + return rv; + } + public static final Xop_xnde_tag + Tag_b = new_(Tid_b, "b").NoInline_() +, Tag_strong = new_(Tid_strong, "strong").NoInline_() +, Tag_i = new_(Tid_i, "i").NoInline_() +, Tag_em = new_(Tid_em, "em").NoInline_() +, Tag_cite = new_(Tid_cite, "cite").NoInline_() +, Tag_dfn = new_(Tid_dfn, "dfn").NoInline_() +, Tag_var = new_(Tid_var, "var").NoInline_() +, Tag_u = new_(Tid_u, "u").NoInline_() +, Tag_ins = new_(Tid_ins, "ins").NoInline_() +, Tag_abbr = new_(Tid_abbr, "abbr").NoInline_() +, Tag_strike = new_(Tid_strike, "strike").NoInline_() +, Tag_del = new_(Tid_del, "del").NoInline_() +, Tag_s = new_(Tid_s, "s").NoInline_() +, Tag_sub = new_(Tid_sub, "sub").NoInline_().Nest_() +, Tag_sup = new_(Tid_sup, "sup").NoInline_().Nest_() +, Tag_big = new_(Tid_big, "big").NoInline_().Nest_() +, Tag_small = new_(Tid_small, "small").NoInline_().Nest_() +, Tag_code = new_(Tid_code, "code").NoInline_().Repeat_ends_() +, Tag_tt = new_(Tid_tt, "tt").NoInline_().Repeat_ends_() +, Tag_kbd = new_(Tid_kbd, "kbd").NoInline_() +, Tag_samp = new_(Tid_samp, "samp").NoInline_() +, Tag_blockquote = new_(Tid_blockquote, "blockquote").NoInline_().Repeat_mids_().Nest_().Section_().Block_open_bgn_().Block_close_end_() // NOTE: should be open_end_, but leaving for now; DATE:2014-03-11; added Repeat_mids_(); PAGE:en.w:Ring_a_Ring_o'_Roses DATE:2014-06-26 +, Tag_pre = new_(Tid_pre, "pre").NoInline_().Section_().Xtn_().Raw_().Block_open_bgn_().Block_close_end_().Ignore_empty_().Xtn_skips_template_args_() +, Tag_font = new_(Tid_font, "font").NoInline_().Nest_() +, Tag_center = new_(Tid_center, "center").NoInline_().Nest_().Block_open_end_().Block_close_end_() // removed .Repeat_ends_(); added Nest_(); EX: w:Burr Truss; DATE:2012-12-12 +, Tag_p = new_(Tid_p, "p").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_span = new_(Tid_span, "span").Nest_().Section_() +, Tag_div = new_(Tid_div, "div").Nest_().Section_().Block_open_end_().Block_close_end_() +, Tag_hr = new_(Tid_hr, "hr").SingleOnly_().BgnNdeMode_inline_().Inline_by_backslash_().EndNdeMode_escape_().Section_().Block_close_end_() +, Tag_br = new_(Tid_br, "br").SingleOnly_().BgnNdeMode_inline_().Inline_by_backslash_().EndNdeMode_inline_().Section_() +, Tag_h1 = new_(Tid_h1, "h1").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_h2 = new_(Tid_h2, "h2").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_h3 = new_(Tid_h3, "h3").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_h4 = new_(Tid_h4, "h4").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_h5 = new_(Tid_h5, "h5").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_h6 = new_(Tid_h6, "h6").NoInline_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_li = new_(Tid_li, "li").Repeat_mids_().Empty_ignored_().Block_open_bgn_().Block_close_end_().Nest_() // added .Nest() DATE:2014-06-26 +, Tag_dt = new_(Tid_dt, "dt").Repeat_mids_() +, Tag_dd = new_(Tid_dd, "dd").Repeat_mids_() +, Tag_ol = new_(Tid_ol, "ol").NoInline_().Nest_().Block_open_bgn_().Block_close_end_() +, Tag_ul = new_(Tid_ul, "ul").NoInline_().Nest_().Block_open_bgn_().Block_close_end_() +, Tag_dl = new_(Tid_dl, "dl").NoInline_().Nest_() +, Tag_table = new_(Tid_table, "table").NoInline_().Nest_().Block_open_bgn_().Block_close_end_() +, Tag_tr = new_(Tid_tr, "tr").Nest_().TblSub_().Block_open_bgn_().Block_open_end_() +, Tag_td = new_(Tid_td, "td").Nest_().TblSub_().Block_open_end_().Block_close_bgn_() +, Tag_th = new_(Tid_th, "th").Nest_().TblSub_().Block_open_end_().Block_close_bgn_() +, Tag_thead = new_(Tid_thead, "thead") +, Tag_tfoot = new_(Tid_tfoot, "tfoot") +, Tag_tbody = new_(Tid_tbody, "tbody") +, Tag_caption = new_(Tid_caption, "caption").NoInline_().TblSub_() +, Tag_colgroup = new_(Tid_colgroup, "colgroup") +, Tag_col = new_(Tid_col, "col") +, Tag_a = new_(Tid_a, "a").Restricted_() +, Tag_img = new_(Tid_img, "img").Xtn_().Restricted_() +, Tag_ruby = new_(Tid_ruby, "ruby").NoInline_() +, Tag_rt = new_(Tid_rt, "rt").NoInline_() +, Tag_rb = new_(Tid_rb, "rb").NoInline_() +, Tag_rp = new_(Tid_rp, "rp").NoInline_() +, Tag_includeonly = new_(Tid_includeonly, "includeonly") +, Tag_noinclude = new_(Tid_noinclude, "noinclude") +, Tag_onlyinclude = new_(Tid_onlyinclude, "onlyinclude") +, Tag_nowiki = new_(Tid_nowiki, "nowiki") +, Tag_xowa_cmd = new_(Tid_xowa_cmd, "xowa_cmd").Xtn_() +, Tag_poem = new_(Tid_poem, "poem").Xtn_().Xtn_auto_close_() +, Tag_math = new_(Tid_math, "math").Xtn_() +, Tag_ref = new_(Tid_ref, "ref").Xtn_().Nest_() +, Tag_references = new_(Tid_references, "references").Xtn_().Nest_() +, Tag_source = new_(Tid_source, "source").Xtn_().Block_open_bgn_().Block_close_end_() // deactivate pre; pre; PAGE:en.w:Comment_(computer_programming); DATE:2014-06-23 +, Tag_syntaxHighlight = new_(Tid_syntaxHighlight, "syntaxHighlight").Xtn_().Block_open_bgn_().Block_close_end_() // deactivate pre; pre; PAGE:en.w:Comment_(computer_programming); DATE:2014-06-23 +, Tag_gallery = new_(Tid_gallery, "gallery").Xtn_().Block_open_bgn_().Block_close_end_() +, Tag_imageMap = new_(Tid_imageMap, "imageMap").Xtn_() +, Tag_timeline = new_(Tid_timeline, "timeline").Xtn_() +, Tag_hiero = new_(Tid_hiero, "hiero").Xtn_() +, Tag_inputBox = new_(Tid_inputBox, "inputBox").Xtn_() +, Tag_pages = new_(Tid_pages, "pages").Xtn_() +, Tag_section = new_(Tid_section, "section").Xtn_() +, Tag_pagequality = new_(Tid_pagequality, "pagequality").Xtn_() +, Tag_pagelist = new_(Tid_pagelist, "pagelist").Xtn_() +, Tag_categoryList = new_(Tid_categoryList, "categoryList").Xtn_() +, Tag_categoryTree = new_(Tid_categoryTree, "categoryTree").Xtn_() +, Tag_dynamicPageList = new_(Tid_dynamicPageList, "dynamicPageList").Xtn_() +, Tag_time = new_(Tid_time, "time") +, Tag_input = new_(Tid_input, "input").Restricted_() +, Tag_textarea = new_(Tid_textarea, "textarea").Restricted_() +, Tag_score = new_(Tid_score, "score").Xtn_() +, Tag_button = new_(Tid_button, "button").Restricted_() +, Tag_select = new_(Tid_select, "select").Restricted_() +, Tag_option = new_(Tid_option, "option").Restricted_() +, Tag_optgroup = new_(Tid_optgroup, "optgroup").Restricted_() +, Tag_script = new_(Tid_script, "script").Restricted_() // NOTE: had ".Block_open_bgn_().Block_close_end_()"; PAGE:en.w:Cascading_Style_Sheets DATE:2014-06-23 +, Tag_style = new_(Tid_style, "style").Restricted_() // NOTE: had ".Block_open_bgn_().Block_close_end_()"; PAGE:en.w:Cascading_Style_Sheets DATE:2014-06-23 +, Tag_form = new_(Tid_form, "form").Restricted_() // NOTE: had ".Block_open_bgn_().Block_close_end_()"; PAGE:en.w:Cascading_Style_Sheets DATE:2014-06-23 +, Tag_translate = new_(Tid_translate, "translate").Xtn_() +, Tag_languages = new_(Tid_languages, "languages").Xtn_() +, Tag_templateData = new_(Tid_templateData, "templateData").Xtn_() +, Tag_bdi = new_(Tid_bdi, "bdi") +, Tag_data = new_(Tid_data, "data") +, Tag_mark = new_(Tid_mark, "mark") +, Tag_wbr = new_(Tid_wbr, "wbr").SingleOnly_() +, Tag_bdo = new_(Tid_bdo, "bdo").NoInline_().Nest_().Section_().Block_open_bgn_().Block_close_end_() +, Tag_listing_buy = new_(Tid_listing_buy, "buy").Xtn_() +, Tag_listing_do = new_(Tid_listing_do, "do").Xtn_() +, Tag_listing_drink = new_(Tid_listing_drink, "drink").Xtn_() +, Tag_listing_eat = new_(Tid_listing_eat, "eat").Xtn_() +, Tag_listing_listing = new_(Tid_listing_listing, "listing").Xtn_() +, Tag_listing_see = new_(Tid_listing_see, "see").Xtn_() +, Tag_listing_sleep = new_(Tid_listing_sleep, "sleep").Xtn_() +, Tag_rss = new_(Tid_rss, "rss").Xtn_() +, Tag_xowa_html = new_(Tid_xowa_html, "xowa_html").Xtn_() +, Tag_xowa_tag_bgn = new_(Tid_xowa_tag_bgn, "xtag_bgn").Xtn_() +, Tag_xowa_tag_end = new_(Tid_xowa_tag_end, "xtag_end").Xtn_() + ; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_regy.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_regy.java new file mode 100644 index 000000000..ee362acc3 --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_regy.java @@ -0,0 +1,51 @@ +/* +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.xowa; import gplx.*; +public class Xop_xnde_tag_regy { + public ByteTrieMgr_slim XndeNames(int i) { + if (nild) {Init(); nild = false;} + switch (i) { + case Xop_parser_.Parse_tid_tmpl: return tagRegy_tmpl; + case Xop_parser_.Parse_tid_page_tmpl: return tagRegy_wiki_tmpl; + case Xop_parser_.Parse_tid_page_wiki: return tagRegy_wiki_main; + default: return tagRegy_wiki_tmpl; //throw Err_.unhandled(i); + } + } boolean nild = true; + public void Init() { + Init_reg(tagRegy_tmpl , FilterXtns(Xop_xnde_tag_.Ary, Xop_xnde_tag_.Tag_includeonly, Xop_xnde_tag_.Tag_noinclude, Xop_xnde_tag_.Tag_onlyinclude, Xop_xnde_tag_.Tag_nowiki)); + Init_reg(tagRegy_wiki_tmpl , FilterXtns(Xop_xnde_tag_.Ary, Xop_xnde_tag_.Tag_includeonly, Xop_xnde_tag_.Tag_noinclude, Xop_xnde_tag_.Tag_onlyinclude, Xop_xnde_tag_.Tag_nowiki)); + Init_reg(tagRegy_wiki_main , Xop_xnde_tag_.Ary); + } + Xop_xnde_tag[] FilterXtns(Xop_xnde_tag[] ary, Xop_xnde_tag... more) { + ListAdp rv = ListAdp_.new_(); + for (Xop_xnde_tag itm : ary) + if (itm.Xtn()) rv.Add(itm); + for (Xop_xnde_tag itm : more) + rv.Add(itm); + return (Xop_xnde_tag[])rv.XtoAry(Xop_xnde_tag.class); + } + private void Init_reg(ByteTrieMgr_slim tagRegy, Xop_xnde_tag... ary) { + for (Xop_xnde_tag tag : ary) + tagRegy.Add(tag.Name_bry(), tag); + } + private ByteTrieMgr_slim + tagRegy_wiki_main = ByteTrieMgr_slim.ci_ascii_() // NOTE:ci.ascii:MW_const.en; listed XML node names are en + , tagRegy_wiki_tmpl = ByteTrieMgr_slim.ci_ascii_() + , tagRegy_tmpl = ByteTrieMgr_slim.ci_ascii_() + ; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_stack.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_stack.java new file mode 100644 index 000000000..070f509ab --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tag_stack.java @@ -0,0 +1,35 @@ +/* +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.xowa; import gplx.*; +public class Xop_xnde_tag_stack { + public void Push() {xmlTagsStack.Add(xmlTags); xmlTags = new int[Xop_xnde_tag_._MaxLen];} + public void Pop() {xmlTags = (int[])ListAdp_.Pop(xmlTagsStack);} + public boolean Has(int id) {return xmlTags[id] != 0;} + public void Add(int id) {++xmlTags[id];} + public void Del(int id) { + int val = --xmlTags[id]; + if (val == -1) xmlTags[id] = 0; + } + public void Clear() { + for (int i = 0; i < Xop_xnde_tag_._MaxLen; i++) + xmlTags[i] = 0; + xmlTagsStack.Clear(); + } + ListAdp xmlTagsStack = ListAdp_.new_(); + int[] xmlTags = new int[Xop_xnde_tag_._MaxLen]; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tkn.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tkn.java new file mode 100644 index 000000000..aece2e588 --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_tkn.java @@ -0,0 +1,115 @@ +/* +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.xowa; import gplx.*; +import gplx.xowa.xtns.*; +public class Xop_xnde_tkn extends Xop_tkn_itm_base implements Xop_tblw_tkn { + @Override public byte Tkn_tid() {return Xop_tkn_itm_.Tid_xnde;} + public int Tblw_tid() {return tag.Id();} // NOTE: tblw tkns actually return xnde as Tblw_tid + public boolean Tblw_xml() {return true;} + public int Tblw_subs_len() {return tblw_subs_len;} public void Tblw_subs_len_add_() {++tblw_subs_len;} private int tblw_subs_len; + public byte CloseMode() {return closeMode;} public Xop_xnde_tkn CloseMode_(byte v) {closeMode = v; return this;} private byte closeMode = Xop_xnde_tkn.CloseMode_null; + public boolean Tag_visible() {return tag_visible;} public Xop_xnde_tkn Tag_visible_(boolean v) {tag_visible = v; return this;} private boolean tag_visible = true; + public int Name_bgn() {return name_bgn;} public Xop_xnde_tkn Name_bgn_(int v) {name_bgn = v; return this;} private int name_bgn = -1; + public int Name_end() {return name_end;} public Xop_xnde_tkn Name_end_(int v) {name_end = v; return this;} private int name_end = -1; + public Xop_xnde_tkn Name_rng_(int bgn, int end) {name_bgn = bgn; name_end = end; return this;} + public int Atrs_bgn() {return atrs_bgn;} public Xop_xnde_tkn Atrs_bgn_(int v) {atrs_bgn = v; return this;} private int atrs_bgn = Xop_tblw_wkr.Atrs_null; + public int Atrs_end() {return atrs_end;} public Xop_xnde_tkn Atrs_end_(int v) {atrs_end = v; return this;} private int atrs_end = Xop_tblw_wkr.Atrs_null; + public Xop_xnde_tkn Atrs_rng_(int bgn, int end) {atrs_bgn = bgn; atrs_end = end; return this;} + public void Atrs_rng_set(int bgn, int end) {Atrs_rng_(bgn, end);} + public Xop_xatr_itm[] Atrs_ary() {return atrs_ary;} + public Xop_xnde_tkn Atrs_ary_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} private Xop_xatr_itm[] atrs_ary; + public Xop_tblw_tkn Atrs_ary_as_tblw_(Xop_xatr_itm[] v) {atrs_ary = v; return this;} + public Xop_xnde_tag Tag() {return tag;} public Xop_xnde_tkn Tag_(Xop_xnde_tag v) {tag = v; return this;} private Xop_xnde_tag tag; + public int Tag_open_bgn() {return tag_open_bgn;} private int tag_open_bgn = Int_.Null; + public int Tag_open_end() {return tag_open_end;} private int tag_open_end = Int_.Null; + public Xop_xnde_tkn Tag_open_rng_(int bgn, int end) {this.tag_open_bgn = bgn; this.tag_open_end = end; return this;} + public int Tag_close_bgn() {return tag_close_bgn;} private int tag_close_bgn = Int_.Null; + public int Tag_close_end() {return tag_close_end;} private int tag_close_end = Int_.Null; + public Xop_xnde_tkn Tag_close_rng_(int bgn, int end) {this.tag_close_bgn = bgn; this.tag_close_end = end; return this;} + public Xop_xnde_tkn Subs_add_ary(Xop_tkn_itm... ary) {for (Xop_tkn_itm itm : ary) Subs_add(itm); return this;} + public Xox_xnde Xnde_xtn() {return xnde_xtn;} public Xop_xnde_tkn Xnde_xtn_(Xox_xnde v) {xnde_xtn = v; return this;} private Xox_xnde xnde_xtn; + @Override public void Tmpl_compile(Xop_ctx ctx, byte[] src, Xot_compile_data prep_data) { + switch (tag.Id()) { + case Xop_xnde_tag_.Tid_noinclude: // NOTE: prep_mode is false to force recompile; see Ex_Tmpl_noinclude and {{{1|a}}} + case Xop_xnde_tag_.Tid_includeonly: // NOTE: changed to always ignore ; DATE:2014-05-10 + break; + case Xop_xnde_tag_.Tid_nowiki: { + int subs_len = this.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Xop_tkn_itm sub = this.Subs_get(i); + sub.Tmpl_compile(ctx, src, prep_data); + } + break; + } + case Xop_xnde_tag_.Tid_onlyinclude: { + int subs_len = this.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Xop_tkn_itm sub = this.Subs_get(i); + sub.Tmpl_compile(ctx, src, prep_data); + } + prep_data.OnlyInclude_exists = true; + break; + } + default: { + int subs_len = this.Subs_len(); + for (int i = 0; i < subs_len; i++) { + Xop_tkn_itm sub = this.Subs_get(i); + sub.Tmpl_compile(ctx, src, prep_data); + } + break; // can happen in compile b/c invks are now being compiled + } + } + } + @Override public boolean Tmpl_evaluate(Xop_ctx ctx, byte[] src, Xot_invk caller, Bry_bfr bfr) { + int subs_len = this.Subs_len(); + switch (tag.Id()) { + case Xop_xnde_tag_.Tid_noinclude: // do not evaluate subs + break; + case Xop_xnde_tag_.Tid_includeonly: // evaluate subs + if (!ctx.Only_include_evaluate()) { + for (int i = 0; i < subs_len; i++) + this.Subs_get(i).Tmpl_evaluate(ctx, src, caller, bfr); + } + break; + case Xop_xnde_tag_.Tid_nowiki: // evaluate subs; add tags + bfr.Add_byte(Byte_ascii.Lt).Add(Xop_xnde_tag_.Tag_nowiki.Name_bry()).Add_byte(Byte_ascii.Gt); + for (int i = 0; i < subs_len; i++) + this.Subs_get(i).Tmpl_evaluate(ctx, src, caller, bfr); + bfr.Add_byte(Byte_ascii.Lt).Add_byte(Byte_ascii.Slash).Add(Xop_xnde_tag_.Tag_nowiki.Name_bry()).Add_byte(Byte_ascii.Gt); + break; + case Xop_xnde_tag_.Tid_onlyinclude: // evaluate subs but toggle onlyinclude flag on/off +// boolean prv_val = ctx.Onlyinclude_enabled; +// ctx.Onlyinclude_enabled = false; + for (int i = 0; i < subs_len; i++) + this.Subs_get(i).Tmpl_evaluate(ctx, src, caller, bfr); +// ctx.Onlyinclude_enabled = prv_val; + break; + default: // ignore tags except for xtn; NOTE: Xtn tags are part of tagRegy_wiki_tmpl stage + if (tag.Xtn()) { + bfr.Add_mid(src, tag_open_bgn, tag_open_end); // write tag_bgn + for (int i = 0; i < subs_len; i++) // always evaluate subs; handle {{{1}}}; DATE:2014-03-03 + this.Subs_get(i).Tmpl_evaluate(ctx, src, caller, bfr); + bfr.Add_mid(src, tag_close_bgn, tag_close_end); // write tag_end + } + break; + } + return true; + } + public static Xop_xnde_tkn new_() {return new Xop_xnde_tkn();} private Xop_xnde_tkn() {} + public static final byte CloseMode_null = 0, CloseMode_inline = 1, CloseMode_pair = 2, CloseMode_open = 3; +} diff --git a/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_wkr.java b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_wkr.java new file mode 100644 index 000000000..b00dc987b --- /dev/null +++ b/400_xowa/src_490_xnde/gplx/xowa/Xop_xnde_wkr.java @@ -0,0 +1,728 @@ +/* +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.xowa; import gplx.*; +import gplx.xowa.apps.fsys.*; import gplx.xowa.wikis.*; import gplx.xowa.xtns.*; +import gplx.xowa.parsers.logs.*; +public class Xop_xnde_wkr implements Xop_ctx_wkr { + public void Ctor_ctx(Xop_ctx ctx) {} + public Xob_xnde_wkr File_wkr() {return file_wkr;} public Xop_xnde_wkr File_wkr_(Xob_xnde_wkr v) {file_wkr = v; return this;} private Xob_xnde_wkr file_wkr; + public boolean Pre_at_bos() {return pre_at_bos;} public void Pre_at_bos_(boolean v) {pre_at_bos = v;} private boolean pre_at_bos; + public void Page_bgn(Xop_ctx ctx, Xop_root_tkn root) {} + public void Page_end(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int src_len) {this.Clear();} + private void Clear() { + pre_at_bos = false; + } + public void AutoClose(Xop_ctx ctx, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos, Xop_tkn_itm tkn) { + Xop_xnde_tkn xnde = (Xop_xnde_tkn)tkn; + xnde.Src_end_(src_len); + xnde.Subs_move(root); // NOTE: ctx.Root used to be root which was a member variable; DATE:2013-12-11 + ctx.Msg_log().Add_itm_none(Xop_xnde_log.Dangling_xnde, src, xnde.Src_bgn(), xnde.Name_end()); // NOTE: xnde.Src_bgn to start at <; xnde.Name_end b/c xnde.Src_end is -1 + } + public int Make_tkn(Xop_ctx ctx, Xop_tkn_mkr tkn_mkr, Xop_root_tkn root, byte[] src, int src_len, int bgn_pos, int cur_pos) { + if (bgn_pos == Xop_parser_.Doc_bgn_bos) { + bgn_pos = 0; // do not allow -1 pos + } + if (cur_pos == src_len) return ctx.Lxr_make_txt_(src_len); // "<" is last char in page; strange, but don't raise error; + Xop_tkn_itm last_tkn = ctx.Stack_get_last(); // BLOCK:invalid_ttl_check + if ( last_tkn != null + && last_tkn.Tkn_tid() == Xop_tkn_itm_.Tid_lnki) { + Xop_lnki_tkn lnki = (Xop_lnki_tkn)last_tkn; + if ( lnki.Pipe_count_is_zero() + // && !Xop_lnki_wkr_.Parse_ttl(ctx, src, lnki, bgn_pos) // NOTE: no ttl parse check; in ttl is automatically invalid; EX: [[ac|d]]; "a" is valid ttl, but "ac" is not + ) { + ctx.Stack_pop_last(); + return Xop_lnki_wkr_.Invalidate_lnki(ctx, src, root, lnki, bgn_pos); + } + } + + // find > + byte cur_byt = src[cur_pos]; + boolean tag_is_closing = false; + if (cur_byt == Byte_ascii.Slash) { // "= src_len) return ctx.Lxr_make_txt_(atrs_bgn_pos); // truncated tag; EX: "; + default: // allow all other symbols by defaults + break; + case Byte_ascii.Ltr_A: case Byte_ascii.Ltr_B: case Byte_ascii.Ltr_C: case Byte_ascii.Ltr_D: case Byte_ascii.Ltr_E: + case Byte_ascii.Ltr_F: case Byte_ascii.Ltr_G: case Byte_ascii.Ltr_H: case Byte_ascii.Ltr_I: case Byte_ascii.Ltr_J: + case Byte_ascii.Ltr_K: case Byte_ascii.Ltr_L: case Byte_ascii.Ltr_M: case Byte_ascii.Ltr_N: case Byte_ascii.Ltr_O: + case Byte_ascii.Ltr_P: case Byte_ascii.Ltr_Q: case Byte_ascii.Ltr_R: case Byte_ascii.Ltr_S: case Byte_ascii.Ltr_T: + case Byte_ascii.Ltr_U: case Byte_ascii.Ltr_V: case Byte_ascii.Ltr_W: case Byte_ascii.Ltr_X: case Byte_ascii.Ltr_Y: case Byte_ascii.Ltr_Z: + case Byte_ascii.Ltr_a: case Byte_ascii.Ltr_b: case Byte_ascii.Ltr_c: case Byte_ascii.Ltr_d: case Byte_ascii.Ltr_e: + case Byte_ascii.Ltr_f: case Byte_ascii.Ltr_g: case Byte_ascii.Ltr_h: case Byte_ascii.Ltr_i: case Byte_ascii.Ltr_j: + case Byte_ascii.Ltr_k: case Byte_ascii.Ltr_l: case Byte_ascii.Ltr_m: case Byte_ascii.Ltr_n: case Byte_ascii.Ltr_o: + case Byte_ascii.Ltr_p: case Byte_ascii.Ltr_q: case Byte_ascii.Ltr_r: case Byte_ascii.Ltr_s: case Byte_ascii.Ltr_t: + case Byte_ascii.Ltr_u: case Byte_ascii.Ltr_v: case Byte_ascii.Ltr_w: case Byte_ascii.Ltr_x: case Byte_ascii.Ltr_y: case Byte_ascii.Ltr_z: + 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: + tag_obj = null; + break; + } + } + boolean ctx_cur_tid_is_tblw_atr_owner = false; + switch (ctx.Cur_tkn_tid()) { + case Xop_tkn_itm_.Tid_tblw_tb: case Xop_tkn_itm_.Tid_tblw_tr: case Xop_tkn_itm_.Tid_tblw_th: + ctx_cur_tid_is_tblw_atr_owner = true; + break; + } + if (tag_obj == null) { // not a known xml tag; EX: ""; "if 5 < 7 then" + if (ctx.Parse_tid() == Xop_parser_.Parse_tid_page_wiki) { + if (ctx_cur_tid_is_tblw_atr_owner) // ; NOTE: MW does not ignore > inside quotes; EX:
    abc
    ->