package org.basex.query.flwor; import static org.basex.query.QueryText.*; import static org.basex.util.Array.*; import java.io.IOException; import java.util.Arrays; import org.basex.io.serial.Serializer; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.expr.Expr; import org.basex.query.expr.ParseExpr; import org.basex.query.item.Item; import org.basex.query.iter.Iter; import org.basex.query.util.ValueList; import org.basex.query.util.Var; import org.basex.util.InputInfo; import org.basex.util.TokenBuilder; import org.basex.util.list.ObjList; /** * Order by expression. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class Order extends ParseExpr { /** Sort list. */ final OrderBy[] ob; /** Keys to sort by. */ private ObjList<Item[]> keys; /** Values to sort. */ private ValueList values; /** * Constructor. * @param ii input info * @param o order by expressions */ public Order(final InputInfo ii, final OrderBy[] o) { super(ii); ob = o; } @Override public Expr comp(final QueryContext ctx) throws QueryException { for(final OrderBy o : ob) o.comp(ctx); return this; } @Override public Iter iter(final QueryContext ctx) { return new OrderedIter(keys, values); } /** * Sets the key and value lists for this Order instance. * @param ks key list * @param vs value list * @return reference to this object for convenience */ Order set(final ObjList<Item[]> ks, final ValueList vs) { keys = ks; values = vs; return this; } /** * Adds the items to be sorted. * @param ctx query context * @param e value to add * @param ks key list * @param vs value list * @throws QueryException query exception */ void add(final QueryContext ctx, final Expr e, final ObjList<Item[]> ks, final ValueList vs) throws QueryException { final Item[] k = new Item[ob.length]; for(int o = k.length; o-- > 0;) k[o] = ob[o].key(ctx, ks.size()); ks.add(k); vs.add(ctx.value(e)); } @Override public boolean uses(final Use u) { for(final OrderBy o : ob) if(o.uses(u)) return true; return false; } @Override public int count(final Var v) { int c = 0; for(final OrderBy o : ob) c += o.count(v); return c; } @Override public boolean removable(final Var v) { for(final OrderBy o : ob) if(!o.removable(v)) return false; return true; } @Override public Order remove(final Var v) { for(int o = 0; o < ob.length; ++o) ob[o] = ob[o].remove(v); return this; } @Override public void plan(final Serializer ser) throws IOException { ser.openElement(this); for(int o = 0; o != ob.length - 1; ++o) ob[o].plan(ser); ser.closeElement(); } @Override public String toString() { return new TokenBuilder(' ' + ORDER + ' ' + BY + ' ').addSep( Arrays.copyOf(ob, ob.length - 1), SEP).toString(); } /** * An ordered iterator. * @author Leo Woerteler */ class OrderedIter extends Iter { /** Sort keys. */ final ObjList<Item[]> kl; /** Values to sort. */ final ValueList vl; /** End position. */ int end; /** Current position. */ int pos = -1; /** Order array. */ int[] order; /** Iterator. */ Iter ir; /** * Constructor. * @param ks sort keys * @param vs values */ public OrderedIter(final ObjList<Item[]> ks, final ValueList vs) { kl = ks; vl = vs; } @Override public Item next() throws QueryException { if(order == null) { // enumerate sort array and sort entries end = vl.size(); order = new int[end]; for(int i = 0; i < end; ++i) order[i] = i; sort(order, 0, end); } while(true) { if(ir != null) { final Item i = ir.next(); if(i != null) return i; ir = null; } else { if(++pos == end) return null; ir = vl.get(order[pos]).iter(); } } } /** * Recursively sorts the specified items. * The algorithm is derived from {@link Arrays#sort(int[])}. * @param o order array * @param s start position * @param e end position * @throws QueryException query exception */ void sort(final int[] o, final int s, final int e) throws QueryException { if(e < 7) { for(int i = s; i < e + s; ++i) for(int j = i; j > s && d(o, j - 1, j) > 0; j--) swap(o, j, j - 1); return; } int m = s + (e >> 1); if(e > 7) { int l = s; int n = s + e - 1; if(e > 40) { final int k = e >>> 3; l = m(o, l, l + k, l + (k << 1)); m = m(o, m - k, m, m + k); n = m(o, n - (k << 1), n - k, n); } m = m(o, l, m, n); } final Item[] im = kl.get(o[m]); int a = s, b = a, c = s + e - 1, d = c; while(true) { while(b <= c) { final int h = d(kl.get(o[b]), im); if(h > 0) break; if(h == 0) swap(o, a++, b); ++b; } while(c >= b) { final int h = d(kl.get(o[c]), im); if(h < 0) break; if(h == 0) swap(o, c, d--); --c; } if(b > c) break; swap(o, b++, c--); } int k; final int n = s + e; k = Math.min(a - s, b - a); swap(o, s, b - k, k); k = Math.min(d - c, n - d - 1); swap(o, b, n - k, k); if((k = b - a) > 1) sort(o, s, k); if((k = d - c) > 1) sort(o, n - k, k); } /** * Returns the difference of two entries (part of QuickSort). * @param sa sort keys of first item * @param sb sort keys of second item * @return result * @throws QueryException query exception */ private int d(final Item[] sa, final Item[] sb) throws QueryException { for(int k = 0; k < ob.length; ++k) { final OrderBy or = ob[k]; final Item m = sa[k], n = sb[k]; final int c = m == null ? n == null ? 0 : or.lst ? -1 : 1 : n == null ? or.lst ? 1 : -1 : m.diff(input, n); if(c != 0) return or.desc ? -c : c; } return 0; } /** * Returns the difference of two entries (part of QuickSort). * @param o order array * @param a first position * @param b second position * @return result * @throws QueryException query exception */ private int d(final int[] o, final int a, final int b) throws QueryException { return d(kl.get(o[a]), kl.get(o[b])); } /** * Returns the index of the median of the three indexed integers. * @param o order array * @param a first offset * @param b second offset * @param c thirst offset * @return median * @throws QueryException query exception */ private int m(final int[] o, final int a, final int b, final int c) throws QueryException { final Item[] ka = kl.get(o[a]), kb = kl.get(o[b]), kc = kl.get(o[c]); return d(ka, kb) < 0 ? d(kb, kc) < 0 ? b : d(ka, kc) < 0 ? c : a : d(kb, kc) > 0 ? b : d(ka, kc) > 0 ? c : a; } } }