package org.basex.query.item.map; import org.basex.query.QueryException; import org.basex.query.item.AtomType; import org.basex.query.item.Item; import org.basex.query.item.SeqType; import org.basex.query.item.Value; import org.basex.query.iter.ItemCache; import org.basex.util.InputInfo; /** * Inner node of a {@link Map}. * * @author BaseX Team 2005-12, BSD License * @author Leo Woerteler */ final class Branch extends TrieNode { /** Child array. */ private final TrieNode[] kids; /** Bit array with a bit set for every used slot. */ final int used; /** * Constructor taking children array and the size of this map. * @param ch children * @param u bit array * @param s size of this node */ Branch(final TrieNode[] ch, final int u, final int s) { super(s); kids = ch; used = u; assert verify(); } /** * Copies the children array. * This is faster than {@code kids.clone()} according to <a * href="http://www.javaspecialists.eu/archive/Issue124.html">Heinz M. * Kabutz</a>. * @return copy of the child array */ TrieNode[] copyKids() { final TrieNode[] copy = new TrieNode[KIDS]; System.arraycopy(kids, 0, copy, 0, KIDS); return copy; } @Override TrieNode insert(final int h, final Item k, final Value v, final int l, final InputInfo ii) throws QueryException { final int key = key(h, l); final TrieNode sub = kids[key], nsub; final int bs, rem; if(sub != null) { nsub = sub.insert(h, k, v, l + 1, ii); if(nsub == sub) return this; bs = used; rem = sub.size; } else { nsub = new Leaf(h, k, v); bs = used | 1 << key; rem = 0; } final TrieNode[] ks = copyKids(); ks[key] = nsub; return new Branch(ks, bs, size - rem + nsub.size); } @Override TrieNode delete(final int h, final Item k, final int l, final InputInfo ii) throws QueryException { final int key = key(h, l); final TrieNode sub = kids[key]; if(sub == null) return this; final TrieNode nsub = sub.delete(h, k, l + 1, ii); if(nsub == sub) return this; final int nu; if(nsub == null) { nu = used ^ 1 << key; if(Integer.bitCount(nu) == 1) { final TrieNode single = kids[Integer.numberOfTrailingZeros(nu)]; // check whether the child depends on the right offset if(!(single instanceof Branch)) return single; } } else nu = used; final TrieNode[] ks = copyKids(); ks[key] = nsub; return new Branch(ks, nu, size - 1); } @Override Value get(final int h, final Item k, final int l, final InputInfo ii) throws QueryException { final int key = key(h, l); final TrieNode sub = kids[key]; return sub == null ? null : sub.get(h, k, l + 1, ii); } @Override boolean contains(final int h, final Item k, final int l, final InputInfo ii) throws QueryException { final int key = key(h, l); final TrieNode sub = kids[key]; return sub != null && sub.contains(h, k, l + 1, ii); } /** End strings. */ private static final String[] ENDS = { "|-- ", "| ", "`-- ", " " }; @Override StringBuilder toString(final StringBuilder sb, final String ind) { final int s = Integer.bitCount(used); for(int i = 0, j = 0; i < s; i++, j++) { while((used & 1 << j) == 0) j++; final int e = i == s - 1 ? 2 : 0; sb.append(ind).append(ENDS[e]).append( String.format("%x", j)).append('\n'); kids[j].toString(sb, ind + ENDS[e + 1]); } return sb; } @Override TrieNode addAll(final TrieNode o, final int l, final InputInfo ii) throws QueryException { return o.add(this, l, ii); } @Override TrieNode add(final Leaf o, final int l, final InputInfo ii) throws QueryException { final int k = key(o.hash, l); final TrieNode ch = kids[k], nw; if(ch != null) { final TrieNode ins = ch.add(o, l + 1, ii); if(ins == ch) return this; nw = ins; } else nw = o; final TrieNode[] ks = copyKids(); ks[k] = nw; // we don't replace here, so the size must increase return new Branch(ks, used | 1 << k, size + 1); } @Override TrieNode add(final List o, final int l, final InputInfo ii) throws QueryException { final int k = key(o.hash, l); final TrieNode ch = kids[k], nw; int n = o.size; if(ch != null) { final TrieNode ins = ch.add(o, l + 1, ii); if(ins == ch) return this; n = ins.size - ch.size; nw = ins; } else nw = o; final TrieNode[] ks = copyKids(); ks[k] = nw; // we don't replace here, so the size must increase return new Branch(ks, used | 1 << k, size + n); } @Override TrieNode add(final Branch o, final int l, final InputInfo ii) throws QueryException { TrieNode[] ch = null; int nu = used, ns = size; for(int i = 0; i < kids.length; i++) { final TrieNode k = kids[i], ok = o.kids[i]; if(ok != null) { final TrieNode nw = k == null ? ok : ok.addAll(k, l + 1, ii); if(nw != k) { if(ch == null) ch = copyKids(); ch[i] = nw; nu |= 1 << i; ns += nw.size - (k == null ? 0 : k.size); } } } return ch == null ? this : new Branch(ch, nu, ns); } @Override boolean verify() { int c = 0; for(int i = 0; i < KIDS; i++) { final boolean bit = (used & 1 << i) != 0, act = kids[i] != null; if(bit ^ act) return false; if(act) c += kids[i].size; } return c == size; } @Override void keys(final ItemCache ks) { for(final TrieNode nd : kids) if(nd != null) nd.keys(ks); } @Override boolean hasType(final AtomType kt, final SeqType vt) { for(final TrieNode k : kids) if(!(k == null || k.hasType(kt, vt))) return false; return true; } @Override int hash(final InputInfo ii) throws QueryException { int hash = 0; for(final TrieNode ch : kids) if(ch != null) hash = 31 * hash + ch.hash(ii); return hash; } @Override boolean deep(final InputInfo ii, final TrieNode o) throws QueryException { if(!(o instanceof Branch)) return false; final Branch ob = (Branch) o; // check bin usage first if(used != ob.used) return false; // recursively compare children for(int i = 0; i < KIDS; i++) if(kids[i] != null && !kids[i].deep(ii, ob.kids[i])) return false; // everything OK return true; } @Override StringBuilder toString(final StringBuilder sb) { for(int i = 0; i < KIDS; i++) if(kids[i] != null) kids[i].toString(sb); return sb; } }