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:
25
100_core/src/gplx/langs/dsvs/DsvDataRdrOpts.java
Normal file
25
100_core/src/gplx/langs/dsvs/DsvDataRdrOpts.java
Normal 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() {}
|
||||
}
|
||||
248
100_core/src/gplx/langs/dsvs/DsvDataRdr_.java
Normal file
248
100_core/src/gplx/langs/dsvs/DsvDataRdr_.java
Normal 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;
|
||||
}
|
||||
216
100_core/src/gplx/langs/dsvs/DsvDataRdr_csv_dat_tst.java
Normal file
216
100_core/src/gplx/langs/dsvs/DsvDataRdr_csv_dat_tst.java
Normal 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;
|
||||
}
|
||||
70
100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_dat_tst.java
Normal file
70
100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_dat_tst.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
82
100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_hdr_tst.java
Normal file
82
100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_hdr_tst.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
76
100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_misc_tst.java
Normal file
76
100_core/src/gplx/langs/dsvs/DsvDataRdr_dsv_misc_tst.java
Normal 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"));
|
||||
}
|
||||
}
|
||||
131
100_core/src/gplx/langs/dsvs/DsvDataRdr_layout_tst.java
Normal file
131
100_core/src/gplx/langs/dsvs/DsvDataRdr_layout_tst.java
Normal 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;
|
||||
}
|
||||
115
100_core/src/gplx/langs/dsvs/DsvDataWtr.java
Normal file
115
100_core/src/gplx/langs/dsvs/DsvDataWtr.java
Normal 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() {}
|
||||
}
|
||||
31
100_core/src/gplx/langs/dsvs/DsvDataWtr_.java
Normal file
31
100_core/src/gplx/langs/dsvs/DsvDataWtr_.java
Normal 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();}
|
||||
}
|
||||
100
100_core/src/gplx/langs/dsvs/DsvDataWtr_csv_tst.java
Normal file
100
100_core/src/gplx/langs/dsvs/DsvDataWtr_csv_tst.java
Normal 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() {}
|
||||
}
|
||||
73
100_core/src/gplx/langs/dsvs/DsvDataWtr_tbls_tst.java
Normal file
73
100_core/src/gplx/langs/dsvs/DsvDataWtr_tbls_tst.java
Normal 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_();
|
||||
}
|
||||
44
100_core/src/gplx/langs/dsvs/DsvHeaderList.java
Normal file
44
100_core/src/gplx/langs/dsvs/DsvHeaderList.java
Normal 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
|
||||
;
|
||||
}
|
||||
51
100_core/src/gplx/langs/dsvs/DsvStoreLayout.java
Normal file
51
100_core/src/gplx/langs/dsvs/DsvStoreLayout.java
Normal 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";
|
||||
}
|
||||
52
100_core/src/gplx/langs/dsvs/DsvSymbols.java
Normal file
52
100_core/src/gplx/langs/dsvs/DsvSymbols.java
Normal 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();}
|
||||
}
|
||||
56
100_core/src/gplx/langs/xmls/HierStrBldr.java
Normal file
56
100_core/src/gplx/langs/xmls/HierStrBldr.java
Normal 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;
|
||||
}
|
||||
}
|
||||
47
100_core/src/gplx/langs/xmls/HierStrBldr_tst.java
Normal file
47
100_core/src/gplx/langs/xmls/HierStrBldr_tst.java
Normal 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));}
|
||||
}
|
||||
24
100_core/src/gplx/langs/xmls/XmlAtr.java
Normal file
24
100_core/src/gplx/langs/xmls/XmlAtr.java
Normal 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;
|
||||
}
|
||||
37
100_core/src/gplx/langs/xmls/XmlAtrList.java
Normal file
37
100_core/src/gplx/langs/xmls/XmlAtrList.java
Normal 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;
|
||||
}
|
||||
24
100_core/src/gplx/langs/xmls/XmlDoc.java
Normal file
24
100_core/src/gplx/langs/xmls/XmlDoc.java
Normal 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;
|
||||
}
|
||||
//#}
|
||||
53
100_core/src/gplx/langs/xmls/XmlDoc_.java
Normal file
53
100_core/src/gplx/langs/xmls/XmlDoc_.java
Normal 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";
|
||||
}
|
||||
//#}
|
||||
71
100_core/src/gplx/langs/xmls/XmlDoc_tst.java
Normal file
71
100_core/src/gplx/langs/xmls/XmlDoc_tst.java
Normal 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());
|
||||
}
|
||||
}
|
||||
141
100_core/src/gplx/langs/xmls/XmlFileSplitter.java
Normal file
141
100_core/src/gplx/langs/xmls/XmlFileSplitter.java
Normal 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;
|
||||
}
|
||||
}
|
||||
27
100_core/src/gplx/langs/xmls/XmlFileSplitterOpts.java
Normal file
27
100_core/src/gplx/langs/xmls/XmlFileSplitterOpts.java
Normal 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();
|
||||
}
|
||||
88
100_core/src/gplx/langs/xmls/XmlFileSplitter_tst.java
Normal file
88
100_core/src/gplx/langs/xmls/XmlFileSplitter_tst.java
Normal 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);
|
||||
}
|
||||
}
|
||||
51
100_core/src/gplx/langs/xmls/XmlNde.java
Normal file
51
100_core/src/gplx/langs/xmls/XmlNde.java
Normal 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;
|
||||
}
|
||||
}
|
||||
35
100_core/src/gplx/langs/xmls/XmlNdeList.java
Normal file
35
100_core/src/gplx/langs/xmls/XmlNdeList.java
Normal 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;
|
||||
}
|
||||
//#}
|
||||
51
100_core/src/gplx/langs/xmls/XmlSplitRdr.java
Normal file
51
100_core/src/gplx/langs/xmls/XmlSplitRdr.java
Normal 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();}
|
||||
}
|
||||
40
100_core/src/gplx/langs/xmls/XmlSplitWtr.java
Normal file
40
100_core/src/gplx/langs/xmls/XmlSplitWtr.java
Normal 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();}
|
||||
}
|
||||
106
100_core/src/gplx/langs/xmls/Xpath_.java
Normal file
106
100_core/src/gplx/langs/xmls/Xpath_.java
Normal 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}
|
||||
44
100_core/src/gplx/langs/xmls/Xpath__tst.java
Normal file
44
100_core/src/gplx/langs/xmls/Xpath__tst.java
Normal 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user