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.item.Item;
import org.basex.query.item.Value;
import org.basex.query.iter.Iter;
import org.basex.query.util.Var;
import org.basex.util.InputInfo;
/**
* Switch expression.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class Switch extends ParseExpr {
/** Cases. */
private final SwitchCase[] cases;
/** Condition. */
private Expr cond;
/**
* Constructor.
* @param ii input info
* @param c condition
* @param sc cases (last one is default case)
*/
public Switch(final InputInfo ii, final Expr c, final SwitchCase[] sc) {
super(ii);
cases = sc;
cond = c;
}
@Override
public Expr comp(final QueryContext ctx) throws QueryException {
// operands may not be updating
cond = checkUp(cond, ctx).comp(ctx);
for(final SwitchCase sc : cases) sc.comp(ctx);
// check if none or all return expressions are updating
final Expr[] tmp = new Expr[cases.length];
for(int i = 0; i < tmp.length; ++i) tmp[i] = cases[i].expr[0];
checkUp(ctx, tmp);
// check if expression can be pre-evaluated
Expr ex = this;
if(cond.isValue()) {
final Item it = cond.item(ctx, input);
LOOP:
for(final SwitchCase sc : cases) {
final int sl = sc.expr.length;
for(int e = 1; e < sl; e++) {
if(!sc.expr[e].isValue()) break LOOP;
// includes check for empty sequence (null reference)
final Item cs = sc.expr[e].item(ctx, input);
if(it == cs || cs != null && it != null && it.equiv(input, cs)) {
ex = sc.expr[0];
break LOOP;
}
}
if(sl == 1) ex = sc.expr[0];
}
}
if(ex != this) return optPre(ex, ctx);
// expression could not be pre-evaluated
type = cases[0].expr[0].type();
for(int c = 1; c < cases.length; c++) {
type = type.intersect(cases[c].expr[0].type());
}
return ex;
}
@Override
public Iter iter(final QueryContext ctx) throws QueryException {
return ctx.iter(getCase(ctx));
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
return ctx.value(getCase(ctx));
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
return getCase(ctx).item(ctx, ii);
}
@Override
public boolean uses(final Use u) {
for(final SwitchCase sc : cases) if(sc.uses(u)) return true;
return cond.uses(u);
}
@Override
public int count(final Var v) {
int c = cond.count(v);
for(final SwitchCase sc : cases) c += sc.count(v);
return c;
}
@Override
public boolean removable(final Var v) {
for(final SwitchCase sc : cases) if(!sc.removable(v)) return false;
return cond.removable(v);
}
@Override
public Expr remove(final Var v) {
for(final SwitchCase sc : cases) sc.remove(v);
cond = cond.remove(v);
return this;
}
/**
* Chooses the selected {@code case} expression.
* @param ctx query context
* @return case expression
* @throws QueryException query exception
*/
private Expr getCase(final QueryContext ctx) throws QueryException {
final Item it = cond.item(ctx, input);
for(final SwitchCase sc : cases) {
final int sl = sc.expr.length;
for(int e = 1; e < sl; e++) {
// includes check for empty sequence (null reference)
final Item cs = sc.expr[e].item(ctx, input);
if(it == cs || it != null && cs != null && it.equiv(input, cs))
return sc.expr[0];
}
if(sl == 1) return sc.expr[0];
}
// will never be evaluated
return null;
}
@Override
public void plan(final Serializer ser) throws IOException {
ser.openElement(this);
cond.plan(ser);
for(final SwitchCase sc : cases) sc.plan(ser);
ser.closeElement();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(SWITCH + PAR1 + cond + PAR2);
for(final SwitchCase sc : cases) sb.append(sc.toString());
return sb.toString();
}
@Override
public Expr markTailCalls() {
for(final SwitchCase sc : cases) sc.markTailCalls();
return this;
}
}