/** * Returns all summary path nodes for the specified location step or {@code null} if nodes cannot * be retrieved or are found on different levels. * * @param data data reference * @param last last step to be checked * @return path nodes */ private ArrayList<PathNode> pathNodes(final Data data, final int last) { // skip request if no path index exists or might be out-of-date if (!data.meta.uptodate) return null; ArrayList<PathNode> nodes = data.paths.root(); for (int s = 0; s <= last; s++) { // only follow axis steps final Step curr = axisStep(s); if (curr == null) return null; final boolean desc = curr.axis == DESC; if (!desc && curr.axis != CHILD || curr.test.kind != Kind.NAME) return null; final int name = data.elemNames.id(curr.test.name.local()); final ArrayList<PathNode> tmp = new ArrayList<>(); for (final PathNode node : PathSummary.desc(nodes, desc)) { if (node.kind == Data.ELEM && name == node.name) { // skip test if an element name occurs on different levels if (!tmp.isEmpty() && tmp.get(0).level() != node.level()) return null; tmp.add(node); } } if (tmp.isEmpty()) return null; nodes = tmp; } return nodes; }
/** * Returns all summary path nodes for the specified location step or {@code null} if nodes cannot * be retrieved or are found on different levels. * * @param data data reference * @param l last step to be checked * @return path nodes */ ArrayList<PathNode> pathNodes(final Data data, final int l) { // skip request if no path index exists or might be out-of-date if (!data.meta.uptodate) return null; ArrayList<PathNode> in = data.paths.root(); for (int s = 0; s <= l; ++s) { final Step curr = axisStep(s); if (curr == null) return null; final boolean desc = curr.axis == DESC; if (!desc && curr.axis != CHILD || curr.test.mode != Mode.LN) return null; final int name = data.tagindex.id(curr.test.name.local()); final ArrayList<PathNode> al = new ArrayList<>(); for (final PathNode pn : PathSummary.desc(in, desc)) { if (pn.kind == Data.ELEM && name == pn.name) { // skip test if a tag is found on different levels if (!al.isEmpty() && al.get(0).level() != pn.level()) return null; al.add(pn); } } if (al.isEmpty()) return null; in = al; } return in; }
/** * Converts descendant to child steps. * * @param qc query context * @param rt root value * @return original or new expression */ private Expr children(final QueryContext qc, final Value rt) { // skip if index does not exist or is out-dated, or if several namespaces occur in the input final Data data = rt.data(); if (data == null || !data.meta.uptodate || data.nspaces.globalNS() == null) return this; Path path = this; final int sl = steps.length; for (int s = 0; s < sl; s++) { // don't allow predicates in preceding location steps final Step prev = s > 0 ? axisStep(s - 1) : null; if (prev != null && prev.preds.length != 0) break; // ignore axes other than descendant, or numeric predicates final Step curr = axisStep(s); if (curr == null || curr.axis != DESC || curr.has(Flag.FCS)) continue; // check if child steps can be retrieved for current step ArrayList<PathNode> nodes = pathNodes(data, s); if (nodes == null) continue; // cache child steps final ArrayList<QNm> qnm = new ArrayList<>(); while (nodes.get(0).parent != null) { QNm nm = new QNm(data.elemNames.key(nodes.get(0).name)); // skip children with prefixes if (nm.hasPrefix()) return this; for (final PathNode p : nodes) { if (nodes.get(0).name != p.name) nm = null; } qnm.add(nm); nodes = PathSummary.parent(nodes); } qc.compInfo(OPTCHILD, steps[s]); // build new steps int ts = qnm.size(); final Expr[] stps = new Expr[ts + sl - s - 1]; for (int t = 0; t < ts; t++) { final Expr[] preds = t == ts - 1 ? ((Preds) steps[s]).preds : new Expr[0]; final QNm nm = qnm.get(ts - t - 1); final NameTest nt = nm == null ? new NameTest(false) : new NameTest(nm, Kind.NAME, false, null); stps[t] = Step.get(info, CHILD, nt, preds); } while (++s < sl) stps[ts++] = steps[s]; path = get(info, root, stps); break; } // check if all steps yield results; if not, return empty sequence final ArrayList<PathNode> nodes = pathNodes(qc); if (nodes != null && nodes.isEmpty()) { qc.compInfo(OPTPATH, path); return Empty.SEQ; } return path; }
/** * Binds the annotated variables. * * @param http http context * @param arg argument array * @throws QueryException query exception * @throws IOException I/O exception */ void bind(final HTTPContext http, final Expr[] arg) throws QueryException, IOException { // bind variables from segments for (int s = 0; s < path.size; s++) { final Matcher m = TEMPLATE.matcher(path.segment[s]); if (!m.find()) continue; final QNm qnm = new QNm(token(m.group(1)), context); bind(qnm, arg, new Atm(http.segment(s))); } // cache request body final String ct = http.contentType(); IOContent body = null; if (requestBody != null) { body = cache(http, null); try { // bind request body in the correct format body.name(http.method + IO.XMLSUFFIX); bind(requestBody, arg, Parser.item(body, context.context.prop, ct)); } catch (final IOException ex) { error(INPUT_CONV, ex); } } // bind query parameters final Map<String, String[]> params = http.params(); for (final RestXqParam rxp : queryParams) bind(rxp, arg, params.get(rxp.key)); // bind form parameters if (!formParams.isEmpty()) { if (MimeTypes.APP_FORM.equals(ct)) { // convert parameters encoded in a form body = cache(http, body); addParams(body.toString(), params); } for (final RestXqParam rxp : formParams) bind(rxp, arg, params.get(rxp.key)); } // bind header parameters for (final RestXqParam rxp : headerParams) { final StringList sl = new StringList(); final Enumeration<?> en = http.req.getHeaders(rxp.key); while (en.hasMoreElements()) { for (final String s : en.nextElement().toString().split(", *")) sl.add(s); } bind(rxp, arg, sl.toArray()); } // bind cookie parameters final Cookie[] ck = http.req.getCookies(); for (final RestXqParam rxp : cookieParams) { String v = null; if (ck != null) { for (final Cookie c : ck) { if (rxp.key.equals(c.getName())) v = c.getValue(); } } if (v == null) bind(rxp, arg); else bind(rxp, arg, v); } }