package org.basex.query.expr; import static org.basex.query.QueryText.*; import static org.basex.query.util.Err.*; import static org.basex.util.Token.*; import org.basex.core.User; import org.basex.io.IO; import org.basex.io.IOFile; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.func.Function; import org.basex.query.item.ANode; import org.basex.query.item.AtomType; import org.basex.query.item.Bln; import org.basex.query.item.DBNode; import org.basex.query.item.Empty; import org.basex.query.item.Item; import org.basex.query.item.NodeType; import org.basex.query.item.SeqType; import org.basex.query.item.Str; import org.basex.query.item.Type; import org.basex.query.item.Uri; import org.basex.query.item.Value; import org.basex.query.item.map.Map; import org.basex.query.iter.ItemCache; import org.basex.query.iter.Iter; import org.basex.query.util.Err; import org.basex.util.InputInfo; /** * Abstract parse expression. All non-value expressions are derived from * this class. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public abstract class ParseExpr extends Expr { /** Input information. */ public final InputInfo input; /** Cardinality of result; unknown if set to -1. */ public long size = -1; /** Data type. */ public SeqType type; /** * Constructor. * @param ii input info */ protected ParseExpr(final InputInfo ii) { input = ii; } @Override public Iter iter(final QueryContext ctx) throws QueryException { final Item it = item(ctx, input); return it != null ? it.iter() : Empty.ITER; } @Override public Item item(final QueryContext ctx, final InputInfo ii) throws QueryException { final Iter ir = iter(ctx); final Item it = ir.next(); if(it == null || ir.size() == 1) return it; Item n = ir.next(); if(n != null) { final ItemCache ic = new ItemCache(); ic.add(it); ic.add(n); n = ir.next(); if(n != null) ic.add(Str.get("...")); XPSEQ.thrw(ii, ic.value()); } return it; } @Override public Value value(final QueryContext ctx) throws QueryException { if(type().zeroOrOne()) { final Value v = item(ctx, input); return v == null ? Empty.SEQ : v; } return ctx.iter(this).value(); } @Override public final Item ebv(final QueryContext ctx, final InputInfo ii) throws QueryException { final Item it; if(type().zeroOrOne()) { it = item(ctx, input); } else { final Iter ir = iter(ctx); it = ir.next(); if(it != null && !it.type.isNode() && ir.next() != null) CONDTYPE.thrw(input, this); } return it == null ? Bln.FALSE : it; } @Override public final Item test(final QueryContext ctx, final InputInfo ii) throws QueryException { final Item it = ebv(ctx, input); return (it.type.isNumber() ? it.dbl(input) == ctx.pos : it.bool(input)) ? it : null; } @Override public SeqType type() { return type != null ? type : SeqType.ITEM_ZM; } @Override public final long size() { return size == -1 ? type().occ() : size; } // OPTIMIZATIONS ============================================================ /** * Pre-evaluates the specified expression. * @param ctx query context * @return optimized expression * @throws QueryException query exception */ public final Expr preEval(final QueryContext ctx) throws QueryException { return optPre(item(ctx, input), ctx); } /** * Adds an optimization info for pre-evaluating the specified expression. * @param opt optimized expression * @param ctx query context * @return optimized expression */ protected final Expr optPre(final Expr opt, final QueryContext ctx) { if(opt != this) ctx.compInfo(OPTPRE, this); return opt == null ? Empty.SEQ : opt; } /** * Returns a boolean equivalent for the specified expression. * If the specified expression yields a boolean value anyway, it will be * returned as is. Otherwise, it will be wrapped into a boolean function. * @param e expression to be rewritten * @return expression */ protected final Expr compBln(final Expr e) { return e.type().eq(SeqType.BLN) ? e : Function.BOOLEAN.get(input, e); } // VALIDITY CHECKS ========================================================== /** * Checks if the specified expressions is no updating expression. * @param e expression * @param ctx query context * @return the specified expression * @throws QueryException query exception */ public final Expr checkUp(final Expr e, final QueryContext ctx) throws QueryException { if(e != null && ctx.updating && e.uses(Use.UPD)) UPNOT.thrw(input, description()); return e; } /** * Tests if the specified expressions are updating or vacuous. * @param ctx query context * @param expr expression array * @throws QueryException query exception */ public void checkUp(final QueryContext ctx, final Expr... expr) throws QueryException { if(!ctx.updating) return; int s = 0; for(final Expr e : expr) { if(e.isVacuous()) continue; final boolean u = e.uses(Use.UPD); if(u && s == 2 || !u && s == 1) UPNOT.thrw(input, description()); s = u ? 1 : 2; } } /** * Checks if the specified expression yields a boolean. * Returns the boolean or throws an exception. * @param e expression to be checked * @param ctx query context * @return boolean * @throws QueryException query exception */ public final boolean checkBln(final Expr e, final QueryContext ctx) throws QueryException { final Item it = checkNoEmpty(e.item(ctx, input), AtomType.BLN); final Type ip = it.type; if(!ip.isUntyped() && ip != AtomType.BLN) Err.type(this, AtomType.BLN, it); return it.bool(input); } /** * Checks if the specified expression yields a double. * Returns the double or throws an exception. * @param e expression to be checked * @param ctx query context * @return double * @throws QueryException query exception */ public final double checkDbl(final Expr e, final QueryContext ctx) throws QueryException { final Item it = checkNoEmpty(e.item(ctx, input), AtomType.DBL); final Type ip = it.type; if(!ip.isUntyped() && !ip.isNumber()) number(this, it); return it.dbl(input); } /** * Checks if the specified expression is an integer. * Returns a token representation or an exception. * @param e expression to be checked * @param ctx query context * @return integer value * @throws QueryException query exception */ public final long checkItr(final Expr e, final QueryContext ctx) throws QueryException { return checkItr(checkNoEmpty(e.item(ctx, input), AtomType.ITR)); } /** * Checks if the specified item is a number. * Returns a token representation or an exception. * @param it item to be checked * @return item * @throws QueryException query exception */ public final long checkItr(final Item it) throws QueryException { final Type ip = it.type; if(!ip.isUntyped() && !ip.instanceOf(AtomType.ITR)) Err.type(this, AtomType.ITR, it); return it.itr(input); } /** * Checks if the specified expression is a node. * Returns the node or an exception. * @param it item to be checked * @return item * @throws QueryException query exception */ public final ANode checkNode(final Item it) throws QueryException { if(!it.type.isNode()) Err.type(this, NodeType.NOD, it); return (ANode) it; } /** * Checks if the specified expression is a database node. * Returns the node or an exception. * @param it item to be checked * @return item * @throws QueryException query exception */ public final DBNode checkDBNode(final Item it) throws QueryException { if(!(it instanceof DBNode)) NODBCTX.thrw(input, this); return (DBNode) it; } /** * Checks if the specified collation is supported. * @param e expression to be checked * @param ctx query context * @throws QueryException query exception */ public final void checkColl(final Expr e, final QueryContext ctx) throws QueryException { final byte[] u = checkStr(e, ctx); if(eq(URLCOLL, u)) return; final Uri uri = Uri.uri(u); if(uri.isAbsolute() || !eq(ctx.sc.baseURI().resolve(uri).string(), URLCOLL)) IMPLCOL.thrw(input, e); } /** * Checks if the specified expression yields a string. * Returns a token representation or an exception. * @param e expression to be evaluated * @param ctx query context * @return item * @throws QueryException query exception */ public final byte[] checkStr(final Expr e, final QueryContext ctx) throws QueryException { final Item it = checkItem(e, ctx); final Type ip = it.type; if(!ip.isString() && !ip.isUntyped()) Err.type(this, AtomType.STR, it); return it.string(input); } /** * Checks if the specified item is a string or an empty sequence. * Returns a token representation or an exception. * @param it item to be checked * @return item * @throws QueryException query exception */ public final byte[] checkEStr(final Item it) throws QueryException { if(it == null) return EMPTY; final Type ip = it.type; if(!ip.isString() && !ip.isUntyped()) Err.type(this, AtomType.STR, it); return it.string(input); } /** * Throws an exception if the context item is not set. * @param ctx query context * @return result of check * @throws QueryException query exception */ public final Value checkCtx(final QueryContext ctx) throws QueryException { final Value v = ctx.value; if(v == null) XPNOCTX.thrw(input, this); return v; } /** * Checks if the specified expression yields a non-empty item. * @param e expression to be evaluated * @param ctx query context * @return item * @throws QueryException query exception */ public final Item checkItem(final Expr e, final QueryContext ctx) throws QueryException { return checkNoEmpty(e.item(ctx, input)); } /** * Checks if the specified expression is an empty sequence; if yes, throws * an exception. * @param it item to be checked * @param t type to be checked * @return specified item * @throws QueryException query exception */ public final Item checkType(final Item it, final Type t) throws QueryException { if(!checkNoEmpty(it).type.instanceOf(t)) Err.type(this, t, it); return it; } /** * Checks if the specified item is no empty sequence. * @param it item to be checked * @return specified item * @throws QueryException query exception */ public final Item checkNoEmpty(final Item it) throws QueryException { if(it == null) XPEMPTY.thrw(input, description()); return it; } /** * Checks if the specified item is no empty sequence. * @param it item to be checked * @param t expected type * @return specified item * @throws QueryException query exception */ private Item checkNoEmpty(final Item it, final Type t) throws QueryException { if(it == null) XPEMPTYPE.thrw(input, description(), t); return it; } /** * Checks if the specified expression yields a string or empty sequence. * Returns a token representation or an exception. * @param e expression to be evaluated * @param ctx query context * @return item * @throws QueryException query exception */ public final byte[] checkEStr(final Expr e, final QueryContext ctx) throws QueryException { return checkEStr(e.item(ctx, input)); } /** * Checks if an expression yields a valid and existing {@link IO} instance. * Returns the instance or an exception. * @param e expression to be evaluated * @param ctx query context * @return io instance * @throws QueryException query exception */ public final IO checkIO(final Expr e, final QueryContext ctx) throws QueryException { checkAdmin(ctx); final String name = string(checkStr(e, ctx)); IO io = IO.get(name); if(!io.exists()) { final IO iob = ctx.sc.baseIO(); if(iob != null) { io = new IOFile(iob.path(), name); if(!io.exists()) RESFNF.thrw(input, name); } } return io; } /** * Checks if the current user has admin permissions. If negative, an * exception is thrown. * @param ctx query context * @throws QueryException query exception */ public final void checkAdmin(final QueryContext ctx) throws QueryException { checkPerm(ctx, User.ADMIN); } /** * Checks if the current user has write permissions. If negative, an * exception is thrown. * @param ctx query context * @throws QueryException query exception */ public final void checkWrite(final QueryContext ctx) throws QueryException { checkPerm(ctx, User.WRITE); } /** * Checks if the current user has given permissions. If negative, an * exception is thrown. * @param ctx query context * @param p permission * @throws QueryException query exception */ private void checkPerm(final QueryContext ctx, final byte p) throws QueryException { if(!ctx.context.user.perm(p)) PERMNO.thrw(input, p); } /** * Assures that the given (non-{@code null}) item is a map. * @param it item to check * @return the map * @throws QueryException if the item is not a map */ public Map checkMap(final Item it) throws QueryException { if(it instanceof Map) return (Map) it; throw Err.type(this, SeqType.ANY_MAP, it); } }