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.StandardFunc;
import org.basex.query.func.Function;
import org.basex.query.item.Bln;
import org.basex.query.item.Item;
import org.basex.query.item.SeqType;
import org.basex.query.item.Value;
import org.basex.query.iter.Iter;
import org.basex.util.InputInfo;
/**
* If expression.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class If extends Arr {
/**
* Constructor.
* @param ii input info
* @param e expression
* @param t then clause
* @param s else clause
*/
public If(final InputInfo ii, final Expr e, final Expr t, final Expr s) {
super(ii, e, t, s);
}
@Override
public Expr comp(final QueryContext ctx) throws QueryException {
// check for updating expressions
expr[0] = checkUp(expr[0], ctx).comp(ctx).compEbv(ctx);
checkUp(ctx, expr[1], expr[2]);
// static condition: return branch in question
if(expr[0].isValue()) return optPre(eval(ctx).comp(ctx), ctx);
// compile both branches
for(int e = 1; e != expr.length; ++e) expr[e] = expr[e].comp(ctx);
// if A then B else B -> B (errors in A will be ignored)
if(expr[1].sameAs(expr[2])) return optPre(expr[1], ctx);
// if not(A) then B else C -> if A then C else B
if(expr[0].isFunction(Function.NOT)) {
ctx.compInfo(OPTWRITE, this);
expr[0] = ((StandardFunc) expr[0]).expr[0];
final Expr tmp = expr[1];
expr[1] = expr[2];
expr[2] = tmp;
}
// if A then true() else false() -> boolean(A)
if(expr[1] == Bln.TRUE && expr[2] == Bln.FALSE) {
ctx.compInfo(OPTWRITE, this);
return compBln(expr[0]);
}
// if A then false() else true() -> not(A)
// if A then B else true() -> not(A) or B
if(expr[1].type().eq(SeqType.BLN) && expr[2] == Bln.TRUE) {
ctx.compInfo(OPTWRITE, this);
final Expr e = Function.NOT.get(input, expr[0]);
return expr[1] == Bln.FALSE ? e : new Or(input, e, expr[1]);
}
type = expr[1].type().intersect(expr[2].type());
return this;
}
@Override
public Iter iter(final QueryContext ctx) throws QueryException {
return ctx.iter(eval(ctx));
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
return ctx.value(eval(ctx));
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
return eval(ctx).item(ctx, input);
}
/**
* Evaluates the condition and returns the correct expression.
* @param ctx query context
* @return resulting expression
* @throws QueryException query exception
*/
private Expr eval(final QueryContext ctx) throws QueryException {
return expr[expr[0].ebv(ctx, input).bool(input) ? 1 : 2];
}
@Override
public boolean isVacuous() {
return expr[1].isVacuous() || expr[2].isVacuous();
}
@Override
public void plan(final Serializer ser) throws IOException {
ser.openElement(this);
expr[0].plan(ser);
ser.openElement(THN);
expr[1].plan(ser);
ser.closeElement();
ser.openElement(ELS);
expr[2].plan(ser);
ser.closeElement();
ser.closeElement();
}
@Override
public String toString() {
return IF + '(' + expr[0] + ") " + THEN + ' ' + expr[1] + ' ' +
ELSE + ' ' + expr[2];
}
@Override
public Expr markTailCalls() {
expr[1] = expr[1].markTailCalls();
expr[2] = expr[2].markTailCalls();
return this;
}
}