package org.basex.query.item;
import java.util.Arrays;
import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import org.basex.query.func.UserFunc;
import org.basex.query.util.Err;
import org.basex.query.util.Var;
import static org.basex.query.QueryText.*;
import org.basex.util.InputInfo;
import org.basex.util.Token;
import org.basex.util.TokenBuilder;
import org.basex.util.Util;
/**
* XQuery 3.0 function data types.
*
* @author BaseX Team 2005-12, BSD License
* @author Leo Woerteler
*/
public class FuncType implements Type {
/** Any function type. */
public static final FuncType ANY_FUN = new FuncType(null, null);
/** Argument types. */
public final SeqType[] args;
/** Return type. */
public final SeqType ret;
/** This function type's sequence type. */
private SeqType seq;
/**
* Constructor.
* @param arg argument types
* @param rt return type
*/
FuncType(final SeqType[] arg, final SeqType rt) {
args = arg;
ret = rt;
}
@Override
public final boolean isNode() {
return false;
}
@Override
public final boolean isNumber() {
return false;
}
@Override
public final SeqType seqType() {
if(seq == null) seq = new SeqType(this);
return seq;
}
@Override
public final boolean isString() {
return false;
}
@Override
public final boolean isUntyped() {
return false;
}
@Override
public final boolean isFunction() {
return true;
}
@Override
public byte[] string() {
return Token.token(FUNCTION);
}
@Override
public FItem cast(final Item it, final QueryContext ctx, final InputInfo ii)
throws QueryException {
if(!it.type.isFunction()) throw Err.cast(ii, this, it);
final FItem f = (FItem) it;
return this == ANY_FUN ? f : f.coerceTo(this, ctx, ii);
}
@Override
public final Item cast(final Object o, final InputInfo ii) {
throw Util.notexpected(o);
}
@Override
public final boolean instanceOf(final Type t) {
// the only non-function super-type of function is item()
if(!(t instanceof FuncType)) return t == AtomType.ITEM;
final FuncType ft = (FuncType) t;
// takes care of FunType.ANY
if(this == ft || ft == ANY_FUN) return true;
if(this == ANY_FUN || args.length != ft.args.length ||
!ret.instance(ft.ret)) return false;
for(int a = 0; a < args.length; a++) {
if(!ft.args[a].instance(args[a])) return false;
}
return true;
}
/**
* Getter for function types.
* @param ret return type
* @param args argument types
* @return function type
*/
public static FuncType get(final SeqType ret, final SeqType... args) {
if(args == null || ret == null) return ANY_FUN;
return new FuncType(args, ret);
}
/**
* Getter for function types with a given arity.
* @param a number of arguments
* @return function type
*/
public static FuncType arity(final int a) {
final SeqType[] args = new SeqType[a];
Arrays.fill(args, SeqType.ITEM_ZM);
return get(SeqType.ITEM_ZM, args);
}
/**
* Getter for a function's type.
* @param f user-defined function
* @return function type
*/
public static FuncType get(final UserFunc f) {
final SeqType[] at = new SeqType[f.args.length];
for(int a = 0; a < at.length; a++) {
at[a] = f.args[a] == null || f.args[a].type == null ?
SeqType.ITEM_ZM : f.args[a].type;
}
return new FuncType(at, f.ret == null ? SeqType.ITEM_ZM : f.ret);
}
/**
* Sets the types of the given variables.
* @param vars variables to type
* @return the variables for convenience
*/
public final Var[] type(final Var[] vars) {
if(this != ANY_FUN) {
for(int v = 0; v < vars.length; v++)
if(vars[v] != null && args[v] != SeqType.ITEM_ZM)
vars[v].type = args[v];
}
return vars;
}
@Override
public boolean isDuration() {
return false;
}
@Override
public boolean isDate() {
return false;
}
@Override
public boolean isMap() {
return false;
}
@Override
public int id() {
return 7;
}
@Override
public String toString() {
final TokenBuilder tb = new TokenBuilder(FUNCTION).add('(');
if(this == ANY_FUN) {
tb.add('*').add(')');
} else {
tb.addSep(args, ", ").add(") as ").add(ret.toString());
}
return tb.toString();
}
}