package org.basex.query.expr; import static org.basex.query.QueryText.*; import java.io.IOException; import org.basex.io.serial.Serializer; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.func.Function; import org.basex.query.item.Empty; import org.basex.query.item.Item; import org.basex.query.path.AxisStep; import org.basex.query.util.Var; import org.basex.util.Array; import org.basex.util.InputInfo; import org.basex.util.list.ObjList; /** * Abstract predicate expression, implemented by {@link Filter} and * {@link AxisStep}. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public abstract class Preds extends ParseExpr { /** Predicates. */ public Expr[] preds; /** Compilation: first predicate uses last function. */ public boolean last; /** Compilation: first predicate uses position. */ public Pos pos; /** * Constructor. * @param ii input info * @param p predicates */ protected Preds(final InputInfo ii, final Expr[] p) { super(ii); preds = p; } @Override public Expr comp(final QueryContext ctx) throws QueryException { for(final Expr p : preds) checkUp(p, ctx); for(int p = 0; p < preds.length; ++p) { Expr pr = preds[p].comp(ctx).compEbv(ctx); pr = Pos.get(CmpV.Op.EQ, pr, pr, input); // position() = last() -> last() if(pr instanceof CmpG || pr instanceof CmpV) { final Cmp cmp = (Cmp) pr; if(cmp.expr[0].isFunction(Function.POSITION) && cmp.expr[1].isFunction(Function.LAST)) { if(cmp instanceof CmpG && ((CmpG) cmp).op == CmpG.Op.EQ || cmp instanceof CmpV && ((CmpV) cmp).op == CmpV.Op.EQ) { ctx.compInfo(OPTWRITE, pr); pr = cmp.expr[1]; } } } if(pr.isValue()) { if(!pr.ebv(ctx, input).bool(input)) { ctx.compInfo(OPTREMOVE, description(), pr); return Empty.SEQ; } ctx.compInfo(OPTREMOVE, description(), pr); preds = Array.delete(preds, p--); } else if(pr instanceof And && !pr.uses(Use.POS)) { // replace AND expression with predicates (don't swap position tests) ctx.compInfo(OPTPRED, pr.description()); final Expr[] and = ((And) pr).expr; final int m = and.length - 1; final ObjList<Expr> tmp = new ObjList<Expr>(preds.length + m); for(int i = 0; i < p; i++) tmp.add(preds[i]); for(final Expr a : and) { // wrap test with boolean() if the result is numeric tmp.add(Function.BOOLEAN.get(input, a).compEbv(ctx)); } for(int i = p + 1; i < preds.length; i++) tmp.add(preds[i]); preds = tmp.toArray(new Expr[tmp.size()]); } else { preds[p] = pr; } } return this; } /** * Checks if this expression can be evaluated in an iterative manner. * This is possible if no predicate, or only the first, is positional, or * if a single {@code last()} predicate is specified. * @return result of check */ protected boolean useIterator() { // numeric predicate pos = preds[0] instanceof Pos ? (Pos) preds[0] : null; last = preds[0].isFunction(Function.LAST); boolean np1 = true; boolean np2 = true; for(int p = 0; p < preds.length; p++) { final boolean np = !preds[p].type().mayBeNum() && !preds[p].uses(Use.POS); np1 &= np; if(p > 0) np2 &= np; } return np1 || pos != null && np2 || last && preds.length == 1; } /** * Checks if the predicates are successful for the specified item. * @param it item to be checked * @param ctx query context * @return result of check * @throws QueryException query exception */ public boolean preds(final Item it, final QueryContext ctx) throws QueryException { if(preds.length == 0) return true; // set context item and position Item i = null; for(final Expr p : preds) { ctx.value = it; i = p.test(ctx, input); if(i == null) return false; } // item accepted.. adopt last scoring value it.score(i.score()); return true; } @Override public boolean uses(final Use u) { for(final Expr p : preds) { if(u == Use.POS && p.type().mayBeNum() || p.uses(u)) return true; } return false; } @Override public int count(final Var v) { int c = 0; for(final Expr p : preds) c += p.count(v); return c; } @Override public boolean removable(final Var v) { for(final Expr p : preds) if(p.count(v) != 0) return false; return true; } @Override public Expr remove(final Var v) { for(int p = 0; p < preds.length; ++p) preds[p] = preds[p].remove(v); return this; } @Override public void plan(final Serializer ser) throws IOException { for(final Expr p : preds) p.plan(ser); } @Override public String toString() { final StringBuilder sb = new StringBuilder(); for(final Expr e : preds) sb.append("[" + e + ']'); return sb.toString(); } }