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

v2.11.1.1

This commit is contained in:
gnosygnu
2015-11-01 20:50:05 -05:00
parent 4f43f51b18
commit b990ec409f
858 changed files with 6758 additions and 4187 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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() {}
}

View File

@@ -0,0 +1,248 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public 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.langs.dsvs; import gplx.*; import gplx.langs.*;
import gplx.core.strings.*; import gplx.stores.*;
import gplx.core.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_wo_type("invalid quote in quoted field; quote must be followed by quote, fieldSpr, or recordSpr", "sym", strm.Cur(), "text", strm.To_str_by_pos(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.To_str_and_clear();
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_wo_type("unknown dsv cmd", "cmd", val);
}
else
throw Err_.new_wo_type("unable to process field value", "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_(); List_adp tkns = List_adp_.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;
List_adp names = List_adp_.new_(), types = List_adp_.new_();
for (int i = 0; i < flds.Count(); i++) {
GfoFld fld = flds.Get_at(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(List_adp tkns) {
if (stage != Stage_Init) CreateTbl(); // CreateTbl if HDR or ROW is in progress
tbl.Name_((String)tkns.Get_at(0));
layout.HeaderList().Add_TableName();
stage = Stage_Hdr; tkns.Clear();
}
public void MakeFldNames(List_adp 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(List_adp 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.Instance.Get_by_or_fail((String)fldTypeObj);
fldTypes.Add(type);
}
layout.HeaderList().Add_LeafTypes();
stage = Stage_Hdr; tkns.Clear();
}
public void MakeComment(List_adp 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.Get_at(i));
layout.HeaderList().Add_Comment(sb.To_str());
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(List_adp 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(List_adp tkns) {
GfoFldList subFlds = tbl.SubFlds(); int subFldsCount = subFlds.Count();
if (tkns.Count() > subFldsCount) throw Err_.new_wo_type("values.Count cannot be greater than fields.Count", "values.Count", tkns.Count(), "fields.Count", subFldsCount);
Object[] rv = new Object[subFldsCount];
for (int i = 0; i < subFldsCount; i++) {
ClassXtn typx = subFlds.Get_at(i).Type();
String val = i < tkns.Count() ? (String)tkns.Get_at(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.Instance);
}
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.Get_at(i) : "fld" + i;
ClassXtn typx = i < fldTypesCount ? (ClassXtn)fldTypes.Get_at(i) : StringClassXtn.Instance;
tbl.SubFlds().Add(name, typx);
}
}
}
GfoNde root; GfoNde tbl; DsvStoreLayout layout = DsvStoreLayout.dsv_brief_();
List_adp fldNames = List_adp_.new_(); List_adp fldTypes = List_adp_.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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
import org.junit.*; import gplx.core.strings.*;
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 e) {
Tfds.Eq_true(String_.Has(Err_.Message_lang(e), "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.Instance).Add("name", StringClassXtn.Instance), 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.Instance;
// }
}
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);
List_adp expdList = List_adp_.new_(), actlList = List_adp_.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, List_adp list, String_bldr sb) {
for (int i = 0; i < flds.Count(); i++) {
GfoFld fld = flds.Get_at(i);
sb.Add(fld.Key()).Add(",").Add(fld.Type().Key());
list.Add(sb.To_str_and_clear());
}
}
public DsvDataRdr_fxt tst_Tbls(String... expdNames) {
List_adp actlList = List_adp_.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.To_str_ary());
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;
}
List_adp expdList = List_adp_.new_(), actlList = List_adp_.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_.Xto_str_strict_or_null_mark(row.ReadAt(j)));
}
expdList.Add(sb.To_str_and_clear());
}
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_.Xto_str_strict_or_null_mark(expdRow[j]));
}
actlList.Add(sb.To_str_and_clear());
}
Tfds.Eq_list(expdList, actlList);
return this;
}
public static DsvDataRdr_fxt new_() {return new DsvDataRdr_fxt();}
static final int TblIdx0 = 0;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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"));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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.Instance).Add("fld1", StringClassXtn.Instance));
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.Instance).Add("name", StringClassXtn.Instance));
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"));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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.Instance).Add("fld1", StringClassXtn.Instance));
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.Instance).Add("fld1", StringClassXtn.Instance));
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.Instance).Add("fld1", StringClassXtn.Instance));
fx.tst_Dat(0, fx.ary_("", "val1"));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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().Get_by(DsvStoreLayout.Key_const);
int[] actl = new int[layout.HeaderList().Count()];
for (int i = 0; i < actl.length; i++)
actl[i] = layout.HeaderList().Get_at(i).Id();
Tfds.Eq_ary(expd, actl);
}
GfoNde root;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
import gplx.core.strings.*;
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().Get_at(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 To_str() {return sb.To_str();}
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.Get_at(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 To_str() {return sb.To_str();}
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() {}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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();}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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.Instance).Add("name", StringClassXtn.Instance);
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.To_str();
Tfds.Eq(expd, actl);
}
public static DsvDataWtr_fxt new_() {return new DsvDataWtr_fxt();} DsvDataWtr_fxt() {}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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_();
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
public class DsvHeaderList {
@gplx.Internal protected int Count() {return list.Count();}
@gplx.Internal protected DsvHeaderItm Get_at(int i) {return (DsvHeaderItm)list.Get_at(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);}
List_adp list = List_adp_.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
;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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_.new_type_mismatch_w_exc(exc, DsvStoreLayout.class, obj);}}
public static final String Key_const = "StoreLayoutWtr";
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.dsvs; import gplx.*; import gplx.langs.*;
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();}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import gplx.core.strings.*;
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_.To_str_fmt(multipleAry[i], numFmt));
sb.Add_fmt(filFmt, Int_.To_str_fmt(idx, numFmt));
return sb.To_str();
}
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import org.junit.*;
import gplx.core.ios.*; import gplx.core.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));}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
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;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
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_.new_missing_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 Get_at(int i) {return list == null ? null : new XmlAtr(list.item(i));}
@gplx.Internal protected XmlAtrList(NamedNodeMap list) {this.list = list;} NamedNodeMap list;
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
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;
}
//#}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
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_.new_exc(e, "xml", "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_.new_exc(e, "xml", "failed to parse xml", "raw", raw);}
catch (IOException e) {throw Err_.new_exc(e, "xml", "failed to parse xml", "raw", raw);}
return doc;
}
public static final String Err_XmlException = "gplx.xmls.XmlException";
}
//#}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import org.junit.*;
public class XmlDoc_tst {
String xml; XmlDoc xdoc; XmlNde xnde;
@Test public void parse() {
xml = String_.Concat("<root/>");
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
( "<root>"
, "<a>"
, "<b/>"
, "<b/>"
, "</a>"
, "</root>"
);
xdoc = XmlDoc_.parse(xml);
xnde = xdoc.Root().SubNdes().Get_at(0);
Tfds.Eq("a", xnde.Name());
Tfds.Eq("<a><b/><b/></a>", xnde.Xml_outer());
}
@Test public void Text_inner() {
xml = String_.Concat
( "<root>"
, "<a>"
, "test me"
, "</a>"
, "</root>"
);
xdoc = XmlDoc_.parse(xml);
xnde = xdoc.Root().SubNdes().Get_at(0);
Tfds.Eq("a", xnde.Name());
Tfds.Eq("test me", xnde.Text_inner());
}
@Test public void Atrs() {
xml = String_.Concat
( "<root atr0=\"0\" atr1=\"1\">"
, "</root>"
);
xdoc = XmlDoc_.parse(xml);
XmlAtrList atrs = xdoc.Root().Atrs();
XmlAtr atr = atrs.Get_at(1);
tst_Atr(atr, "atr1", "1");
atr = atrs.Get_at(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());
}
}

