package org.basex.query.func;
import java.util.Arrays;
import java.util.Comparator;
import org.basex.query.QueryContext;
import org.basex.query.QueryError;
import org.basex.query.QueryException;
import org.basex.query.expr.Expr;
import org.basex.query.item.AtomType;
import org.basex.query.item.FItem;
import org.basex.query.item.FuncType;
import org.basex.query.item.Item;
import org.basex.query.item.Value;
import org.basex.query.iter.ItemCache;
import org.basex.query.iter.Iter;
import org.basex.query.util.Err;
import org.basex.util.InputInfo;
/**
* Implementation-specific functions on functions.
*
* @author BaseX Team 2005-12, BSD License
* @author Leo Woerteler
*/
public final class FNHof extends StandardFunc {
/**
* Constructor.
* @param ii input info
* @param f function definition
* @param e arguments
*/
public FNHof(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 _HOF_SORT_WITH: return sortWith(ctx);
case _HOF_ID:
case _HOF_CONST: return ctx.iter(expr[0]);
case _HOF_FOLD_LEFT1: return foldLeft1(ctx).iter();
case _HOF_UNTIL: return until(ctx).iter();
case _HOF_ITERATE: return iterate(ctx);
default: return super.iter(ctx);
}
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
switch(sig) {
case _HOF_FOLD_LEFT1: return foldLeft1(ctx);
case _HOF_UNTIL: return until(ctx);
case _HOF_ID:
case _HOF_CONST: return ctx.value(expr[0]);
default: return super.value(ctx);
}
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
switch(sig) {
case _HOF_ID:
case _HOF_CONST: return expr[0].item(ctx, ii);
default: return super.item(ctx, ii);
}
}
/**
* Folds a sequence into a return value, starting from the left and using the
* leftmost item as start value.
* @param ctx query context
* @return resulting sequence
* @throws QueryException query exception
*/
private Value foldLeft1(final QueryContext ctx) throws QueryException {
final FItem f = withArity(0, 2, ctx);
final Iter xs = expr[1].iter(ctx);
Value sum = checkNoEmpty(xs.next());
for(Item x; (x = xs.next()) != null;) sum = f.invValue(ctx, input, sum, x);
return sum;
}
/**
* Sorts the input sequence according to the given relation.
* @param ctx query context
* @return sorted sequence
* @throws QueryException query exception
*/
private ItemCache sortWith(final QueryContext ctx) throws QueryException {
final FItem lt = withArity(0, 2, ctx);
final ItemCache ic = expr[1].value(ctx).cache();
try {
Arrays.sort(ic.item, 0, (int) ic.size(), new Comparator<Item>(){
@Override
public int compare(final Item it1, final Item it2) {
try {
return checkType(lt.invItem(ctx, input, it1, it2),
AtomType.BLN).bool(input) ? -1 : 1;
} catch(final QueryException qe) {
throw new QueryError(qe);
}
}
});
} catch(final QueryError err) {
throw err.wrapped();
}
return ic;
}
/**
* Applies a function to a start value until the given predicate holds.
* @param ctx query context
* @return accepted value
* @throws QueryException exception
*/
private Value until(final QueryContext ctx) throws QueryException {
final FItem pred = withArity(0, 1, ctx);
final FItem fun = withArity(1, 1, ctx);
Value v = ctx.value(expr[2]);
while(!checkBln(checkNoEmpty(pred.invItem(ctx, input, v)), ctx)) {
v = fun.invValue(ctx, input, v);
}
return v;
}
/**
* Repeatedly applies a function to an argument, lazily returning all results.
* @param ctx query context
* @return result iterator
* @throws QueryException query context
*/
private Iter iterate(final QueryContext ctx) throws QueryException {
final FItem f = withArity(0, 1, ctx);
return new Iter() {
// current value
Value v = ctx.value(expr[1]);
long i, len = v.size();
@Override
public Item next() throws QueryException {
while(i >= len) {
v = f.invValue(ctx, input, v);
i = 0;
len = v.size();
}
return v.itemAt(i++);
}
};
}
/**
* Casts and checks the function item for its arity.
* @param p position of the function
* @param a arity
* @param ctx query context
* @return function item
* @throws QueryException query exception
*/
private FItem withArity(final int p, final int a, final QueryContext ctx)
throws QueryException {
final Item f = checkItem(expr[p], ctx);
if(!f.type.isFunction() || ((FItem) f).arity() != a)
Err.type(this, FuncType.arity(a), f);
return (FItem) f;
}
@Override
public boolean uses(final Use u) {
return sig == Function.PARTIAL_APPLY && u == Use.CTX || u == Use.X30 ||
super.uses(u);
}
}