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