@Override public boolean accept(final ASTVisitor visitor) { for (final Expr e : preds) { visitor.enterFocus(); if (!e.accept(visitor)) return false; visitor.exitFocus(); } return root.accept(visitor); }
@Override public VarUsage count(final Var v) { final VarUsage inPreds = super.count(v), inRoot = root.count(v); if (inPreds == VarUsage.NEVER) return inRoot; final long sz = root.size(); return sz >= 0 && sz <= 1 || root.type().zeroOrOne() ? inRoot.plus(inPreds) : VarUsage.MORE_THAN_ONCE; }
@Override public Expr inline(final QueryContext ctx, final VarScope scp, final Var v, final Expr e) throws QueryException { final boolean pr = super.inline(ctx, scp, v, e) != null; final Expr rt = root == null ? null : root.inline(ctx, scp, v, e); if (rt != null) root = rt; return pr || rt != null ? optimize(ctx, scp) : null; }
@Override public final Expr optimize(final QueryContext ctx, final VarScope scp) throws QueryException { // invalidate current context value (will be overwritten by filter) final Value cv = ctx.value; try { // return empty root if (root.isEmpty()) return optPre(null, ctx); // convert filters without numeric predicates to axis paths if (root instanceof AxisPath && !super.has(Flag.FCS)) return ((AxisPath) root.copy(ctx, scp)).addPreds(ctx, scp, preds); // no predicates.. return root; otherwise, do some advanced compilations return preds.length == 0 ? root : opt(ctx); } finally { ctx.value = cv; } }
/** * Compiles the filter expression, excluding the root node. * * @param ctx query context * @return compiled expression */ private Expr opt(final QueryContext ctx) { // evaluate return type final SeqType t = root.type(); // determine number of results and type final long s = root.size(); if (s != -1) { if (pos != null) { size = Math.max(0, s + 1 - pos.min) - Math.max(0, s - pos.max); } else if (last) { size = s > 0 ? 1 : 0; } // no results will remain: return empty sequence if (size == 0) return optPre(null, ctx); type = SeqType.get(t.type, size); } else { type = SeqType.get(t.type, t.zeroOrOne() ? Occ.ZERO_ONE : Occ.ZERO_MORE); } // no numeric predicates.. use simple iterator if (!super.has(Flag.FCS)) return new IterFilter(this); // one single position() or last() function specified: return single value if (preds.length == 1 && (last || pos != null) && root.isValue() && t.one() && (last || pos.min == 1 && pos.max == 1)) return optPre(root, ctx); // only choose deterministic and context-independent offsets; e.g., skip: // (1 to 10)[random:integer(10)] or (1 to 10)[.] boolean off = false; if (preds.length == 1) { final Expr p = preds[0]; final SeqType st = p.type(); off = st.type.isNumber() && st.zeroOrOne() && !p.has(Flag.CTX) && !p.has(Flag.NDT); if (off) type = SeqType.get(type.type, Occ.ZERO_ONE); } // iterator for simple numeric predicate return off || useIterator() ? new IterPosFilter(this, off) : this; }
@Override public final int exprSize() { int sz = 1; for (final Expr e : preds) sz += e.exprSize(); return sz + root.exprSize(); }
@Override public final boolean removable(final Var v) { return root.removable(v) && super.removable(v); }
@Override public final boolean has(final Flag flag) { return root.has(flag) || flag != Flag.CTX && super.has(flag); }