package org.basex.query.func;
import java.io.IOException;
import org.basex.io.serial.Serializer;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.expr.Arr;
import org.basex.query.expr.Expr;
import org.basex.query.item.FItem;
import org.basex.query.item.FuncType;
import org.basex.query.item.Item;
import org.basex.query.item.Type;
import org.basex.query.item.Value;
import org.basex.query.item.map.Map;
import org.basex.query.iter.Iter;
import org.basex.query.util.Err;
import static org.basex.query.util.Err.*;
import org.basex.util.Array;
import org.basex.util.InputInfo;
import org.basex.util.TokenBuilder;
/**
* Dynamic function call.
*
* @author BaseX Team 2005-12, BSD License
* @author Leo Woerteler
*/
public final class DynamicFunc extends Arr {
/**
* Function constructor.
* @param ii input info
* @param fun function expression
* @param arg arguments
*/
public DynamicFunc(final InputInfo ii, final Expr fun, final Expr[] arg) {
super(ii, Array.add(arg, fun));
}
@Override
public Expr comp(final QueryContext ctx) throws QueryException {
super.comp(ctx);
final int ar = expr.length - 1;
final Expr f = expr[ar];
final Type t = f.type().type;
if(t instanceof FuncType) {
final FuncType ft = (FuncType) t;
if(ft.args != null && ft.args.length != ar)
throw INVARITY.thrw(input, f, ar);
if(ft.ret != null) type = ft.ret;
}
// maps can only contain fully evaluated Values, so this is safe
return allAreValues() && f instanceof Map ? optPre(value(ctx), ctx) : this;
}
@Override
public Item item(final QueryContext ctx, final InputInfo ii)
throws QueryException {
return getFun(ctx).invItem(ctx, ii, argv(ctx));
}
@Override
public Value value(final QueryContext ctx) throws QueryException {
return getFun(ctx).invValue(ctx, input, argv(ctx));
}
@Override
public Iter iter(final QueryContext ctx) throws QueryException {
return getFun(ctx).invIter(ctx, input, argv(ctx));
}
/**
* Evaluates all arguments.
* @param ctx query context
* @return array of argument values
* @throws QueryException query exception
*/
private Value[] argv(final QueryContext ctx) throws QueryException {
final Value[] argv = new Value[expr.length - 1];
for(int i = argv.length; --i >= 0;) argv[i] = ctx.value(expr[i]);
return argv;
}
/**
* Evaluates and checks the function item.
* @param ctx query context
* @return function item
* @throws QueryException query exception
*/
private FItem getFun(final QueryContext ctx) throws QueryException {
final int ar = expr.length - 1;
final Item it = checkItem(expr[ar], ctx);
if(!it.type.isFunction()) throw Err.type(this, FuncType.arity(ar), it);
final FItem fit = (FItem) it;
if(fit.arity() != ar) throw INVARITY.thrw(input, fit, ar);
return fit;
}
@Override
public void plan(final Serializer ser) throws IOException {
ser.openElement(this);
expr[expr.length - 1].plan(ser);
for(int i = 0; i < expr.length - 1; i++) expr[i].plan(ser);
ser.closeElement();
}
@Override
public String description() {
return expr[expr.length - 1].description() + "(...)";
}
@Override
public String toString() {
final TokenBuilder tb = new TokenBuilder(expr[expr.length - 1].toString());
tb.add('(');
for(int i = 0; i < expr.length - 1; i++) {
tb.add(expr[i].toString());
if(i < expr.length - 2) tb.add(", ");
}
return tb.add(')').toString();
}
}