package org.basex.query.path; import static org.basex.query.util.Err.*; import org.basex.data.Data; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.item.Empty; import org.basex.query.item.Item; import org.basex.query.item.ANode; import org.basex.query.item.NodeType; import org.basex.query.item.SeqType; import org.basex.query.item.Value; import org.basex.query.iter.Iter; import org.basex.query.iter.NodeCache; import org.basex.query.iter.ItemCache; import org.basex.query.util.Var; import org.basex.util.InputInfo; /** * Mixed path expression. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class MixedPath extends Path { /** * Constructor. * @param ii input info * @param r root expression; can be a {@code null} reference * @param s axis steps */ public MixedPath(final InputInfo ii, final Expr r, final Expr... s) { super(ii, r, s); } @Override protected Expr compPath(final QueryContext ctx) throws QueryException { for(final Expr s : steps) checkUp(s, ctx); final AxisStep v = voidStep(steps); if(v != null) COMPSELF.thrw(input, v); for(int s = 0; s != steps.length; ++s) { steps[s] = steps[s].comp(ctx); if(steps[s].isEmpty()) return Empty.SEQ; } optSteps(ctx); // rewrite to child steps final Data data = ctx.data(); if(data != null && ctx.value.type == NodeType.DOC) { final Expr e = children(ctx, data); // return optimized expression if(e != this) return e.comp(ctx); } size = size(ctx); type = SeqType.get(steps[steps.length - 1].type().type, size); return this; } @Override public Iter iter(final QueryContext ctx) throws QueryException { // creates an iterator from the root value final Value v = root != null ? ctx.value(root) : checkCtx(ctx); Iter res = v.iter(); final Value cv = ctx.value; final long cs = ctx.size; final long cp = ctx.pos; try { // loop through all expressions final int el = steps.length; for(int ex = 0; ex < el; ex++) { final Expr e = steps[ex]; final boolean last = ex + 1 == el; final ItemCache ic = new ItemCache(); // this flag indicates if the resulting items contain nodes boolean nodes = false; ctx.size = res.size(); ctx.pos = 1; // loop through all input items for(Item it; (it = res.next()) != null;) { if(!it.type.isNode()) NODESPATH.thrw(input, this, it.type); ctx.value = it; // loop through all resulting items final Iter ir = ctx.iter(e); for(Item i; (i = ir.next()) != null;) { // set node flag if(ic.size() == 0) nodes = i.type.isNode(); // check if both nodes and atomic values occur in last result else if(last && nodes != i.type.isNode()) EVALNODESVALS.thrw(input); ic.add(i); } ctx.pos++; } if(nodes) { // remove potential duplicates from node sets final NodeCache nc = new NodeCache().random(); for(Item it; (it = ic.next()) != null;) nc.add((ANode) it); res = nc.value().cache(); } else { res = ic; } } return res; } finally { ctx.value = cv; ctx.size = cs; ctx.pos = cp; } } @Override public int count(final Var v) { int c = 0; for(final Expr e : steps) c += e.count(v); return c + super.count(v); } @Override public boolean removable(final Var v) { for(final Expr e : steps) if(e.uses(Use.VAR)) return false; return true; } @Override public Expr remove(final Var v) { for(int e = 0; e != steps.length; ++e) steps[e] = steps[e].remove(v); return super.remove(v); } }