/** * Inverts a location path. * * @param r new root node * @param curr current location step * @return inverted path */ public final AxisPath invertPath(final Expr r, final AxisStep curr) { // hold the steps to the end of the inverted path int s = steps.length; final Expr[] e = new Expr[s--]; // add predicates of last step to new root node final Expr rt = step(s).preds.length != 0 ? new Filter(input, r, step(s).preds) : r; // add inverted steps in a backward manner int c = 0; while (--s >= 0) { e[c++] = AxisStep.get(input, step(s + 1).axis.invert(), step(s).test, step(s).preds); } e[c] = AxisStep.get(input, step(s + 1).axis.invert(), curr.test); return new AxisPath(input, rt, e); }
@Override public final Expr addText(final QueryContext ctx) { final AxisStep s = step(steps.length - 1); if (s.preds.length != 0 || !s.axis.down || s.test.type == NodeType.ATT || s.test.test != Name.NAME && s.test.test != Name.STD) return this; final Data data = ctx.data(); if (data == null || !data.meta.uptodate) return this; final Stats stats = data.tagindex.stat(data.tagindex.id(s.test.name.local())); if (stats != null && stats.isLeaf()) { steps = Array.add(steps, AxisStep.get(input, Axis.CHILD, Test.TXT)); ctx.compInfo(OPTTEXT, this); } return this; }
/** * Returns a copy of the path expression. * * @return copy */ public final Path copy() { final Expr[] stps = new Expr[steps.length]; for (int s = 0; s < steps.length; ++s) stps[s] = AxisStep.get(step(s)); return get(input, root, stps); }
/** * If possible, returns an expression which accesses the index. Otherwise, returns the original * expression. * * @param ctx query context * @param data data reference * @return resulting expression * @throws QueryException query exception */ private Expr index(final QueryContext ctx, final Data data) throws QueryException { // disallow relative paths and numeric predicates if (root == null || uses(Use.POS)) return this; // cache index access costs IndexContext ics = null; // cheapest predicate and step int pmin = 0; int smin = 0; // check if path can be converted to an index access for (int s = 0; s < steps.length; ++s) { // find cheapest index access final AxisStep stp = step(s); if (!stp.axis.down) break; // check if resulting index path will be duplicate free final boolean i = pathNodes(data, s) != null; // choose cheapest index access for (int p = 0; p < stp.preds.length; ++p) { final IndexContext ic = new IndexContext(ctx, data, stp, i); if (!stp.preds[p].indexAccessible(ic)) continue; if (ic.costs() == 0) { if (ic.not) { // not operator... accept all results stp.preds[p] = Bln.TRUE; continue; } // no results... ctx.compInfo(OPTNOINDEX, this); return Empty.SEQ; } if (ics == null || ics.costs() > ic.costs()) { ics = ic; pmin = p; smin = s; } } } // skip if no index access is possible, or if it is too expensive if (ics == null || ics.costs() > data.meta.size) return this; // replace expressions for index access final AxisStep stp = step(smin); final Expr ie = stp.preds[pmin].indexEquivalent(ics); if (ics.seq) { // sequential evaluation; do not invert path stp.preds[pmin] = ie; } else { // inverted path, which will be represented as predicate AxisStep[] invSteps = {}; // collect remaining predicates final Expr[] newPreds = new Expr[stp.preds.length - 1]; int c = 0; for (int p = 0; p != stp.preds.length; ++p) { if (p != pmin) newPreds[c++] = stp.preds[p]; } // check if path before index step needs to be inverted and traversed final Test test = DocTest.get(ctx, data); boolean inv = true; if (test == Test.DOC && data.meta.pathindex && data.meta.uptodate) { int j = 0; for (; j <= smin; ++j) { // invert if axis is not a child or has predicates final AxisStep s = axisStep(j); if (s == null) break; if (s.axis != Axis.CHILD || s.preds.length > 0 && j != smin) break; if (s.test.test == Name.ALL || s.test.test == null) continue; if (s.test.test != Name.NAME) break; // support only unique paths with nodes on the correct level final int name = data.tagindex.id(s.test.name.local()); final ObjList<PathNode> pn = data.paths.desc(name, Data.ELEM); if (pn.size() != 1 || pn.get(0).level() != j + 1) break; } inv = j <= smin; } // invert path before index step if (inv) { for (int j = smin; j >= 0; --j) { final Axis ax = step(j).axis.invert(); if (ax == null) break; if (j != 0) { final AxisStep prev = step(j - 1); invSteps = Array.add(invSteps, AxisStep.get(input, ax, prev.test, prev.preds)); } else { // add document test for collections and axes other than ancestors if (test != Test.DOC || ax != Axis.ANC && ax != Axis.ANCORSELF) invSteps = Array.add(invSteps, AxisStep.get(input, ax, test)); } } } // create resulting expression final AxisPath result; final boolean simple = invSteps.length == 0 && newPreds.length == 0; if (ie instanceof AxisPath) { result = (AxisPath) ie; } else if (smin + 1 < steps.length || !simple) { result = simple ? new AxisPath(input, ie) : new AxisPath(input, ie, AxisStep.get(input, Axis.SELF, Test.NOD)); } else { return ie; } // add remaining predicates to last step final int ls = result.steps.length - 1; if (ls >= 0) { result.steps[ls] = result.step(ls).addPreds(newPreds); // add inverted path as predicate to last step if (invSteps.length != 0) result.steps[ls] = result.step(ls).addPreds(Path.get(input, null, invSteps)); } // add remaining steps for (int s = smin + 1; s < steps.length; ++s) { result.steps = Array.add(result.steps, steps[s]); } return result; } return this; }