package org.basex.query.func;
import static org.basex.query.QueryText.*;
import static org.basex.query.util.Err.*;
import static org.basex.util.Token.*;
import java.io.IOException;
import org.basex.data.Data;
import org.basex.data.MetaData;
import org.basex.io.serial.Serializer;
import org.basex.io.serial.SerializerException;
import org.basex.io.serial.SerializerProp;
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.ANode;
import org.basex.query.item.Atm;
import org.basex.query.item.Item;
import org.basex.query.item.NodeType;
import org.basex.query.item.QNm;
import org.basex.query.item.Str;
import org.basex.query.item.Type;
import org.basex.query.item.map.Map;
import org.basex.query.iter.AxisIter;
import org.basex.util.InputInfo;
import org.basex.util.TokenBuilder;
import org.basex.util.hash.TokenObjMap;
/**
* Standard (built-in) functions.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public abstract class StandardFunc extends Arr {
/** Element: output:serialization-parameters. */
private static final QNm E_PARAM =
new QNm(token("serialization-parameters"), OUTPUTURI);
/** Attribute: value. */
private static final QNm A_VALUE = new QNm(token("value"));
/** Function signature. */
Function sig;
/**
* Constructor.
* @param ii input info
* @param s function definition
* @param args arguments
*/
StandardFunc(final InputInfo ii, final Function s,
final Expr... args) {
super(ii, args);
sig = s;
type = sig.ret;
}
@Override
public final Expr comp(final QueryContext ctx) throws QueryException {
// compile all arguments
super.comp(ctx);
// skip context-based or non-deterministic functions, and non-values
if(uses(Use.CTX) || uses(Use.NDT) || !allAreValues())
return optPre(cmp(ctx), ctx);
// pre-evaluate function
return optPre(sig.ret.zeroOrOne() ? item(ctx, input) : value(ctx), ctx);
}
/**
* Performs function specific compilations.
* @param ctx query context
* @return evaluated item
* @throws QueryException query exception
*/
@SuppressWarnings("unused")
Expr cmp(final QueryContext ctx) throws QueryException {
return this;
}
/**
* Atomizes the specified item.
* @param it input item
* @return atomized item
* @throws QueryException query exception
*/
final Item atom(final Item it) throws QueryException {
final Type ip = it.type;
return ip.isNode() ? ip == NodeType.PI || ip == NodeType.COM ?
Str.get(it.string(input)) : new Atm(it.string(input)) : it;
}
@Override
public final boolean isFunction(final Function f) {
return sig == f;
}
@Override
public final String description() {
return sig.toString();
}
@Override
public final void plan(final Serializer ser) throws IOException {
ser.openElement(this, NAM, token(sig.desc));
for(final Expr arg : expr) arg.plan(ser);
ser.closeElement();
}
@Override
public final String toString() {
final String desc = sig.toString();
return new TokenBuilder().add(desc.substring(0,
desc.indexOf('(') + 1)).addSep(expr, SEP).add(PAR2).toString();
}
/**
* Returns the data instance for the specified argument.
* @param i index of argument
* @param ctx query context
* @return data instance
* @throws QueryException query exception
*/
Data data(final int i, final QueryContext ctx)
throws QueryException {
final Item it = checkNoEmpty(expr[i].item(ctx, input));
final Type ip = it.type;
if(ip.isNode()) return checkDBNode(it).data;
if(ip.isString()) {
final String name = string(it.string(input));
if(!MetaData.validName(name, false)) INVDB.thrw(input, name);
return ctx.resource.data(name, input);
}
throw STRNODTYPE.thrw(input, this, ip);
}
/**
* Creates serializer properties.
* @param fun calling function
* @param arg argument with parameters
* @param ctx query context
* @return serialization parameters
* @throws SerializerException serializer exception
* @throws QueryException query exception
*/
static SerializerProp serialPar(final StandardFunc fun, final int arg,
final QueryContext ctx) throws SerializerException, QueryException {
// check if enough arguments are available
TokenObjMap<Object> tm = new TokenObjMap<Object>();
if(arg < fun.expr.length) {
// retrieve parameters
final Item it = fun.expr[arg].item(ctx, fun.input);
if(it != null) {
if(it instanceof Map) {
tm = ((Map) it).tokenJavaMap(fun.input);
} else {
// check root node
ANode n = (ANode) fun.checkType(it, NodeType.ELM);
if(!n.qname().eq(E_PARAM)) SERUNKNOWN.thrw(fun.input, n.qname());
// interpret query parameters
final AxisIter ai = n.children();
while((n = ai.next()) != null) {
final QNm qn = n.qname();
if(!eq(qn.uri(), OUTPUTURI)) SERUNKNOWN.thrw(fun.input, qn);
final byte[] val = n.attribute(A_VALUE);
if(val == null) SERNOVAL.thrw(fun.input);
tm.add(qn.local(), val);
}
}
}
}
// use default parameters if no parameters have been assigned
final TokenBuilder tb = new TokenBuilder();
for(final byte[] key : tm) {
if(tb.size() != 0) tb.add(',');
tb.add(key).add('=').addExt(tm.get(key));
}
return tb.size() == 0 ? ctx.serProp(true) :
new SerializerProp(tb.toString());
}
}