View File

@@ -0,0 +1,141 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public 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.langs.xmls; import gplx.*; import gplx.langs.*;
import gplx.core.consoles.*;
import gplx.core.ios.*;
import gplx.core.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_u8(opts.XmlEnd());
byte[][] nameAry = XtoByteAry(opts.XmlNames());
int partIdx = 0;
// bgn reading file
XmlSplitRdr rdr = new XmlSplitRdr().Init_(xmlUrl, opts.FileSizeMax());
// split hdr: includes <root>, 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_wo_type("could not find any names in first segment");
byte[] dataAry = SplitHdr(rdr.CurAry(), findPos);
if (opts.XmlBgn() != null)
hdr = Bry_.new_u8(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) Console_adp__sys.Instance.Write_str_w_nl(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_.Copy_to(src, 0, hdr, 0, findPos);
byte[] rv = new byte[src.length - findPos];
Array_.Copy_to(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_.Copy_to(src, 0, rv[0], 0, findPos);
rv[1] = new byte[src.length - findPos];
Array_.Copy_to(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_.Max_value;
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_.Max_value;
for (int find : findAry) {
if ((fwd && find > best)
|| (!fwd && find < best))
best = find;
}
if (best == Int_.Max_value) 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_u8(names[i]);
return rv;
}
}

View File

@@ -0,0 +1,27 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
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();
}

