@Override public Value value(final QueryContext ctx) throws QueryException { final int o = (int) ctx.resources.output.size(); final Updates updates = ctx.resources.updates(); final ContextModifier tmp = updates.mod; final TransformModifier pu = new TransformModifier(); updates.mod = pu; try { for (final Let fo : copies) { final Iter ir = ctx.iter(fo.expr); Item i = ir.next(); if (!(i instanceof ANode) || ir.next() != null) throw UPCOPYMULT.get(fo.info, fo.var.name); // copy node to main memory data instance i = ((ANode) i).dbCopy(ctx.context.options); // add resulting node to variable ctx.set(fo.var, i, info); pu.addData(i.data()); } final Value v = ctx.value(expr[0]); if (!v.isEmpty()) throw BASEX_MOD.get(info); updates.prepare(); updates.apply(); return ctx.value(expr[1]); } finally { ctx.resources.output.size(o); updates.mod = tmp; } }
/** * Performs the test-uris function. * * @param ctx query context * @return resulting value * @throws QueryException query exception */ private Item testUris(final QueryContext ctx) throws QueryException { checkCreate(ctx); final ArrayList<IO> inputs = new ArrayList<>(); final Iter ir = ctx.iter(expr[0]); for (Item it; (it = ir.next()) != null; ) inputs.add(checkPath(it, ctx)); return new Suite(ctx, info).test(inputs); }
/** * Performs the test-libraries function (still experimental). * * @param ctx query context * @return resulting value * @throws QueryException query exception */ private Item testLibraries(final QueryContext ctx) throws QueryException { checkCreate(ctx); final TokenList tl = new TokenList(); final Iter ir = ctx.iter(expr[0]); for (Item it; (it = ir.next()) != null; ) tl.add(checkStr(it)); return new Suite(ctx, info).test(tl); }
@Override public Value value(final QueryContext qc) throws QueryException { final FItem getKey = checkArity(exprs[1], 1, qc); final long k = Math.min(toLong(exprs[2], qc), Integer.MAX_VALUE); if (k < 1) return Empty.SEQ; final Iter iter = exprs[0].iter(qc); final MinHeap<Item, Item> heap = new MinHeap<>( new Comparator<Item>() { @Override public int compare(final Item it1, final Item it2) { try { return OpV.LT.eval(it1, it2, sc.collation, sc, info) ? -1 : 1; } catch (final QueryException qe) { throw new QueryRTException(qe); } } }); try { for (Item it; (it = iter.next()) != null; ) { heap.insert(checkNoEmpty(getKey.invokeItem(qc, info, it)), it); if (heap.size() > k) heap.removeMin(); } } catch (final QueryRTException ex) { throw ex.getCause(); } final ValueBuilder vb = new ValueBuilder(); while (!heap.isEmpty()) vb.addFront(heap.removeMin()); return vb.value(); }
/** * Extracts entries from the archive. * * @param ctx query context * @return text entries * @throws QueryException query exception */ private TokenList extract(final QueryContext ctx) throws QueryException { final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64); TokenSet hs = null; if (expr.length > 1) { // filter result to specified entries hs = new TokenSet(); final Iter names = ctx.iter(expr[1]); for (Item en; (en = names.next()) != null; ) { hs.add(checkElmStr(en).string(info)); } } final TokenList tl = new TokenList(); final ArchiveIn in = ArchiveIn.get(archive.input(info), info); try { while (in.more()) { final ZipEntry ze = in.entry(); if (ze.isDirectory()) continue; if (hs == null || hs.delete(token(ze.getName())) != 0) tl.add(in.read()); } } catch (final IOException ex) { Util.debug(ex); ARCH_FAIL.thrw(info, ex); } finally { in.close(); } return tl; }
@Override public Item item(final QueryContext qc, final InputInfo ii) throws QueryException { final Iter ir = iter(qc); final Item it = ir.next(); if (it == null || ir.size() == 1) return it; final Item n = ir.next(); if (n != null) { final ValueBuilder vb = new ValueBuilder().add(it).add(n); if (ir.next() != null) vb.add(Str.get(DOTS)); throw SEQFOUND_X.get(info, vb.value()); } return it; }
/** * Performs the test function. * * @param ctx query context * @return resulting value * @throws QueryException query exception */ private Item test(final QueryContext ctx) throws QueryException { final Unit unit = new Unit(ctx, info); if (expr.length == 0) return unit.test(sc); final ArrayList<StaticFunc> funcs = new ArrayList<>(); final Iter ir = ctx.iter(expr[0]); for (Item it; (it = ir.next()) != null; ) { final FItem fi = checkFunc(it, ctx); if (fi.funcName() != null) { final StaticFunc sf = ctx.funcs.get(fi.funcName(), fi.arity(), null, true); if (sf != null) funcs.add(sf); } } return unit.test(sc, funcs); }
@Override protected NodeSeqBuilder eval(final Iter[] iter) throws QueryException { final NodeSeqBuilder nc = new NodeSeqBuilder().check(); for (Item it; (it = iter[0].next()) != null; ) nc.add(checkNode(it)); final boolean db = nc.dbnodes(); for (int e = 1; e != expr.length && nc.size() != 0; ++e) { final Iter ir = iter[e]; for (Item it; (it = ir.next()) != null; ) { final int i = nc.indexOf(checkNode(it), db); if (i != -1) nc.delete(i); } } return nc; }
@Override public Value value(final QueryContext qc) throws QueryException { final FItem f = checkArity(exprs[1], 1, qc); final Iter iter = exprs[0].iter(qc); Item it = iter.next(); if (it == null) return Empty.SEQ; final Value v1 = f.invokeValue(qc, info, it); it = iter.next(); if (it == null) return v1; final ValueBuilder vb = new ValueBuilder().add(v1); do { vb.add(f.invokeValue(qc, info, it)); } while ((it = iter.next()) != null); return vb.value(); }
@Override protected ANodeList eval(final Iter[] iter) throws QueryException { final ANodeList list = new ANodeList().check(); for (Item it; (it = iter[0].next()) != null; ) list.add(toNode(it)); final boolean db = list.dbnodes(); final int el = exprs.length; for (int e = 1; e < el && !list.isEmpty(); e++) { final Iter ir = iter[e]; for (Item it; (it = ir.next()) != null; ) { final int i = list.indexOf(toNode(it), db); if (i != -1) list.delete(i); } } return list; }
/** * Performs the assert-equals function. * * @param ctx query context * @return resulting value * @throws QueryException query exception */ private Item assertEquals(final QueryContext ctx) throws QueryException { final byte[] str = expr.length < 3 ? null : checkStr(expr[2], ctx); final Iter iter1 = ctx.iter(expr[0]), iter2 = ctx.iter(expr[1]); final Compare comp = new Compare(info); Item it1, it2; int c = 1; while (true) { it1 = iter1.next(); it2 = iter2.next(); final boolean empty1 = it1 == null, empty2 = it2 == null; if (empty1 && empty2) return null; if (empty1 || empty2 || !comp.deep(it1.iter(), it2.iter())) break; c++; } if (str != null) throw UNIT_MESSAGE.get(info, str); throw new UnitException(info, UNIT_ASSERT_EQUALS, it1, it2, c); }
@Override public final Item ebv(final QueryContext qc, final InputInfo ii) throws QueryException { final Item it; if (seqType().zeroOrOne()) { it = item(qc, info); } else { final Iter ir = iter(qc); it = ir.next(); if (it != null && !(it instanceof ANode)) { final Item n = ir.next(); if (n != null) { final ValueBuilder vb = new ValueBuilder().add(it).add(n); if (ir.next() != null) vb.add(Str.get(DOTS)); throw EBV_X.get(info, vb.value()); } } } return it == null ? Bln.FALSE : it; }
@Override public Iter iter(final QueryContext qc) throws QueryException { final Iter ir = exprs[0].iter(qc); final long len = ir.size(); if (len == 0) throw ONEORMORE.get(info); if (len > 0) return ir; return new Iter() { private boolean first = true; @Override public Item next() throws QueryException { final Item it = ir.next(); if (first) { if (it == null) throw ONEORMORE.get(info); first = false; } return it; } }; }
/** * Deletes files from an archive. * * @param ctx query context * @return updated archive * @throws QueryException query exception */ private B64 delete(final QueryContext ctx) throws QueryException { final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64); // entries to be deleted final TokenObjMap<Item[]> hm = new TokenObjMap<Item[]>(); final Iter names = ctx.iter(expr[1]); for (Item en; (en = names.next()) != null; ) { hm.add(checkElmStr(en).string(info), null); } final ArchiveIn in = ArchiveIn.get(archive.input(info), info); final ArchiveOut out = ArchiveOut.get(in.format(), info); try { if (in instanceof GZIPIn) ARCH_MODIFY.thrw(info, in.format().toUpperCase(Locale.ENGLISH)); while (in.more()) if (!hm.contains(token(in.entry().getName()))) out.write(in); } catch (final IOException ex) { Util.debug(ex); ARCH_FAIL.thrw(info, ex); } finally { in.close(); out.close(); } return new B64(out.toArray()); }
/** * Returns a minimum or maximum item. * * @param cmp comparator * @param qc query context * @return resulting item * @throws QueryException query exception */ Item minmax(final OpV cmp, final QueryContext qc) throws QueryException { final Collation coll = toCollation(1, qc); final Iter iter = exprs[0].atomIter(qc, info); Item curr = iter.next(); if (curr == null) return null; // check if first item is comparable cmp.eval(curr, curr, coll, sc, info); // strings if (curr instanceof AStr) { for (Item it; (it = iter.next()) != null; ) { if (!(it instanceof AStr)) throw EXPTYPE_X_X_X.get(info, curr.type, it.type, it); final Type rt = curr.type, ri = it.type; if (cmp.eval(curr, it, coll, sc, info)) curr = it; if (rt != ri && curr.type == URI) curr = STR.cast(curr, qc, sc, info); } return curr; } // dates, durations, booleans, binary values if (curr instanceof ADate || curr instanceof Dur || curr instanceof Bin || curr.type == BLN) { for (Item it; (it = iter.next()) != null; ) { if (curr.type != it.type) throw EXPTYPE_X_X_X.get(info, curr.type, it.type, it); if (cmp.eval(curr, it, coll, sc, info)) curr = it; } return curr; } // numbers if (curr.type.isUntyped()) curr = DBL.cast(curr, qc, sc, info); for (Item it; (it = iter.next()) != null; ) { final Type type = numType(curr, it); if (cmp.eval(curr, it, coll, sc, info) || Double.isNaN(it.dbl(info))) curr = it; if (type != null) curr = (Item) type.cast(curr, qc, sc, info); } return curr; }
@Override public B64 item(final QueryContext qc, final InputInfo ii) throws QueryException { checkCreate(qc); final IOFile root = new IOFile(toPath(0, qc).toString()); final ArchOptions opts = toOptions(1, Q_OPTIONS, new ArchOptions(), qc); final Iter entries; if (exprs.length > 2) { entries = qc.iter(exprs[2]); } else { final TokenList tl = new TokenList(); for (final String file : root.descendants()) tl.add(file); entries = StrSeq.get(tl).iter(); } final String format = opts.get(ArchOptions.FORMAT); final int level = level(opts); if (!root.isDir()) throw FILE_NO_DIR_X.get(info, root); try (final ArchiveOut out = ArchiveOut.get(format.toLowerCase(Locale.ENGLISH), info)) { out.level(level); try { while (true) { Item en = entries.next(); if (en == null) break; en = checkElemToken(en); final IOFile file = new IOFile(root, string(en.string(info))); if (!file.exists()) throw FILE_NOT_FOUND_X.get(info, file); if (file.isDir()) throw FILE_IS_DIR_X.get(info, file); add(en, new B64(file.read()), out, level, qc); } } catch (final IOException ex) { throw ARCH_FAIL_X.get(info, ex); } return new B64(out.finish()); } }
/** * Creates a new archive. * * @param ctx query context * @return archive * @throws QueryException query exception */ private B64 create(final QueryContext ctx) throws QueryException { final Iter entr = ctx.iter(expr[0]); final Iter cont = ctx.iter(expr[1]); final Item opt = expr.length > 2 ? expr[2].item(ctx, info) : null; final TokenMap map = new FuncParams(Q_OPTIONS, info).parse(opt); final byte[] f = map.get(FORMAT); final String format = f != null ? string(lc(f)) : "zip"; final ArchiveOut out = ArchiveOut.get(format, info); // check algorithm final byte[] alg = map.get(ALGORITHM); int level = ZipEntry.DEFLATED; if (alg != null) { if (format.equals("zip") && !eq(alg, STORED, DEFLATE) || format.equals("gzip") && !eq(alg, DEFLATE)) { ARCH_SUPP.thrw(info, ALGORITHM, alg); } if (eq(alg, STORED)) level = ZipEntry.STORED; else if (eq(alg, DEFLATE)) level = ZipEntry.DEFLATED; } out.level(level); try { int e = 0; int c = 0; Item en, cn; while (true) { en = entr.next(); cn = cont.next(); if (en == null || cn == null) break; if (out instanceof GZIPOut && c > 0) ARCH_ONE.thrw(info, format.toUpperCase(Locale.ENGLISH)); add(checkElmStr(en), cn, out, level); e++; c++; } // count remaining entries if (cn != null) do c++; while (cont.next() != null); if (en != null) do e++; while (entr.next() != null); if (e != c) throw ARCH_DIFF.thrw(info, e, c); } catch (final IOException ex) { Util.debug(ex); throw ARCH_FAIL.thrw(info, ex); } finally { out.close(); } return new B64(out.toArray()); }
/** * Updates an archive. * * @param ctx query context * @return updated archive * @throws QueryException query exception */ private B64 update(final QueryContext ctx) throws QueryException { final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64); // entries to be updated final TokenObjMap<Item[]> hm = new TokenObjMap<Item[]>(); final Iter entr = ctx.iter(expr[1]); final Iter cont = ctx.iter(expr[2]); int e = 0; int c = 0; Item en, cn; while (true) { en = entr.next(); cn = cont.next(); if (en == null || cn == null) break; hm.add(checkElmStr(en).string(info), new Item[] {en, cn}); e++; c++; } // count remaining entries if (cn != null) do c++; while (cont.next() != null); if (en != null) do e++; while (entr.next() != null); if (e != c) ARCH_DIFF.thrw(info, e, c); final ArchiveIn in = ArchiveIn.get(archive.input(info), info); final ArchiveOut out = ArchiveOut.get(in.format(), info); try { if (in instanceof GZIPIn) ARCH_MODIFY.thrw(info, in.format().toUpperCase(Locale.ENGLISH)); // delete entries to be updated while (in.more()) if (!hm.contains(token(in.entry().getName()))) out.write(in); // add new and updated entries for (final byte[] h : hm) { if (h == null) continue; final Item[] it = hm.get(h); add(it[0], it[1], out, ZipEntry.DEFLATED); } } catch (final IOException ex) { Util.debug(ex); ARCH_FAIL.thrw(info, ex); } finally { in.close(); out.close(); } return new B64(out.toArray()); }
/** * Evaluates the specified query. * * @param query query * @return success flag */ final boolean query(final String query) { final Performance p = new Performance(); String error; if (exception != null) { error = Util.message(exception); } else { try { long hits = 0; final boolean run = options.get(MainOptions.RUNQUERY); final boolean serial = options.get(MainOptions.SERIALIZE); final int runs = Math.max(1, options.get(MainOptions.RUNS)); for (int r = 0; r < runs; ++r) { // reuse existing processor instance if (r != 0) qp = null; qp(query, context); parse(p); if (r == 0) plan(false); qp.compile(); info.compiling += p.time(); if (r == 0) plan(true); if (!run) continue; final PrintOutput po = r == 0 && serial ? out : new NullOutput(); try (final Serializer ser = qp.getSerializer(po)) { if (maxResults >= 0) { result = qp.cache(maxResults); info.evaluating += p.time(); result.serialize(ser); hits = result.size(); } else { hits = 0; final Iter ir = qp.iter(); info.evaluating += p.time(); for (Item it; (it = ir.next()) != null; ) { ser.serialize(it); ++hits; checkStop(); } } } qp.close(); info.serializing += p.time(); } // dump some query info // out.flush(); // remove string list if global locking is used and if query is updating if (soptions.get(StaticOptions.GLOBALLOCK) && qp.updating) { info.readLocked = null; info.writeLocked = null; } return info(info.toString(qp, out.size(), hits, options.get(MainOptions.QUERYINFO))); } catch (final QueryException | IOException ex) { exception = ex; error = Util.message(ex); } catch (final ProcException ex) { error = INTERRUPTED; } catch (final StackOverflowError ex) { Util.debug(ex); error = BASX_STACKOVERFLOW.desc; } catch (final RuntimeException ex) { extError(""); Util.debug(info()); throw ex; } finally { // close processor after exceptions if (qp != null) qp.close(); } } return extError(error); }
/** * Returns the next token of the specified iterator or {@code null}. * * @param iter iterator to be checked * @return item * @throws QueryException query exception */ private byte[] nextToken(final Iter iter) throws QueryException { final Item it = iter.next(); return it == null ? null : toToken(it); }
/** * Evaluates the specified function and creates a response. * * @throws Exception exception (including unexpected ones) */ void create() throws Exception { // bind variables final StaticFunc sf = function.function; final Expr[] args = new Expr[sf.args.length]; function.bind(http, args, error); // wrap function with a function call final MainModule mm = new MainModule(sf, args); // assign main module and http context and register process query.mainModule(mm); query.http(http); query.context.register(query); String redirect = null, forward = null; RestXqRespBuilder resp = null; try { // compile and evaluate query query.compile(); final Iter iter = query.iter(); Item item = iter.next(); // handle response element if (item != null && item instanceof ANode) { final ANode node = (ANode) item; // send redirect to browser if (REST_REDIRECT.eq(node)) { final ANode ch = node.children().next(); if (ch == null || ch.type != NodeType.TXT) throw function.error(NO_VALUE, node.name()); redirect = string(ch.string()).trim(); return; } // server-side forwarding if (REST_FORWARD.eq(node)) { final ANode ch = node.children().next(); if (ch == null || ch.type != NodeType.TXT) throw function.error(NO_VALUE, node.name()); forward = string(ch.string()).trim(); return; } if (REST_RESPONSE.eq(node)) { resp = new RestXqRespBuilder(); resp.build(node, function, iter, http); return; } } // HEAD method must return a single response element if (function.methods.size() == 1 && function.methods.contains(HTTPMethod.HEAD.name())) throw function.error(HEAD_METHOD); // serialize result final SerializerOptions sp = function.output; http.sopts(sp); http.initResponse(); final Serializer ser = Serializer.get(http.res.getOutputStream(), sp); for (; item != null; item = iter.next()) ser.serialize(item); ser.close(); } finally { query.close(); query.context.unregister(query); if (redirect != null) { http.res.sendRedirect(redirect); } else if (forward != null) { http.req.getRequestDispatcher(forward).forward(http.req, http.res); } else if (resp != null) { if (resp.status != 0) http.status(resp.status, resp.message, resp.error); http.res.getOutputStream().write(resp.cache.toArray()); } } }