You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnosygnu_xowa/100_core/src/gplx/langs/xmls/Xpath_.java

107 lines
4.6 KiB

/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public 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 Keyval_hash 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);
Keyval_hash 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 Keyval_hash ExtractNodeVals(String xml, Err xmlErr) {
Keyval_hash rv = new Keyval_hash();
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}