/** * 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; }
/** * 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; }
/** Finishes the index creation. */ void finish() { if (reorder == null) return; for (int i = 1; i < reorder.size(); i++) { if (reorder.get(i)) Arrays.sort(idsList.get(i), 0, lenList.get(i)); } reorder = null; }
@Override public IndexIterator iter(final IndexToken token) { final int id = values.id(token.get()); if (id == 0) return IndexIterator.EMPTY; final int len = lenList.get(id); final int[] ids = idsList.get(id), pres; if (data.meta.updindex) { final IntList tmp = new IntList(); for (int i = 0; i < len; ++i) tmp.add(data.pre(ids[i])); pres = tmp.sort().finish(); } else { pres = ids; } return new IndexIterator() { int p; @Override public boolean more() { return p < len; } @Override public int pre() { return pres[p++]; } @Override public int size() { return len; } }; }
/** * 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; }
/** * Removes values from the index. * * @param key key * @param vals sorted values */ void delete(final byte[] key, final int... vals) { final int id = values.id(key), vl = vals.length, l = lenList.get(id), s = l - vl; final int[] ids = idsList.get(id); for (int i = 0, n = 0, v = 0; i < l; i++) { if (v == vl || ids[i] != vals[v]) ids[n++] = ids[i]; else v++; } lenList.set(id, s); if (s == 0) idsList.set(id, null); }
/** * Checks if steps before index step need to be inverted and traversed. * * @param data data reference * @param iStep index step * @return result of check */ private boolean predSteps(final Data data, final int iStep) { for (int s = iStep; s >= 0; s--) { final Step step = axisStep(s); // ensure that the index step does not use wildcard if (step.test.kind == Kind.WILDCARD && s != iStep) continue; // consider child steps with name test and without predicates if (step.test.kind != Kind.NAME || step.axis != Axis.CHILD || s != iStep && step.preds.length > 0) return true; // support only unique paths with nodes on the correct level final ArrayList<PathNode> pn = data.paths.desc(step.test.name.local()); if (pn.size() != 1 || pn.get(0).level() != s + 1) return true; } return false; }
/** * Adds values to the index. * * @param key key to be indexed * @param vals sorted values */ void add(final byte[] key, final int... vals) { // token index: add values. otherwise, reference existing values final int id = type == IndexType.TOKEN ? values.put(key) : values.id(key), vl = vals.length; // updatable index: if required, resize existing arrays while (idsList.size() < id + 1) idsList.add(null); if (lenList.size() < id + 1) lenList.set(id, 0); final int len = lenList.get(id), size = len + vl; int[] ids = idsList.get(id); if (ids == null) { ids = vals; } else { if (ids.length < size) ids = Arrays.copyOf(ids, Array.newSize(size)); System.arraycopy(vals, 0, ids, len, vl); if (ids[len - 1] > vals[0]) { if (reorder == null) reorder = new BoolList(values.size()); reorder.set(id, true); } } idsList.set(id, ids); lenList.set(id, size); }
/** * Returns a string representation of the index structure. * * @param all include database contents in the representation. During updates, database lookups * must be avoided, as the data structures will be inconsistent. * @return string */ public String toString(final boolean all) { final TokenBuilder tb = new TokenBuilder(); tb.addExt(type).add(" INDEX, '").add(data.meta.name).add("':\n"); final int s = lenList.size(); for (int m = 1; m < s; m++) { final int len = lenList.get(m); if (len == 0) continue; final int[] ids = idsList.get(m); tb.add(" ").addInt(m); if (all) tb.add(", key: \"").add(data.text(data.pre(ids[0]), type == IndexType.TEXT)).add('"'); tb.add(", ids"); if (all) tb.add("/pres"); tb.add(": "); for (int n = 0; n < len; n++) { if (n != 0) tb.add(","); tb.addInt(ids[n]); if (all) tb.add('/').addInt(data.pre(ids[n])); } tb.add("\n"); } return tb.toString(); }
/** * Converts descendant to child steps. * * @param ctx query context * @param data data reference * @return path */ Expr children(final QueryContext ctx, final Data data) { // skip path check if no path index exists, or if it is out-of-date if (!data.meta.uptodate || data.nspaces.globalNS() == null) return this; Path path = this; for (int s = 0; s < steps.length; ++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> pn = pathNodes(data, s); if (pn == null) continue; // cache child steps final ArrayList<QNm> qnm = new ArrayList<>(); while (pn.get(0).par != null) { QNm nm = new QNm(data.tagindex.key(pn.get(0).name)); // skip children with prefixes if (nm.hasPrefix()) return this; for (final PathNode p : pn) { if (pn.get(0).name != p.name) nm = null; } qnm.add(nm); pn = PathSummary.parent(pn); } ctx.compInfo(OPTCHILD, steps[s]); // build new steps int ts = qnm.size(); final Expr[] stps = new Expr[ts + steps.length - 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, Mode.LN, false, null); stps[t] = Step.get(info, CHILD, nt, preds); } while (++s < steps.length) stps[ts++] = steps[s]; path = get(info, root, stps); break; } // check if the all children in the path exist; don't test with namespaces if (data.nspaces.size() == 0) { LOOP: for (int s = 0; s < path.steps.length; ++s) { // only verify child steps; ignore namespaces final Step st = path.axisStep(s); if (st == null || st.axis != CHILD) break; if (st.test.mode == Mode.ALL || st.test.mode == null) continue; if (st.test.mode != Mode.LN) break; // check if one of the addressed nodes is on the correct level final int name = data.tagindex.id(st.test.name.local()); for (final PathNode pn : data.paths.desc(name, Data.ELEM)) { if (pn.level() == s + 1) continue LOOP; } ctx.compInfo(OPTPATH, path); return Empty.SEQ; } } return path; }