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.util.Arrays; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.expr.Expr.Use; import org.basex.query.item.QNm; import org.basex.query.util.NSGlobal; import org.basex.util.InputInfo; import org.basex.util.Levenshtein; import org.basex.util.TokenBuilder; import org.basex.util.Util; import org.basex.util.hash.TokenSet; /** * Static functions. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class Functions extends TokenSet { /** Singleton instance. */ private static final Functions INSTANCE = new Functions(); /** Function classes. */ private Function[] funcs; /** * Returns the singleton instance. * @return instance */ public static Functions get() { return INSTANCE; } /** * Constructor, registering XQuery functions. */ private Functions() { funcs = new Function[CAP]; for(final Function def : Function.values()) { final String dsc = def.desc; final byte[] ln = token(dsc.substring(0, dsc.indexOf(PAR1))); final int i = add(full(def.uri(), ln)); if(i < 0) Util.notexpected("Function defined twice:" + def); funcs[i] = def; } } /** * Returns the specified function. * @param name function name * @param uri function uri * @param args optional arguments * @param ctx query context * @param ii input info * @return function instance * @throws QueryException query exception */ public StandardFunc get(final byte[] name, final byte[] uri, final Expr[] args, final QueryContext ctx, final InputInfo ii) throws QueryException { final int id = id(full(uri, name)); if(id == 0) return null; // create function final Function fl = funcs[id]; if(!eq(fl.uri(), uri)) return null; final StandardFunc f = fl.get(ii, args); if(!ctx.xquery3 && f.uses(Use.X30)) FEATURE30.thrw(ii); // check number of arguments if(args.length < fl.min || args.length > fl.max) XPARGS.thrw(ii, fl); return f; } /** * Throws an error if one of the pre-defined functions is similar to the * specified function name. * @param name function name * @param ii input info * @throws QueryException query exception */ public void error(final QNm name, final InputInfo ii) throws QueryException { // compare specified name with names of predefined functions final byte[] ln = name.local(); final Levenshtein ls = new Levenshtein(); for(int k = 1; k < size; ++k) { final int i = indexOf(keys[k], '}'); final byte[] u = substring(keys[k], 1, i); final byte[] l = substring(keys[k], i + 1); if(eq(ln, l)) { final byte[] ur = name.uri(); FUNSIMILAR.thrw(ii, new TokenBuilder(NSGlobal.prefix(ur)).add(':').add(l), new TokenBuilder(NSGlobal.prefix(u)).add(':').add(l)); } else if(ls.similar(ln, l, 0)) { FUNSIMILAR.thrw(ii, name.string(), l); } } } /** * Returns a unique name representation of the function, * including the URI and function name. * @param uri namespace uri * @param ln local name * @return full name */ private static byte[] full(final byte[] uri, final byte[] ln) { return new TokenBuilder().add('{').add(uri).add('}').add(ln).finish(); } @Override protected void rehash() { super.rehash(); funcs = Arrays.copyOf(funcs, size << 1); } }