Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.basex.query.func;
import static org.basex.query.QueryError.*;
import static org.basex.query.QueryText.*;
import static org.basex.util.Token.*;
import java.io.*;
import java.net.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;
import org.basex.core.*;
import org.basex.core.users.*;
import org.basex.data.*;
import org.basex.io.*;
import org.basex.io.out.*;
import org.basex.io.serial.*;
import org.basex.query.*;
import org.basex.query.CompileContext.*;
import org.basex.query.expr.*;
import org.basex.query.iter.*;
import org.basex.query.util.*;
import org.basex.query.util.collation.*;
import org.basex.query.value.*;
import org.basex.query.value.item.*;
import org.basex.query.value.map.*;
import org.basex.query.value.node.*;
import org.basex.query.value.seq.*;
import org.basex.query.value.type.*;
import org.basex.query.var.*;
import org.basex.util.*;
import org.basex.util.hash.*;
import org.basex.util.options.*;
import org.basex.util.similarity.*;
/**
* Built-in functions.
*
* @author BaseX Team 2005-22, BSD License
* @author Christian Gruen
*/
public abstract class StandardFunc extends Arr {
/** Function definition. */
public FuncDefinition definition;
/** Static context. */
public StaticContext sc;
/** Data reference (can be {@code null}). */
private Data data;
/**
* Constructor.
*/
protected StandardFunc() {
super(null, SeqType.ITEM_ZM);
}
/**
* Initializes the function.
* @param ii input info
* @param sctx static context
* @param df function definition
* @param args function arguments
*/
final void init(final StaticContext sctx, final InputInfo ii, final FuncDefinition df,
final Expr[] args) {
sc = sctx;
info = ii;
definition = df;
exprs = args;
exprType.assign(df.seqType);
}
@Override
public final Expr optimize(final CompileContext cc) throws QueryException {
checkPerm(cc.qc, definition.perm);
simplifyArgs(cc);
// apply custom optimizations
final Expr expr = opt(cc);
if(expr != this) return cc.replaceWith(this, expr);
// pre-evaluate if arguments are values and not too large
final SeqType st = definition.seqType;
return allAreValues(st.occ.max > 1 || st.type instanceof FuncType) && isSimple()
? cc.preEval(this) : this;
}
/**
* Simplifies the types of all arguments. This function is overwritten by functions that
* rely on the original argument type.
* @param cc compilation context
* @throws QueryException query exception
*/
protected void simplifyArgs(final CompileContext cc) throws QueryException {
final int el = exprs.length;
for(int e = 0; e < el; e++) {
// consider variable-size parameters
final int p = Math.min(e, definition.params.length - 1);
final Type type = definition.params[p].type;
if(type.instanceOf(AtomType.ANY_ATOMIC_TYPE)) {
final Simplify mode = type.instanceOf(AtomType.NUMERIC) ? Simplify.NUMBER : Simplify.STRING;
exprs[e] = exprs[e].simplifyFor(mode, cc);
}
}
}
/**
* Performs function specific optimizations.
* @param cc compilation context
* @return optimized or original expression
* @throws QueryException query exception
*/
@SuppressWarnings("unused")
protected Expr opt(final CompileContext cc) throws QueryException {
return this;
}
@Override
public final StandardFunc copy(final CompileContext cc, final IntObjMap vm) {
final int el = exprs.length;
final Expr[] arg = new Expr[el];
for(int e = 0; e < el; e++) arg[e] = exprs[e].copy(cc, vm);
return copyType(definition.get(sc, info, arg));
}
/**
* Optimizes a function that returns an empty sequence when the first atomized argument is empty,
* and adjusts the occurrence indicator if the argument will always yield one item.
* @return original expression or function argument
*/
protected final Expr optFirst() {
return optFirst(true, true, null);
}
/**
* Optimizes a function that returns an empty sequence when the first argument or the
* context value is empty.
*
*
Returns the first argument (or the context) if it yields an empty sequence.
*
Sets the occurrence indicator to 1 if the argument returns at least one item.
*
* @param occ assign occurrence indicator
* ({@code true} if function will always yield a result if first argument is non-empty)
* @param atom argument will be atomized
* @param value context value (ignored if {@code null})
* @return original expression or function argument
*/
protected final Expr optFirst(final boolean occ, final boolean atom, final Value value) {
final Expr expr = exprs.length > 0 ? exprs[0] : value;
if(expr != null) {
final SeqType st = expr.seqType();
if(st.zero()) return expr;
if(occ && st.oneOrMore() && !(atom && st.mayBeArray())) exprType.assign(Occ.EXACTLY_ONE);
}
return this;
}
/**
* Serializes the data from the specified iterator.
* @param iter data to serialize
* @param sopts serialization parameters
* @param err error to raise
* @param qc query context
* @return result
* @throws QueryException query exception
*/
protected final byte[] serialize(final Iter iter, final SerializerOptions sopts,
final QueryError err, final QueryContext qc) throws QueryException {
try {
final ArrayOutput ao = new ArrayOutput();
try(Serializer ser = Serializer.get(ao, sopts)) {
for(Item item; (item = qc.next(iter)) != null;) ser.serialize(item);
}
return new TokenBuilder(ao.finish()).normalize().finish();
} catch(final QueryIOException ex) {
throw ex.getCause(info);
} catch(final IOException ex) {
throw err.get(info, ex);
}
}
@Override
public boolean has(final Flag... flags) {
// check signature flags
for(final Flag flag : flags) {
if(definition.has(flag)) return true;
}
// mix updates: higher-order function may be updating
if(Flag.UPD.in(flags) && sc.mixUpdates && definition.has(Flag.HOF)) return true;
// check arguments (without function invocation; it only applies to function itself)
final Flag[] flgs = Flag.HOF.remove(flags);
return flgs.length != 0 && super.has(flgs);
}
@Override
public boolean vacuous() {
return size() == 0 && !has(Flag.UPD);
}
/**
* Refines the type of a function item argument.
* @param expr expression
* @param cc compilation context
* @param declType declared return type
* @param argTypes argument types
* @return old or new expression
* @throws QueryException query context
*/
public final Expr coerceFunc(final Expr expr, final CompileContext cc, final SeqType declType,
final SeqType... argTypes) throws QueryException {
// check if argument is function item
if(!(expr instanceof FuncItem)) return expr;
// check number of arguments
final FuncItem func = (FuncItem) expr;
final int al = argTypes.length, fargs = func.arity();
if(fargs != al) return expr;
// select most specific argument and return types
final FuncType oldType = func.funcType();
final SeqType[] oldArgs = oldType.argTypes, newArgs = new SeqType[al];
for(int a = 0; a < al; a++) {
newArgs[a] = argTypes[a].instanceOf(oldArgs[a]) ? argTypes[a] : oldArgs[a];
}
final SeqType newDecl = declType.instanceOf(oldType.declType) ? declType : oldType.declType;
final FuncType newType = FuncType.get(newDecl, newArgs);
// new type is more specific: coerce to new function type
return !newType.eq(oldType) ? func.coerceTo(newType, cc.qc, info, true) : expr;
}
/**
* Opens a database at compile time.
* @param cc compilation context
* @return self reference
* @throws QueryException query exception
*/
protected final Expr compileData(final CompileContext cc) throws QueryException {
if(cc.dynamic && exprs.length > 0 && exprs[0] instanceof Value) {
data = toData(cc.qc);
cc.info(OPTOPEN_X, data.meta.name);
}
return this;
}
/**
* Tries to embed a positional function call in its first argument.
* @param cc compilation context
* @param skip skip evaluation of remaining operands
* @return optimized expression or {@code null}
* @throws QueryException query exception
*/
protected Expr embed(final CompileContext cc, final boolean skip) throws QueryException {
// util:last((1 to 8) ! <_>{ . }) -> util:last((1 to 8)) ! <_>{ . }
if(exprs[0] instanceof SimpleMap) {
final Expr[] ops = exprs[0].args();
if(((Checks) op -> op == ops[0] || op.seqType().one()).all(ops)) {
exprs[0] = ops[0];
ops[0] = definition.get(sc, info, exprs).optimize(cc);
return skip ? ops[0] : SimpleMap.get(cc, info, ops);
}
}
return null;
}
@Override
public final Data data() {
return data;
}
@Override
public final void data(final Data dt) {
data = dt;
}
/**
* Checks if the specified item has the specified Date type.
* If it is item, the specified Date is returned.
* @param item item to be checked
* @param type target type
* @param qc query context
* @return date
* @throws QueryException query exception
*/
protected final ADate toDate(final Item item, final AtomType type, final QueryContext qc)
throws QueryException {
return (ADate) (item.type.isUntyped() ? type.cast(item, qc, sc, info) : checkType(item, type));
}
/**
* Checks if the specified expression is a database node.
* Returns the node or an exception.
* @param item item to be checked
* @return item
* @throws QueryException query exception
*/
protected final DBNode toDBNode(final Item item) throws QueryException {
if(checkNoEmpty(item, NodeType.NODE) instanceof DBNode) return (DBNode) item;
throw DB_NODE_X.get(info, item);
}
/**
* Checks if the specified collation is supported.
* @param i index of argument
* @param qc query context
* @return collator or {@code null} (default collation)
* @throws QueryException query exception
*/
protected final Collation toCollation(final int i, final QueryContext qc) throws QueryException {
final byte[] coll = i >= exprs.length ? null : toToken(exprs[i], qc);
return Collation.get(coll, qc, sc, info, WHICHCOLL_X);
}
/**
* Converts the specified argument to a file path.
* @param i index of argument
* @param qc query context
* @return file instance
* @throws QueryException query exception
*/
protected final Path toPath(final int i, final QueryContext qc) throws QueryException {
return toPath(toToken(exprs[i], qc));
}
/**
* Converts the specified string to a file path.
* @param path path string
* @return file instance
* @throws QueryException query exception
*/
protected final Path toPath(final byte[] path) throws QueryException {
try {
final String p = string(path);
return p.startsWith(IO.FILEPREF) ? Paths.get(new URI(p)) : Paths.get(p);
} catch(final InvalidPathException | URISyntaxException ex) {
Util.debug(ex);
throw FILE_INVALID_PATH_X.get(info, path);
}
}
/**
* Returns a valid reference if a file is found at the specified path or the static base uri.
* Otherwise, returns an error.
* @param i index of URI argument
* @param qc query context
* @return input source, or exception
* @throws QueryException query exception
*/
protected final IO toIO(final int i, final QueryContext qc) throws QueryException {
return toIO(toToken(exprs[i], qc));
}
/**
* Converts the specified URI to a reference to a resource.
* @param uri file URI
* @return io reference
* @throws QueryException query exception
*/
protected final IO toIO(final byte[] uri) throws QueryException {
final IO io = sc.resolve(string(uri));
if(!io.exists()) throw WHICHRES_X.get(info, io);
if(io instanceof IOFile && io.isDir()) throw RESDIR_X.get(info, io);
return io;
}
/**
* Returns the content of the specified input.
* @param i index of input argument (xs:anyURI with URI or xs:string with content)
* @param qc query context
* @return input content (UTF-8) with optional base URI
* @throws QueryException query exception
*/
protected final IOContent toContent(final int i, final QueryContext qc) throws QueryException {
final Item item = toItem(exprs[i], qc);
return item instanceof Uri ? toContent(item.string(info), qc) : new IOContent(toToken(item));
}
/**
* Returns the content of the specified input.
* @param uri URI
* @param qc query context
* @return input content (UTF-8) with attached base URI
* @throws QueryException query exception
*/
protected final IOContent toContent(final byte[] uri, final QueryContext qc)
throws QueryException {
checkPerm(qc, Perm.ADMIN);
final IO io = toIO(uri);
try {
return new IOContent(io.string(), io.url());
} catch(final IOException ex) {
throw IOERR_X.get(info, ex);
}
}
/**
* Evaluates the specified URI.
* @param path custom path (can be {@code null})
* @param options options
* @param option base-uri option
* @return base URI
*/
protected final String toBaseUri(final String path, final Options options,
final StringOption option) {
final String base = options.get(option);
return base != null && !base.isEmpty() ? base :
path != null && !path.isEmpty() ? path : string(sc.baseURI().string());
}
/**
* Returns a normalized encoding representation.
* @param i index of encoding argument
* @param err error to raise
* @param qc query context
* @return string or {@code null}
* @throws QueryException query exception
*/
protected final String toEncodingOrNull(final int i, final QueryError err, final QueryContext qc)
throws QueryException {
if(i >= exprs.length) return null;
final byte[] encoding = toToken(exprs[i], qc);
try {
final String enc = toString(exprs[i], qc);
if(Charset.isSupported(enc)) return Strings.normEncoding(enc);
} catch(final IllegalArgumentException ex) {
// character set is invalid or unknown (e.g. empty string)
Util.debug(ex);
}
throw err.get(info, QueryError.similar(encoding,
Levenshtein.similar(encoding, Strings.encodings())));
}
/**
* Returns the expression at the specified index as node or atomized item.
* Returns the item or throws an exception.
* @param i index of argument
* @param qc query context
* @return node or atomized item
* @throws QueryException query exception
*/
protected final Item toNodeOrAtomItem(final int i, final QueryContext qc) throws QueryException {
final Item item = toItem(exprs[i], qc);
return item instanceof ANode ? item : item.atomItem(qc, info);
}
/**
* Parses the options at the specified index.
* @param options type
* @param i index of argument (can exceed length of argument, or may yield an empty sequence)
* @param opts options
* @param qc query context
* @return passed on options
* @throws QueryException query exception
*/
protected final E toOptions(final int i, final E opts, final QueryContext qc)
throws QueryException {
return i >= exprs.length ? opts : new FuncOptions(info).assign(exprs[i].item(qc, info), opts);
}
/**
* Returns all keys and values of the specified binding argument.
* @param i index of argument
* @param qc query context
* @return resulting map
* @throws QueryException query exception
*/
protected final HashMap toBindings(final int i, final QueryContext qc)
throws QueryException {
final HashMap hm = new HashMap<>();
final int el = exprs.length;
if(i < el) {
final Item item = exprs[i].item(qc, info);
final XQMap map = item == Empty.VALUE ? XQMap.empty() : toMap(item);
map.apply((it, v) -> {
final byte[] key;
if(it.type.isStringOrUntyped()) {
key = it.string(null);
} else {
final QNm qnm = toQNm(it, false);
final TokenBuilder tb = new TokenBuilder();
if(qnm.uri() != null) tb.add('{').add(qnm.uri()).add('}');
key = tb.add(qnm.local()).finish();
}
hm.put(string(key), v);
});
}
return hm;
}
/**
* Returns a database instance for the first string argument of the function.
* This method assumes that the function has at least one argument.
* @param qc query context
* @return data instance
* @throws QueryException query exception
*/
protected final Data toData(final QueryContext qc) throws QueryException {
return data != null ? data : qc.resources.database(toName(0, false, DB_NAME_X, qc), info);
}
/**
* Checks if the current user has given permissions. If negative, an
* exception is thrown.
* @param qc query context
* @param perm permission
* @throws QueryException query exception
*/
protected void checkPerm(final QueryContext qc, final Perm perm) throws QueryException {
if(perm != Perm.NONE && !qc.context.user().has(perm))
throw BASEX_PERMISSION_X_X.get(info, perm, this);
}
/**
* Casts and checks the function item for its arity.
* @param expr expression
* @param nargs number of arguments (arity)
* @param qc query context
* @return function item
* @throws QueryException query exception
*/
protected final FItem toFunction(final Expr expr, final int nargs, final QueryContext qc)
throws QueryException {
return toFunction(expr, nargs, false, qc);
}
/**
* Casts and checks the function item for its arity.
* @param expr expression
* @param nargs number of arguments (arity)
* @param qc query context
* @param updating updating flag
* @return function item
* @throws QueryException query exception
*/
protected final FItem toFunction(final Expr expr, final int nargs, final boolean updating,
final QueryContext qc) throws QueryException {
final FItem func = checkUp(toFunction(expr, qc), updating, sc);
final int fargs = func.arity();
if(fargs == nargs) return func;
throw FUNARITY_X_X.get(info, arguments(fargs), nargs);
}
/**
* Checks if the specified expression is a valid name.
* @param i index of argument
* @param empty allow empty string
* @param err error to raise
* @param qc query context
* @return name
* @throws QueryException query exception
*/
protected final String toName(final int i, final boolean empty, final QueryError err,
final QueryContext qc) throws QueryException {
final String name = toString(exprs[i], qc);
if(empty && name.length() == 0 || Databases.validName(name)) return name;
throw err.get(info, name);
}
/**
* Converts the specified dateTime to milliseconds.
* @param expr expression
* @param qc query context
* @return resulting value
* @throws QueryException query exception
*/
protected final long toMilliseconds(final Expr expr, final QueryContext qc)
throws QueryException {
final Dtm dtm = (Dtm) checkType(expr, qc, AtomType.DATE_TIME);
if(dtm.yea() > 292278993) throw INTRANGE_X.get(info, dtm.yea());
return dtm.toJava().toGregorianCalendar().getTimeInMillis();
}
/**
* Tries to lock a database supplied by the specified argument.
* @param visitor visitor
* @param backup backup flag
* @param i index of argument
* @return result of check
*/
protected final boolean dataLock(final ASTVisitor visitor, final boolean backup, final int i) {
return visitor.lock(() -> {
final ArrayList list = new ArrayList<>(1);
final Expr expr = exprs[i];
String name = expr instanceof Str ? string(((Str) expr).string()) :
expr instanceof Atm ? string(((Atm) expr).string(null)) : null;
if(name != null) {
if(backup) {
final String db = Databases.name(name);
if(db.isEmpty()) {
name = db;
} else {
list.add(db);
}
}
if(name.isEmpty()) name = null;
}
list.add(name);
return list;
});
}
@Override
public final boolean equals(final Object obj) {
return this == obj || obj instanceof StandardFunc &&
definition == ((StandardFunc) obj).definition && super.equals(obj);
}
@Override
public final String description() {
return definition.toString();
}
@Override
public final void toXml(final QueryPlan plan) {
plan.add(plan.create(this, NAME, definition.id()), exprs);
}
@Override
public final void toString(final QueryString qs) {
qs.token(definition.id()).params(exprs);
}
}