View File

@@ -0,0 +1,88 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import org.junit.*;
import gplx.core.ios.*; import gplx.core.texts.*;
public class XmlFileSplitter_tst {
@Before public void setup() {
splitter = new XmlFileSplitter();
Io_mgr.Instance.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("<a><b>", "<b", "<a>", "<b>");
}
@Test public void Split() {
splitter.Opts().FileSizeMax_(30).XmlNames_("<a").XmlEnd_("</root>");
tst_Split
( "<root><a id='1'/><a id='2'/><a id='3'/><a id='4'/><a id='5'/></root>"
, "<root><a id='1'/><a id='2'/></root>"
, "<root><a id='3'/><a id='4'/></root>"
, "<root><a id='5'/></root>"
);
tst_Split
( "<root><a id='1' name='long_text_that_will_force_next_read'/><a id='2'/></root>"
, "<root><a id='1' name='long_text_that_will_force_next_read'/></root>"
, "<root><a id='2'/></root>"
);
}
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.Instance.DeleteDirDeep(tmpDir);
splitter.Opts().StatusFmt_(null).PartDir_(tmpDir);
splitter.Opts().Namer().Ctor_io(tmpDir, "", "fil_{0}.xml", "000");
Io_mgr.Instance.SaveFilStr(xmlFil, txt);
splitter.Split(xmlFil);
Io_url[] tmpFilAry = Io_mgr.Instance.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.Instance.LoadFilStr(tmpFil));
}
}
byte[] byte_(String s) {return Bry_.new_u8(s);}
byte[][] byteAry_(String s) {
byte[][] rv = new byte[1][];
rv[0] = Bry_.new_u8(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_u8(splitter.Hdr()), expdHdr);
Tfds.Eq(String_.new_u8(srcAry), expdSrc);
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
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_.new_exc(e, "xml", "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_.new_exc(e, "xml", "failed to get create transformer");}
transformer.setOutputProperty("omit-xml-declaration", "yes");
return transformer;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import org.w3c.dom.NodeList;
public interface XmlNdeList {
int Count();
XmlNde Get_at(int i);
}
class XmlNdeList_cls_xml implements XmlNdeList {
public int Count() {return list.getLength();}
public XmlNde Get_at(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 Get_at(int i) {return (XmlNde)list.Get_at(i);}
public void Add(XmlNde xnde) {list.Add(xnde);}
@gplx.Internal protected XmlNdeList_cls_list(int count) {list = List_adp_.new_(); list.Resize_bounds(count);} List_adp list;
}
//#}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import gplx.core.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.Instance.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.Instance.OpenStreamRead(url);
curLen = Io_mgr.Instance.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 = Bry_.Resize(curAry, curRead);
}
public void Rls() {stream.Rls();}
}

View File

@@ -0,0 +1,40 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import gplx.core.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.Instance.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();}
}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import gplx.core.primitives.*;
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.Get_at(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.Get_at(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(List_adp_.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.Get_at(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_wo_type("error parsing xml", "xml", xml, "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, "</" + nodeName + ">", headBgn); if (tail == String_.Find_none) throw Err_.new_wo_type("could not find tailPos", "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.Args_add("eqPos", eqPos);
int q1Pos = String_.FindFwd(xml, "\"", q0Pos + 1); if (q1Pos == String_.Find_none) throw xmlErr.Args_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.Args_add("sub_msg", "could not find hdr", "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}

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
package gplx.langs.xmls; import gplx.*; import gplx.langs.*;
import org.junit.*;
public class Xpath__tst {
@Test public void Select_all() {
String xml = String_.Concat
( "<root>"
, "<a>"
, "</a>"
, "<b>"
, "<c/>"
, "<c/>"
, "<c/>"
, "</b>"
, "<a>"
, "</a>"
, "</root>"
);
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());
}
}