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.query.util.Compare;
import org.basex.util.InputInfo;
/**
* Abstract superclass of all trie nodes.
*
* @author BaseX Team 2005-12, BSD License
* @author Leo Woerteler
*/
abstract class TrieNode {
/** Number of children on each level. */
static final int KIDS = 1 << Map.BITS;
/** Mask for the bits used on the current level. */
private static final int MASK = KIDS - 1;
/** The empty node. */
static final TrieNode EMPTY = new TrieNode(0) {
@Override
StringBuilder toString(final StringBuilder sb, final String ind) {
return sb.append("map{}"); }
@Override
TrieNode delete(final int h, final Item k, final int l, final InputInfo i) {
return this; }
@Override
Value get(final int h, final Item k, final int l, final InputInfo i) {
return null; }
@Override
boolean contains(final int h, final Item k, final int l,
final InputInfo ii) { return false; }
@Override
TrieNode addAll(final TrieNode o, final int l, final InputInfo ii) {
return o; }
@Override
TrieNode add(final Leaf o, final int l, final InputInfo ii) { return o; }
@Override
TrieNode add(final List o, final int l, final InputInfo ii) { return o; }
@Override
TrieNode add(final Branch o, final int l, final InputInfo ii) { return o; }
@Override
boolean verify() { return true; }
@Override
void keys(final ItemCache ks) { }
@Override
boolean hasType(final AtomType kt, final SeqType vt) { return true; }
@Override
int hash(final InputInfo ii) throws QueryException { return 0; }
@Override
boolean deep(final InputInfo ii, final TrieNode o) { return this == o; }
@Override
public TrieNode insert(final int h, final Item k, final Value v,
final int l, final InputInfo i) throws QueryException {
return new Leaf(h, k, v); }
@Override
StringBuilder toString(final StringBuilder sb) { return sb; }
};
/** Size of this node. */
final int size;
/**
* Constructor.
* @param s size
*/
TrieNode(final int s) {
assert s != 0 || EMPTY == null;
size = s;
}
/**
* Inserts the given value into this map.
* @param hash hash code used as key
* @param key key to insert
* @param val value to insert
* @param lvl level
* @param ii input info
* @return updated map if changed, {@code this} otherwise
* @throws QueryException query exception
*/
abstract TrieNode insert(final int hash, final Item key, final Value val,
final int lvl, final InputInfo ii) throws QueryException;
/**
* Deletes a key from this map.
* @param hash hash code of the key
* @param key key to delete
* @param lvl level
* @param ii input info
* @return updated map if changed, {@code null} if deleted,
* {@code this} otherwise
* @throws QueryException query exception
*/
abstract TrieNode delete(int hash, Item key, int lvl, final InputInfo ii)
throws QueryException;
/**
* Looks up the value associated with the given key.
* @param hash hash code
* @param key key to look up
* @param lvl level
* @param ii input info
* @return bound value if found, {@code null} otherwise
* @throws QueryException query exception
*/
abstract Value get(int hash, Item key, int lvl, final InputInfo ii)
throws QueryException;
/**
* Checks if the given key exists in the map.
* @param hash hash code
* @param key key to look for
* @param lvl level
* @param ii input info
* @return {@code true}, if the key exists, {@code false} otherwise
* @throws QueryException query exception
*/
abstract boolean contains(int hash, Item key, int lvl, final InputInfo ii)
throws QueryException;
/**
* <p> Inserts all bindings from the given node into this one.
* <p> This method is part of the <i>double dispatch</i> pattern and
* should be implemented as {@code return o.add(this, lvl, ii);}.
* @param o other node
* @param lvl level
* @param ii input info
* @return updated map if changed, {@code this} otherwise
* @throws QueryException query exception
*/
abstract TrieNode addAll(final TrieNode o, final int lvl, final InputInfo ii)
throws QueryException;
/**
* Add a leaf to this node, if the key isn't already used.
* @param o leaf to insert
* @param lvl level
* @param ii input info
* @return updated map if changed, {@code this} otherwise
* @throws QueryException query exception
*/
abstract TrieNode add(final Leaf o, final int lvl, final InputInfo ii)
throws QueryException;
/**
* Add an overflow list to this node, if the key isn't already used.
* @param o leaf to insert
* @param lvl level
* @param ii input info
* @return updated map if changed, {@code this} otherwise
* @throws QueryException query exception
*/
abstract TrieNode add(final List o, final int lvl, final InputInfo ii)
throws QueryException;
/**
* Add all bindings of the given branch to this node for which the key isn't
* already used.
* @param o leaf to insert
* @param lvl level
* @param ii input info
* @return updated map if changed, {@code this} otherwise
* @throws QueryException query exception
*/
abstract TrieNode add(final Branch o, final int lvl, final InputInfo ii)
throws QueryException;
/**
* Verifies the tree.
* @return check result
*/
abstract boolean verify();
/**
* Collects all keys in this subtree.
* @param ks key cache
*/
abstract void keys(final ItemCache ks);
/**
* Calculates the hash key for the given level.
* @param hash hash value
* @param lvl current level
* @return hash key
*/
static int key(final int hash, final int lvl) {
return hash >>> lvl * Map.BITS & MASK;
}
@Override
public String toString() {
return toString(new StringBuilder(), "").toString();
}
/**
* Recursive {@link #toString()} helper.
*
* @param sb string builder
* @param ind indentation string
* @return string builder for convenience
*/
abstract StringBuilder toString(final StringBuilder sb, final String ind);
/**
* Checks if the map has the specified key and value type.
* @param kt key type
* @param vt value type
* @return {@code true} if the type fits, {@code false} otherwise
*/
abstract boolean hasType(final AtomType kt, final SeqType vt);
/**
* Compares two values.
* @param a first value
* @param b second value
* @param ii input info
* @return {@code true} if both values are deep equal, {@code false} otherwise
* @throws QueryException query exception
*/
static boolean deep(final Value a, final Value b, final InputInfo ii)
throws QueryException {
return a.size() == b.size() && Compare.deep(a, b, ii);
}
/**
* Compares two items.
* @param a first item
* @param b second item
* @param ii input info
* @return {@code true} if both items are equal, {@code false} otherwise
* @throws QueryException query exception
*/
static boolean eq(final Item a, final Item b, final InputInfo ii)
throws QueryException {
return a.comparable(b) && a.eq(ii, b);
}
/**
* Calculates the hash code of this node.
* @param ii input info
* @return hash value
* @throws QueryException query exception
*/
abstract int hash(InputInfo ii) throws QueryException;
/**
* Checks if this node is indistinguishable from the given node.
* @param ii input info
* @param o other node
* @return result of check
* @throws QueryException query exception
*/
abstract boolean deep(final InputInfo ii, final TrieNode o)
throws QueryException;
/**
* Recursive helper for {@link Map#toString()}.
* @param sb string builder
* @return reference to {@code sb}
*/
abstract StringBuilder toString(final StringBuilder sb);
}