package org.basex.query.func;
import static org.basex.query.util.Err.*;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.QueryText;
import org.basex.query.expr.Cmp;
import org.basex.query.expr.CmpG;
import org.basex.query.expr.CmpV;
import org.basex.query.expr.Expr;
import org.basex.query.item.Bln;
import org.basex.query.item.Item;
import org.basex.query.item.SeqType;
import org.basex.query.item.SeqType.Occ;
import org.basex.query.item.Value;
import org.basex.query.iter.Iter;
import org.basex.query.util.Compare;
import org.basex.util.InputInfo;
/**
* Simple functions.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class FNSimple extends StandardFunc {
/**
* Constructor.
* @param ii input info
* @param f function definition
* @param e arguments
*/
public FNSimple(final InputInfo ii, final Function f, final Expr... e) {
super(ii, f, e);
}
@Override
public Iter iter(final QueryContext ctx) throws QueryException {
switch(sig) {
case ONE_OR_MORE:
final Iter ir = expr[0].iter(ctx);
final long len = ir.size();
if(len == 0) throw EXPECTOM.thrw(input);
if(len > 0) return ir;
return new Iter() {
private boolean first = true;
@Override
public Item next() throws QueryException {
final Item it = ir.next();
if(first) {
if(it == null) throw EXPECTOM.thrw(input);
first = false;
}
return it;
}
};
case UNORDERED:
return ctx.iter(expr[0]);
default:
return super.iter(ctx);
}
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
switch(sig) {
case ONE_OR_MORE:
final Value val = ctx.value(expr[0]);
if(val.isEmpty()) throw EXPECTOM.thrw(input);
return val;
case UNORDERED:
return ctx.value(expr[0]);
default:
return super.value(ctx);
}
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
final Expr e = expr.length == 1 ? expr[0] : null;
switch(sig) {
case FALSE:
return Bln.FALSE;
case TRUE:
return Bln.TRUE;
case EMPTY:
return Bln.get(e.iter(ctx).next() == null);
case EXISTS:
return Bln.get(e.iter(ctx).next() != null);
case BOOLEAN:
return Bln.get(e.ebv(ctx, input).bool(input));
case NOT:
return Bln.get(!e.ebv(ctx, input).bool(input));
case DEEP_EQUAL:
return Bln.get(deep(ctx));
case ZERO_OR_ONE:
Iter ir = e.iter(ctx);
Item it = ir.next();
if(it != null && ir.next() != null) EXPECTZ0.thrw(input);
return it;
case EXACTLY_ONE:
ir = e.iter(ctx);
it = ir.next();
if(it == null || ir.next() != null) EXPECTO.thrw(input);
return it;
default:
return super.item(ctx, ii);
}
}
@Override
public Expr cmp(final QueryContext ctx) {
// all functions have at least 1 argument
final Expr e = expr[0];
switch(sig) {
case EMPTY:
case EXISTS:
// ignore non-deterministic expressions (e.g.: error())
return e.size() == -1 || e.uses(Use.NDT) || e.uses(Use.CNS) ? this :
Bln.get(sig == Function.EMPTY ^ e.size() != 0);
case BOOLEAN:
// simplify, e.g.: if(boolean(A)) -> if(A)
return e.type().eq(SeqType.BLN) ? e : this;
case NOT:
if(e.isFunction(Function.EMPTY)) {
// simplify: not(empty(A)) -> exists(A)
ctx.compInfo(QueryText.OPTWRITE, this);
expr = ((StandardFunc) e).expr;
sig = Function.EXISTS;
} else if(e.isFunction(Function.EXISTS)) {
// simplify: not(exists(A)) -> empty(A)
ctx.compInfo(QueryText.OPTWRITE, this);
expr = ((StandardFunc) e).expr;
sig = Function.EMPTY;
} else if(e instanceof CmpV || e instanceof CmpG) {
// simplify: not('a' = 'b') -> 'a' != 'b'
final Cmp c = ((Cmp) e).invert();
return c == e ? this : c;
} else if(e.isFunction(Function.NOT)) {
// simplify: not(not(A)) -> boolean(A)
return compBln(((StandardFunc) e).expr[0]);
} else {
// simplify, e.g.: not(boolean(A)) -> not(A)
expr[0] = e.compEbv(ctx);
}
return this;
case ZERO_OR_ONE:
type = SeqType.get(e.type().type, Occ.ZO);
return e.type().zeroOrOne() ? e : this;
case EXACTLY_ONE:
type = SeqType.get(e.type().type, Occ.O);
return e.type().one() ? e : this;
case ONE_OR_MORE:
type = SeqType.get(e.type().type, Occ.OM);
return !e.type().mayBeZero() ? e : this;
case UNORDERED:
return e;
default:
return this;
}
}
@Override
public Expr compEbv(final QueryContext ctx) {
// all functions have at least 1 argument
final Expr e = expr[0];
Expr ex = this;
if(sig == Function.BOOLEAN) {
// (test)[boolean(A)] -> (test)[A]
if(!e.type().mayBeNum()) ex = e;
} else if(sig == Function.EXISTS) {
// if(exists(node*)) -> if(node*)
if(e.type().type.isNode() || e.size() > 0) ex = e;
}
if(ex != this) ctx.compInfo(QueryText.OPTWRITE, this);
return ex;
}
/**
* Checks items for deep equality.
* @param ctx query context
* @return result of check
* @throws QueryException query exception
*/
private boolean deep(final QueryContext ctx) throws QueryException {
if(expr.length == 3) checkColl(expr[2], ctx);
return Compare.deep(ctx.iter(expr[0]), ctx.iter(expr[1]), input);
}
}