@Override public Iter iter(final QueryContext ctx) throws QueryException { final Iter iter = ctx.iter(expr); final Item it = iter.next(); if (it == null) { if (type.mayBeZero()) return Empty.ITER; throw XPEMPTY.thrw(input, desc()); } if (type.zeroOrOne()) { if (iter.next() != null) NOTREATS.thrw(input, desc(), type); if (!it.type.instance(type.type)) NOTREAT.thrw(input, desc(), type, it.type); return it.iter(); } return new Iter() { Item i = it; @Override public Item next() throws QueryException { if (i == null) return null; if (!i.type.instance(type.type)) NOTREAT.thrw(input, desc(), type, i.type); final Item ii = i; i = iter.next(); return ii; } }; }
/** * Checks if the items can be compared. Items are comparable * * @param b second item * @return result of check */ public final boolean comparable(final Item b) { return type == b.type || num() && b.num() || (unt() || str()) && (b.str() || b.unt()) || dur() && b.dur() || func(); }
/** * Extracts connection options. * * @param arg argument with options * @param root expected root element * @param ctx query context * @return options * @throws QueryException query exception */ private TokenObjMap<Object> options(final int arg, final QNm root, final QueryContext ctx) throws QueryException { // initialize token map final TokenObjMap<Object> tm = new TokenObjMap<Object>(); // argument does not exist... if (arg >= expr.length) return tm; // empty sequence... final Item it = expr[arg].item(ctx, input); if (it == null) return tm; // XQuery map: convert to internal map if (it instanceof Map) return ((Map) it).tokenJavaMap(input); // no element: convert XQuery map to internal map if (!it.type().eq(SeqType.ELM)) throw NODFUNTYPE.thrw(input, this, it.type); // parse nodes ANode node = (ANode) it; if (!node.qname().eq(root)) PARWHICH.thrw(input, node.qname()); // interpret query parameters final AxisIter ai = node.children(); while ((node = ai.next()) != null) { final QNm qn = node.qname(); if (!qn.uri().eq(Uri.uri(SQLURI))) PARWHICH.thrw(input, qn); tm.add(qn.ln(), node.children().next()); } return tm; }
@Override public NodeIter iter(final QueryContext ctx) throws QueryException { final Value v = checkCtx(ctx); if (!v.type.isNode()) NODESPATH.thrw(input, AxisStep.this, v.type); final AxisIter ai = axis.iter((ANode) v); final NodeCache nc = new NodeCache(); for (ANode n; (n = ai.next()) != null; ) if (test.eval(n)) nc.add(n.finish()); // evaluate predicates for (final Expr p : preds) { ctx.size = nc.size(); ctx.pos = 1; int c = 0; for (int n = 0; n < nc.size(); ++n) { ctx.value = nc.get(n); final Item i = p.test(ctx, input); if (i != null) { // assign score value nc.get(n).score(i.score()); nc.item[c++] = nc.get(n); } ctx.pos++; } nc.size(c); } return nc; }
@Override public String getItemAsString(final Properties props) throws XQException { try { return it.node() && it.type != NodeType.TXT ? serialize() : Token.string(it.atom(null)); } catch (final QueryException e) { throw new XQException(e.getMessage(), e.code()); } }
/** * Casts the current item. * * @param type expected type * @return cast item * @throws XQException xquery exception */ private long castItr(final Type type) throws XQException { opened(); try { final double d = it.dbl(null); if (!it.num() || d != (long) d) throw new BXQException(NUM, d); return type.e(it, ctx.ctx, null).itr(null); } catch (final QueryException ex) { throw new BXQException(ex); } }
@Override public Item ev(final InputInfo ii, final Item a, final Item b) throws QueryException { checkNum(ii, a, b); final double d1 = a.dbl(ii); final double d2 = b.dbl(ii); if (d2 == 0) DIVZERO.thrw(ii, a); final double d = d1 / d2; if (Double.isNaN(d) || Double.isInfinite(d)) DIVFLOW.thrw(ii, d1, d2); return Int.get(type(a.type, b.type) == ITR ? a.itr(ii) / b.itr(ii) : (long) d); }
@Override public String getAtomicValue() throws XQException { opened(); if (it.node()) throw new BXQException(ATOM); try { return Token.string(it.atom(null)); } catch (final QueryException e) { // function item throw new BXQException(ATOM); } }
/** * Checks if the predicates are successful for the specified item. * * @param it item to be checked * @param ctx query context * @return result of check * @throws QueryException query exception */ public boolean preds(final Item it, final QueryContext ctx) throws QueryException { // set context item and position ctx.value = it; for (final Expr p : pred) { final Item i = p.test(ctx, input); if (i == null) return false; // item accepted.. adopt last scoring value it.score(i.score()); } return true; }
@Override public final Item ebv(final QueryContext ctx, final InputInfo ii) throws QueryException { Item it = null; if (type().zeroOrOne()) { it = item(ctx, input); } else { final Iter ir = iter(ctx); it = ir.next(); if (it != null && !it.node() && ir.next() != null) CONDTYPE.thrw(input, this); } return it == null ? Bln.FALSE : it; }
@Override public Item ev(final InputInfo ii, final Item a, final Item b) throws QueryException { final Type ta = a.type, tb = b.type; final boolean t1 = ta.isNumber() || ta.isUntyped(); final boolean t2 = tb.isNumber() || tb.isUntyped(); if (t1 ^ t2) errNum(ii, !t1 ? a : b); if (t1 && t2) { final Type t = type(ta, tb); if (t == ITR) { final long l1 = a.itr(ii); final long l2 = b.itr(ii); checkRange(ii, l1 - (double) l2); return Int.get(l1 - l2); } if (t == DBL) return Dbl.get(a.dbl(ii) - b.dbl(ii)); if (t == FLT) return Flt.get(a.flt(ii) - b.flt(ii)); return Dec.get(a.dec(ii).subtract(b.dec(ii))); } if (ta == tb) { if (ta == DTM || ta == DAT || ta == TIM) return new DTd((Date) a, (Date) b); if (ta == YMD) return new YMd((YMd) a, (YMd) b, false); if (ta == DTD) return new DTd((DTd) a, (DTd) b, false); errNum(ii, !t1 ? a : b); } if (ta == DTM) return new Dtm((Date) a, checkDur(ii, b), false, ii); if (ta == DAT) return new Dat((Date) a, checkDur(ii, b), false, ii); if (ta == TIM) { if (tb != DTD) errType(ii, DTD, b); return new Tim((Tim) a, (DTd) b, false, ii); } errType(ii, ta, b); return null; }
@Override public void prepare() throws QueryException { // build data with all documents, to prevent dirty reads md = new MemData(data); for (final Item d : docs) { final MemData docData; if (d.node()) { // adding a document node final ANode doc = (ANode) d; if (doc.ndType() != NodeType.DOC) UPDOCTYPE.thrw(input, doc); docData = new MemData(data); new DataBuilder(docData).build(doc); } else if (d.str()) { // adding file(s) from a path final String docpath = string(d.atom(input)); final String nm = ctx.mprop.random(data.meta.name); final DirParser p = new DirParser(IO.get(docpath), ctx.prop); final MemBuilder b = new MemBuilder(nm, p, ctx.prop); try { docData = b.build(); } catch (final IOException e) { throw DOCERR.thrw(input, docpath); } } else { throw STRNODTYPE.thrw(input, this, d.type); } md.insert(md.meta.size, -1, docData); } // set new names, if needed final IntList pres = md.doc(); if (pres.size() == 1 && name != null) { // name is specified and a single document is added: set the name final byte[] nm = path == null ? name : concat(path, SLASH, name); md.update(pres.get(0), Data.DOC, nm); } else if (path != null) { // path is specified: replace the path of each new document for (int i = 0, is = pres.size(); i < is; i++) { final int d = pres.get(i); final byte[] old = md.text(d, true); final int p = lastIndexOf(old, '/'); final byte[] nm = p < 0 ? old : subtoken(old, p + 1); md.update(d, Data.DOC, concat(path, SLASH, nm)); } } }
/** * Sends an event to the registered sessions. * * @param ctx query context * @return event result * @throws QueryException query exception */ private Item event(final QueryContext ctx) throws QueryException { final byte[] name = checkStr(expr[0], ctx); final ArrayOutput ao = new ArrayOutput(); try { // run serialization final Serializer ser = Serializer.get(ao, ctx.serProp(true)); final ValueIter ir = ctx.value(expr[1]).iter(); for (Item it; (it = ir.next()) != null; ) it.serialize(ser); ser.close(); } catch (final SerializerException ex) { throw ex.getCause(input); } catch (final IOException ex) { SERANY.thrw(input, ex); } // throw exception if event is unknown if (!ctx.context.events.notify(ctx.context, name, ao.toArray())) { NOEVENT.thrw(input, name); } return null; }
@Override public URI getNodeUri() throws XQException { opened(); if (!it.node()) throw new BXQException(NODE); final ANode node = (ANode) it; try { return new URI(Token.string(node.base())); } catch (final URISyntaxException ex) { throw new BXQException(ex.toString()); } }
@Override public Item ev(final InputInfo ii, final Item a, final Item b) throws QueryException { checkNum(ii, a, b); final Type t = type(a.type, b.type); if (t == DBL) return Dbl.get(a.dbl(ii) % b.dbl(ii)); if (t == FLT) return Flt.get(a.flt(ii) % b.flt(ii)); if (t == ITR) { final long b1 = a.itr(ii); final long b2 = b.itr(ii); if (b2 == 0) DIVZERO.thrw(ii, a); return Int.get(b1 % b2); } final BigDecimal b1 = a.dec(ii); final BigDecimal b2 = b.dec(ii); if (b2.signum() == 0) DIVZERO.thrw(ii, a); final BigDecimal q = b1.divide(b2, 0, BigDecimal.ROUND_DOWN); return Dec.get(b1.subtract(q.multiply(b2))); }
@Override public Item ev(final InputInfo ii, final Item a, final Item b) throws QueryException { final Type ta = a.type, tb = b.type; if (ta == tb) { if (ta == YMD) { final BigDecimal bd = BigDecimal.valueOf(((YMd) b).ymd()); if (bd.equals(BigDecimal.ZERO)) DATEZERO.thrw(ii, info()); return Dec.get( BigDecimal.valueOf(((YMd) a).ymd()).divide(bd, 20, BigDecimal.ROUND_HALF_EVEN)); } if (ta == DTD) { final BigDecimal bd = ((DTd) b).dtd(); if (bd.equals(BigDecimal.ZERO)) DATEZERO.thrw(ii, info()); return Dec.get(((DTd) a).dtd().divide(bd, 20, BigDecimal.ROUND_HALF_EVEN)); } } if (ta == YMD) { if (!tb.isNumber()) errNum(ii, b); return new YMd((Dur) a, b.dbl(ii), false, ii); } if (ta == DTD) { if (!tb.isNumber()) errNum(ii, b); return new DTd((Dur) a, b.dbl(ii), false, ii); } checkNum(ii, a, b); final Type t = type(ta, tb); if (t == DBL) return Dbl.get(a.dbl(ii) / b.dbl(ii)); if (t == FLT) return Flt.get(a.flt(ii) / b.flt(ii)); final BigDecimal b1 = a.dec(ii); final BigDecimal b2 = b.dec(ii); if (b2.signum() == 0) DIVZERO.thrw(ii, a); final int s = Math.max(18, Math.max(b1.scale(), b2.scale())); return Dec.get(b1.divide(b2, s, BigDecimal.ROUND_HALF_EVEN)); }
/** * 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); if (!it.str() && !it.unt()) Err.type(this, AtomType.STR, it); return it.atom(input); }
@Override public Object getObject() throws XQException { opened(); return it.toJava(); }
@Override public final boolean eq(final InputInfo ii, final Item it) throws QueryException { return !it.unt() ? it.eq(ii, this) : Token.eq(atom(), it.atom(ii)); }
@Override public Node getNode() throws XQException { opened(); if (!it.node()) throw new BXQException(WRONG, NodeType.NOD, it.type); return ((ANode) it).toJava(); }
@Override public final int diff(final InputInfo ii, final Item it) throws QueryException { return !it.unt() ? -it.diff(ii, this) : Token.diff(atom(), it.atom(ii)); }
/** * Parses the specified test case. * * @param root root node * @throws Exception exception * @return true if the query, specified by {@link #single}, was evaluated */ private boolean parse(final Nodes root) throws Exception { final String pth = text("@FilePath", root); final String outname = text("@name", root); if (single != null && !outname.startsWith(single)) return true; final Performance perf = new Performance(); if (verbose) Util.out("- " + outname); boolean inspect = false; boolean correct = true; final Nodes nodes = states(root); for (int n = 0; n < nodes.size(); ++n) { final Nodes state = new Nodes(nodes.list[n], nodes.data); final String inname = text("*:query/@name", state); context.query = new IOFile(queries + pth + inname + IO.XQSUFFIX); final String in = read(context.query); String er = null; ItemCache iter = null; boolean doc = true; final Nodes cont = nodes("*:contextItem", state); Nodes curr = null; if (cont.size() != 0) { final Data d = Check.check(context, srcs.get(string(data.atom(cont.list[0])))); curr = new Nodes(d.doc(), d); curr.root = true; } context.prop.set(Prop.QUERYINFO, compile); final QueryProcessor xq = new QueryProcessor(in, curr, context); context.prop.set(Prop.QUERYINFO, false); // limit result sizes to 1MB final ArrayOutput ao = new ArrayOutput(); final TokenBuilder files = new TokenBuilder(); try { files.add( file(nodes("*:input-file", state), nodes("*:input-file/@variable", state), xq, n == 0)); files.add(file(nodes("*:defaultCollection", state), null, xq, n == 0)); var(nodes("*:input-URI", state), nodes("*:input-URI/@variable", state), xq); eval(nodes("*:input-query/@name", state), nodes("*:input-query/@variable", state), pth, xq); parse(xq, state); for (final int p : nodes("*:module", root).list) { final String uri = text("@namespace", new Nodes(p, data)); final String file = mods.get(string(data.atom(p))) + IO.XQSUFFIX; xq.module(file, uri); } // evaluate and serialize query final SerializerProp sp = new SerializerProp(); sp.set(SerializerProp.S_INDENT, context.prop.is(Prop.CHOP) ? DataText.YES : DataText.NO); final XMLSerializer xml = new XMLSerializer(ao, sp); iter = xq.value().cache(); for (Item it; (it = iter.next()) != null; ) { doc &= it.type == NodeType.DOC; it.serialize(xml); } xml.close(); } catch (final Exception ex) { if (!(ex instanceof QueryException || ex instanceof IOException)) { System.err.println("\n*** " + outname + " ***"); System.err.println(in + "\n"); ex.printStackTrace(); } er = ex.getMessage(); if (er.startsWith(STOPPED)) er = er.substring(er.indexOf('\n') + 1); if (er.startsWith("[")) er = er.replaceAll("\\[(.*?)\\] (.*)", "$1 $2"); // unexpected error - dump stack trace } // print compilation steps if (compile) { Util.errln("---------------------------------------------------------"); Util.err(xq.info()); Util.errln(in); } final Nodes expOut = nodes("*:output-file/text()", state); final TokenList result = new TokenList(); for (int o = 0; o < expOut.size(); ++o) { final String resFile = string(data.atom(expOut.list[o])); final IOFile exp = new IOFile(expected + pth + resFile); result.add(read(exp)); } final Nodes cmpFiles = nodes("*:output-file/@compare", state); boolean xml = false; boolean frag = false; boolean ignore = false; for (int o = 0; o < cmpFiles.size(); ++o) { final byte[] type = data.atom(cmpFiles.list[o]); xml |= eq(type, XML); frag |= eq(type, FRAGMENT); ignore |= eq(type, IGNORE); } String expError = text("*:expected-error/text()", state); final StringBuilder log = new StringBuilder(pth + inname + IO.XQSUFFIX); if (files.size() != 0) { log.append(" ["); log.append(files); log.append("]"); } log.append(NL); /** Remove comments. */ log.append(norm(in)); log.append(NL); final String logStr = log.toString(); // skip queries with variable results final boolean print = currTime || !logStr.contains("current-"); boolean correctError = false; if (er != null && (expOut.size() == 0 || !expError.isEmpty())) { expError = error(pth + outname, expError); final String code = er.substring(0, Math.min(8, er.length())); for (final String e : SLASH.split(expError)) { if (code.equals(e)) { correctError = true; break; } } } if (correctError) { if (print) { logOK.append(logStr); logOK.append("[Right] "); logOK.append(norm(er)); logOK.append(NL); logOK.append(NL); addLog(pth, outname + ".log", er); } ++ok; } else if (er == null) { int s = -1; final int rs = result.size(); while (!ignore && ++s < rs) { inspect |= s < cmpFiles.list.length && eq(data.atom(cmpFiles.list[s]), INSPECT); final byte[] res = result.get(s), actual = ao.toArray(); if (res.length == ao.size() && eq(res, actual)) break; if (xml || frag) { iter.reset(); try { final ItemCache ic = toIter(string(res).replaceAll("^<\\?xml.*?\\?>", "").trim(), frag); if (FNSimple.deep(null, iter, ic)) break; ic.reset(); final ItemCache ia = toIter(string(actual), frag); if (FNSimple.deep(null, ia, ic)) break; } catch (final Throwable ex) { System.err.println("\n" + outname + ":"); ex.printStackTrace(); } } } if ((rs > 0 || !expError.isEmpty()) && s == rs && !inspect) { if (print) { if (expOut.size() == 0) result.add(error(pth + outname, expError)); logErr.append(logStr); logErr.append("[" + testid + " ] "); logErr.append(norm(string(result.get(0)))); logErr.append(NL); logErr.append("[Wrong] "); logErr.append(norm(ao.toString())); logErr.append(NL); logErr.append(NL); addLog(pth, outname + (xml ? IO.XMLSUFFIX : ".txt"), ao.toString()); } correct = false; ++err; } else { if (print) { logOK.append(logStr); logOK.append("[Right] "); logOK.append(norm(ao.toString())); logOK.append(NL); logOK.append(NL); addLog(pth, outname + (xml ? IO.XMLSUFFIX : ".txt"), ao.toString()); } ++ok; } } else { if (expOut.size() == 0 || !expError.isEmpty()) { if (print) { logOK2.append(logStr); logOK2.append("[" + testid + " ] "); logOK2.append(norm(expError)); logOK2.append(NL); logOK2.append("[Rght?] "); logOK2.append(norm(er)); logOK2.append(NL); logOK2.append(NL); addLog(pth, outname + ".log", er); } ++ok2; } else { if (print) { logErr2.append(logStr); logErr2.append("[" + testid + " ] "); logErr2.append(norm(string(result.get(0)))); logErr2.append(NL); logErr2.append("[Wrong] "); logErr2.append(norm(er)); logErr2.append(NL); logErr2.append(NL); addLog(pth, outname + ".log", er); } correct = false; ++err2; } } if (curr != null) Close.close(curr.data, context); xq.close(); } if (reporting) { logReport.append(" <test-case name=\""); logReport.append(outname); logReport.append("\" result='"); logReport.append(correct ? "pass" : "fail"); if (inspect) logReport.append("' todo='inspect"); logReport.append("'/>"); logReport.append(NL); } // print verbose/timing information final long nano = perf.getTime(); final boolean slow = nano / 1000000 > timer; if (verbose) { if (slow) Util.out(": " + Performance.getTimer(nano, 1)); Util.outln(); } else if (slow) { Util.out(NL + "- " + outname + ": " + Performance.getTimer(nano, 1)); } return single == null || !outname.equals(single); }
/** * 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); if (!it.unt() && it.type != 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); if (!it.unt() && !it.num()) Err.number(this, it); return it.dbl(input); }
/** * 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 { if (!it.unt() && !it.type.instance(AtomType.ITR)) Err.type(this, AtomType.ITR, it); return it.itr(input); }
@Override public final Item test(final QueryContext ctx, final InputInfo ii) throws QueryException { final Item it = ebv(ctx, input); return (it.num() ? it.dbl(input) == ctx.pos : it.bool(input)) ? it : null; }
/** * 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.node()) Err.type(this, NodeType.NOD, it); return (ANode) it; }
@Override public Iter iter(final QueryContext ctx) throws QueryException { final Item it = item(ctx, input); return it != null ? it.iter() : Empty.ITER; }
/** * Checks the items for equivalence. * * @param ii input info * @param it item to be compared * @return result of check * @throws QueryException query exception */ public final boolean equiv(final InputInfo ii, final Item it) throws QueryException { // check if both values are NaN, or if values are equal.. return (this == Dbl.NAN || this == Flt.NAN) && it.num() && Double.isNaN(it.dbl(ii)) || comparable(it) && eq(ii, it); }
/** * 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; if (!it.str() && !it.unt()) Err.type(this, AtomType.STR, it); return it.atom(input); }