| /* |
| * Copyright 2005-2010 Roger Kapsi, Sam Berlin |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * This class is taken from https://github.com/rkapsi/patricia-trie (v0.6), and slightly modified |
| * to correspond to Cassandra code style, as the only Patricia Trie implementation, |
| * which supports pluggable key comparators (e.g. commons-collections PatriciaTrie (which is based |
| * on rkapsi/patricia-trie project) only supports String keys) |
| * but unfortunately is not deployed to the maven central as a downloadable artifact. |
| */ |
| |
| package org.apache.cassandra.index.sasi.utils.trie; |
| |
| import java.util.AbstractCollection; |
| import java.util.AbstractSet; |
| import java.util.Collection; |
| import java.util.ConcurrentModificationException; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| import org.apache.cassandra.index.sasi.utils.trie.Cursor.Decision; |
| |
| /** |
| * This class implements the base PATRICIA algorithm and everything that |
| * is related to the {@link Map} interface. |
| */ |
| abstract class AbstractPatriciaTrie<K, V> extends AbstractTrie<K, V> |
| { |
| private static final long serialVersionUID = -2303909182832019043L; |
| |
| /** |
| * The root node of the {@link Trie}. |
| */ |
| final TrieEntry<K, V> root = new TrieEntry<>(null, null, -1); |
| |
| /** |
| * Each of these fields are initialized to contain an instance of the |
| * appropriate view the first time this view is requested. The views are |
| * stateless, so there's no reason to create more than one of each. |
| */ |
| private transient volatile Set<K> keySet; |
| private transient volatile Collection<V> values; |
| private transient volatile Set<Map.Entry<K,V>> entrySet; |
| |
| /** |
| * The current size of the {@link Trie} |
| */ |
| private int size = 0; |
| |
| /** |
| * The number of times this {@link Trie} has been modified. |
| * It's used to detect concurrent modifications and fail-fast |
| * the {@link Iterator}s. |
| */ |
| transient int modCount = 0; |
| |
| public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer) |
| { |
| super(keyAnalyzer); |
| } |
| |
| public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer, Map<? extends K, ? extends V> m) |
| { |
| super(keyAnalyzer); |
| putAll(m); |
| } |
| |
| @Override |
| public void clear() |
| { |
| root.key = null; |
| root.bitIndex = -1; |
| root.value = null; |
| |
| root.parent = null; |
| root.left = root; |
| root.right = null; |
| root.predecessor = root; |
| |
| size = 0; |
| incrementModCount(); |
| } |
| |
| @Override |
| public int size() |
| { |
| return size; |
| } |
| |
| /** |
| * A helper method to increment the {@link Trie} size |
| * and the modification counter. |
| */ |
| void incrementSize() |
| { |
| size++; |
| incrementModCount(); |
| } |
| |
| /** |
| * A helper method to decrement the {@link Trie} size |
| * and increment the modification counter. |
| */ |
| void decrementSize() |
| { |
| size--; |
| incrementModCount(); |
| } |
| |
| /** |
| * A helper method to increment the modification counter. |
| */ |
| private void incrementModCount() |
| { |
| ++modCount; |
| } |
| |
| @Override |
| public V put(K key, V value) |
| { |
| if (key == null) |
| throw new NullPointerException("Key cannot be null"); |
| |
| int lengthInBits = lengthInBits(key); |
| |
| // The only place to store a key with a length |
| // of zero bits is the root node |
| if (lengthInBits == 0) |
| { |
| if (root.isEmpty()) |
| incrementSize(); |
| else |
| incrementModCount(); |
| |
| return root.setKeyValue(key, value); |
| } |
| |
| TrieEntry<K, V> found = getNearestEntryForKey(key); |
| if (compareKeys(key, found.key)) |
| { |
| if (found.isEmpty()) // <- must be the root |
| incrementSize(); |
| else |
| incrementModCount(); |
| |
| return found.setKeyValue(key, value); |
| } |
| |
| int bitIndex = bitIndex(key, found.key); |
| if (!Tries.isOutOfBoundsIndex(bitIndex)) |
| { |
| if (Tries.isValidBitIndex(bitIndex)) // in 99.999...9% the case |
| { |
| /* NEW KEY+VALUE TUPLE */ |
| TrieEntry<K, V> t = new TrieEntry<>(key, value, bitIndex); |
| addEntry(t); |
| incrementSize(); |
| return null; |
| } |
| else if (Tries.isNullBitKey(bitIndex)) |
| { |
| // A bits of the Key are zero. The only place to |
| // store such a Key is the root Node! |
| |
| /* NULL BIT KEY */ |
| if (root.isEmpty()) |
| incrementSize(); |
| else |
| incrementModCount(); |
| |
| return root.setKeyValue(key, value); |
| |
| } |
| else if (Tries.isEqualBitKey(bitIndex)) |
| { |
| // This is a very special and rare case. |
| |
| /* REPLACE OLD KEY+VALUE */ |
| if (found != root) |
| { |
| incrementModCount(); |
| return found.setKeyValue(key, value); |
| } |
| } |
| } |
| |
| throw new IndexOutOfBoundsException("Failed to put: " |
| + key + " -> " + value + ", " + bitIndex); |
| } |
| |
| /** |
| * Adds the given {@link TrieEntry} to the {@link Trie} |
| */ |
| TrieEntry<K, V> addEntry(TrieEntry<K, V> entry) |
| { |
| TrieEntry<K, V> current = root.left; |
| TrieEntry<K, V> path = root; |
| |
| while(true) |
| { |
| if (current.bitIndex >= entry.bitIndex || current.bitIndex <= path.bitIndex) |
| { |
| entry.predecessor = entry; |
| |
| if (!isBitSet(entry.key, entry.bitIndex)) |
| { |
| entry.left = entry; |
| entry.right = current; |
| } |
| else |
| { |
| entry.left = current; |
| entry.right = entry; |
| } |
| |
| entry.parent = path; |
| if (current.bitIndex >= entry.bitIndex) |
| current.parent = entry; |
| |
| // if we inserted an uplink, set the predecessor on it |
| if (current.bitIndex <= path.bitIndex) |
| current.predecessor = entry; |
| |
| if (path == root || !isBitSet(entry.key, path.bitIndex)) |
| path.left = entry; |
| else |
| path.right = entry; |
| |
| return entry; |
| } |
| |
| path = current; |
| |
| current = !isBitSet(entry.key, current.bitIndex) |
| ? current.left : current.right; |
| } |
| } |
| |
| @Override |
| public V get(Object k) |
| { |
| TrieEntry<K, V> entry = getEntry(k); |
| return entry != null ? entry.getValue() : null; |
| } |
| |
| /** |
| * Returns the entry associated with the specified key in the |
| * AbstractPatriciaTrie. Returns null if the map contains no mapping |
| * for this key. |
| * |
| * This may throw ClassCastException if the object is not of type K. |
| */ |
| TrieEntry<K,V> getEntry(Object k) |
| { |
| K key = Tries.cast(k); |
| if (key == null) |
| return null; |
| |
| TrieEntry<K,V> entry = getNearestEntryForKey(key); |
| return !entry.isEmpty() && compareKeys(key, entry.key) ? entry : null; |
| } |
| |
| @Override |
| public Map.Entry<K, V> select(K key) |
| { |
| Reference<Map.Entry<K, V>> reference = new Reference<>(); |
| return !selectR(root.left, -1, key, reference) ? reference.get() : null; |
| } |
| |
| @Override |
| public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor) |
| { |
| Reference<Map.Entry<K, V>> reference = new Reference<>(); |
| selectR(root.left, -1, key, cursor, reference); |
| return reference.get(); |
| } |
| |
| /** |
| * This is equivalent to the other {@link #selectR(TrieEntry, int, |
| * K, Cursor, Reference)} method but without its overhead |
| * because we're selecting only one best matching Entry from the |
| * {@link Trie}. |
| */ |
| private boolean selectR(TrieEntry<K, V> h, int bitIndex, final K key, final Reference<Map.Entry<K, V>> reference) |
| { |
| if (h.bitIndex <= bitIndex) |
| { |
| // If we hit the root Node and it is empty |
| // we have to look for an alternative best |
| // matching node. |
| if (!h.isEmpty()) |
| { |
| reference.set(h); |
| return false; |
| } |
| return true; |
| } |
| |
| if (!isBitSet(key, h.bitIndex)) |
| { |
| if (selectR(h.left, h.bitIndex, key, reference)) |
| { |
| return selectR(h.right, h.bitIndex, key, reference); |
| } |
| } |
| else |
| { |
| if (selectR(h.right, h.bitIndex, key, reference)) |
| { |
| return selectR(h.left, h.bitIndex, key, reference); |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * |
| */ |
| private boolean selectR(TrieEntry<K,V> h, int bitIndex, |
| final K key, final Cursor<? super K, ? super V> cursor, |
| final Reference<Map.Entry<K, V>> reference) |
| { |
| if (h.bitIndex <= bitIndex) |
| { |
| if (!h.isEmpty()) |
| { |
| Decision decision = cursor.select(h); |
| switch(decision) |
| { |
| case REMOVE: |
| throw new UnsupportedOperationException("Cannot remove during select"); |
| |
| case EXIT: |
| reference.set(h); |
| return false; // exit |
| |
| case REMOVE_AND_EXIT: |
| TrieEntry<K, V> entry = new TrieEntry<>(h.getKey(), h.getValue(), -1); |
| reference.set(entry); |
| removeEntry(h); |
| return false; |
| |
| case CONTINUE: |
| // fall through. |
| } |
| } |
| |
| return true; // continue |
| } |
| |
| if (!isBitSet(key, h.bitIndex)) |
| { |
| if (selectR(h.left, h.bitIndex, key, cursor, reference)) |
| { |
| return selectR(h.right, h.bitIndex, key, cursor, reference); |
| } |
| } |
| else |
| { |
| if (selectR(h.right, h.bitIndex, key, cursor, reference)) |
| { |
| return selectR(h.left, h.bitIndex, key, cursor, reference); |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public Map.Entry<K, V> traverse(Cursor<? super K, ? super V> cursor) |
| { |
| TrieEntry<K, V> entry = nextEntry(null); |
| while (entry != null) |
| { |
| TrieEntry<K, V> current = entry; |
| |
| Decision decision = cursor.select(current); |
| entry = nextEntry(current); |
| |
| switch(decision) |
| { |
| case EXIT: |
| return current; |
| |
| case REMOVE: |
| removeEntry(current); |
| break; // out of switch, stay in while loop |
| |
| case REMOVE_AND_EXIT: |
| Map.Entry<K, V> value = new TrieEntry<>(current.getKey(), current.getValue(), -1); |
| removeEntry(current); |
| return value; |
| |
| case CONTINUE: // do nothing. |
| } |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public boolean containsKey(Object k) |
| { |
| if (k == null) |
| return false; |
| |
| K key = Tries.cast(k); |
| TrieEntry<K, V> entry = getNearestEntryForKey(key); |
| return !entry.isEmpty() && compareKeys(key, entry.key); |
| } |
| |
| @Override |
| public Set<Map.Entry<K,V>> entrySet() |
| { |
| if (entrySet == null) |
| entrySet = new EntrySet(); |
| |
| return entrySet; |
| } |
| |
| @Override |
| public Set<K> keySet() |
| { |
| if (keySet == null) |
| keySet = new KeySet(); |
| return keySet; |
| } |
| |
| @Override |
| public Collection<V> values() |
| { |
| if (values == null) |
| values = new Values(); |
| return values; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @throws ClassCastException if provided key is of an incompatible type |
| */ |
| @Override |
| public V remove(Object k) |
| { |
| if (k == null) |
| return null; |
| |
| K key = Tries.cast(k); |
| TrieEntry<K, V> current = root.left; |
| TrieEntry<K, V> path = root; |
| while (true) |
| { |
| if (current.bitIndex <= path.bitIndex) |
| { |
| if (!current.isEmpty() && compareKeys(key, current.key)) |
| { |
| return removeEntry(current); |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| path = current; |
| current = !isBitSet(key, current.bitIndex) ? current.left : current.right; |
| } |
| } |
| |
| /** |
| * Returns the nearest entry for a given key. This is useful |
| * for finding knowing if a given key exists (and finding the value |
| * for it), or for inserting the key. |
| * |
| * The actual get implementation. This is very similar to |
| * selectR but with the exception that it might return the |
| * root Entry even if it's empty. |
| */ |
| TrieEntry<K, V> getNearestEntryForKey(K key) |
| { |
| TrieEntry<K, V> current = root.left; |
| TrieEntry<K, V> path = root; |
| |
| while(true) |
| { |
| if (current.bitIndex <= path.bitIndex) |
| return current; |
| |
| path = current; |
| current = !isBitSet(key, current.bitIndex) ? current.left : current.right; |
| } |
| } |
| |
| /** |
| * Removes a single entry from the {@link Trie}. |
| * |
| * If we found a Key (Entry h) then figure out if it's |
| * an internal (hard to remove) or external Entry (easy |
| * to remove) |
| */ |
| V removeEntry(TrieEntry<K, V> h) |
| { |
| if (h != root) |
| { |
| if (h.isInternalNode()) |
| { |
| removeInternalEntry(h); |
| } |
| else |
| { |
| removeExternalEntry(h); |
| } |
| } |
| |
| decrementSize(); |
| return h.setKeyValue(null, null); |
| } |
| |
| /** |
| * Removes an external entry from the {@link Trie}. |
| * |
| * If it's an external Entry then just remove it. |
| * This is very easy and straight forward. |
| */ |
| private void removeExternalEntry(TrieEntry<K, V> h) |
| { |
| if (h == root) |
| { |
| throw new IllegalArgumentException("Cannot delete root Entry!"); |
| } |
| else if (!h.isExternalNode()) |
| { |
| throw new IllegalArgumentException(h + " is not an external Entry!"); |
| } |
| |
| TrieEntry<K, V> parent = h.parent; |
| TrieEntry<K, V> child = (h.left == h) ? h.right : h.left; |
| |
| if (parent.left == h) |
| { |
| parent.left = child; |
| } |
| else |
| { |
| parent.right = child; |
| } |
| |
| // either the parent is changing, or the predecessor is changing. |
| if (child.bitIndex > parent.bitIndex) |
| { |
| child.parent = parent; |
| } |
| else |
| { |
| child.predecessor = parent; |
| } |
| |
| } |
| |
| /** |
| * Removes an internal entry from the {@link Trie}. |
| * |
| * If it's an internal Entry then "good luck" with understanding |
| * this code. The Idea is essentially that Entry p takes Entry h's |
| * place in the trie which requires some re-wiring. |
| */ |
| private void removeInternalEntry(TrieEntry<K, V> h) |
| { |
| if (h == root) |
| { |
| throw new IllegalArgumentException("Cannot delete root Entry!"); |
| } |
| else if (!h.isInternalNode()) |
| { |
| throw new IllegalArgumentException(h + " is not an internal Entry!"); |
| } |
| |
| TrieEntry<K, V> p = h.predecessor; |
| |
| // Set P's bitIndex |
| p.bitIndex = h.bitIndex; |
| |
| // Fix P's parent, predecessor and child Nodes |
| { |
| TrieEntry<K, V> parent = p.parent; |
| TrieEntry<K, V> child = (p.left == h) ? p.right : p.left; |
| |
| // if it was looping to itself previously, |
| // it will now be pointed from it's parent |
| // (if we aren't removing it's parent -- |
| // in that case, it remains looping to itself). |
| // otherwise, it will continue to have the same |
| // predecessor. |
| if (p.predecessor == p && p.parent != h) |
| p.predecessor = p.parent; |
| |
| if (parent.left == p) |
| { |
| parent.left = child; |
| } |
| else |
| { |
| parent.right = child; |
| } |
| |
| if (child.bitIndex > parent.bitIndex) |
| { |
| child.parent = parent; |
| } |
| } |
| |
| // Fix H's parent and child Nodes |
| { |
| // If H is a parent of its left and right child |
| // then change them to P |
| if (h.left.parent == h) |
| h.left.parent = p; |
| |
| if (h.right.parent == h) |
| h.right.parent = p; |
| |
| // Change H's parent |
| if (h.parent.left == h) |
| { |
| h.parent.left = p; |
| } |
| else |
| { |
| h.parent.right = p; |
| } |
| } |
| |
| // Copy the remaining fields from H to P |
| //p.bitIndex = h.bitIndex; |
| p.parent = h.parent; |
| p.left = h.left; |
| p.right = h.right; |
| |
| // Make sure that if h was pointing to any uplinks, |
| // p now points to them. |
| if (isValidUplink(p.left, p)) |
| p.left.predecessor = p; |
| |
| if (isValidUplink(p.right, p)) |
| p.right.predecessor = p; |
| } |
| |
| /** |
| * Returns the entry lexicographically after the given entry. |
| * If the given entry is null, returns the first node. |
| */ |
| TrieEntry<K, V> nextEntry(TrieEntry<K, V> node) |
| { |
| return (node == null) ? firstEntry() : nextEntryImpl(node.predecessor, node, null); |
| } |
| |
| /** |
| * Scans for the next node, starting at the specified point, and using 'previous' |
| * as a hint that the last node we returned was 'previous' (so we know not to return |
| * it again). If 'tree' is non-null, this will limit the search to the given tree. |
| * |
| * The basic premise is that each iteration can follow the following steps: |
| * |
| * 1) Scan all the way to the left. |
| * a) If we already started from this node last time, proceed to Step 2. |
| * b) If a valid uplink is found, use it. |
| * c) If the result is an empty node (root not set), break the scan. |
| * d) If we already returned the left node, break the scan. |
| * |
| * 2) Check the right. |
| * a) If we already returned the right node, proceed to Step 3. |
| * b) If it is a valid uplink, use it. |
| * c) Do Step 1 from the right node. |
| * |
| * 3) Back up through the parents until we encounter find a parent |
| * that we're not the right child of. |
| * |
| * 4) If there's no right child of that parent, the iteration is finished. |
| * Otherwise continue to Step 5. |
| * |
| * 5) Check to see if the right child is a valid uplink. |
| * a) If we already returned that child, proceed to Step 6. |
| * Otherwise, use it. |
| * |
| * 6) If the right child of the parent is the parent itself, we've |
| * already found & returned the end of the Trie, so exit. |
| * |
| * 7) Do Step 1 on the parent's right child. |
| */ |
| TrieEntry<K, V> nextEntryImpl(TrieEntry<K, V> start, TrieEntry<K, V> previous, TrieEntry<K, V> tree) |
| { |
| TrieEntry<K, V> current = start; |
| |
| // Only look at the left if this was a recursive or |
| // the first check, otherwise we know we've already looked |
| // at the left. |
| if (previous == null || start != previous.predecessor) |
| { |
| while (!current.left.isEmpty()) |
| { |
| // stop traversing if we've already |
| // returned the left of this node. |
| if (previous == current.left) |
| break; |
| |
| if (isValidUplink(current.left, current)) |
| return current.left; |
| |
| current = current.left; |
| } |
| } |
| |
| // If there's no data at all, exit. |
| if (current.isEmpty()) |
| return null; |
| |
| // If we've already returned the left, |
| // and the immediate right is null, |
| // there's only one entry in the Trie |
| // which is stored at the root. |
| // |
| // / ("") <-- root |
| // \_/ \ |
| // null <-- 'current' |
| // |
| if (current.right == null) |
| return null; |
| |
| // If nothing valid on the left, try the right. |
| if (previous != current.right) |
| { |
| // See if it immediately is valid. |
| if (isValidUplink(current.right, current)) |
| return current.right; |
| |
| // Must search on the right's side if it wasn't initially valid. |
| return nextEntryImpl(current.right, previous, tree); |
| } |
| |
| // Neither left nor right are valid, find the first parent |
| // whose child did not come from the right & traverse it. |
| while (current == current.parent.right) |
| { |
| // If we're going to traverse to above the subtree, stop. |
| if (current == tree) |
| return null; |
| |
| current = current.parent; |
| } |
| |
| // If we're on the top of the subtree, we can't go any higher. |
| if (current == tree) |
| return null; |
| |
| // If there's no right, the parent must be root, so we're done. |
| if (current.parent.right == null) |
| return null; |
| |
| // If the parent's right points to itself, we've found one. |
| if (previous != current.parent.right && isValidUplink(current.parent.right, current.parent)) |
| return current.parent.right; |
| |
| // If the parent's right is itself, there can't be any more nodes. |
| if (current.parent.right == current.parent) |
| return null; |
| |
| // We need to traverse down the parent's right's path. |
| return nextEntryImpl(current.parent.right, previous, tree); |
| } |
| |
| /** |
| * Returns the first entry the {@link Trie} is storing. |
| * |
| * This is implemented by going always to the left until |
| * we encounter a valid uplink. That uplink is the first key. |
| */ |
| TrieEntry<K, V> firstEntry() |
| { |
| // if Trie is empty, no first node. |
| return isEmpty() ? null : followLeft(root); |
| } |
| |
| /** |
| * Goes left through the tree until it finds a valid node. |
| */ |
| TrieEntry<K, V> followLeft(TrieEntry<K, V> node) |
| { |
| while(true) |
| { |
| TrieEntry<K, V> child = node.left; |
| // if we hit root and it didn't have a node, go right instead. |
| if (child.isEmpty()) |
| child = node.right; |
| |
| if (child.bitIndex <= node.bitIndex) |
| return child; |
| |
| node = child; |
| } |
| } |
| |
| /** |
| * Returns true if 'next' is a valid uplink coming from 'from'. |
| */ |
| static boolean isValidUplink(TrieEntry<?, ?> next, TrieEntry<?, ?> from) |
| { |
| return next != null && next.bitIndex <= from.bitIndex && !next.isEmpty(); |
| } |
| |
| /** |
| * A {@link Reference} allows us to return something through a Method's |
| * argument list. An alternative would be to an Array with a length of |
| * one (1) but that leads to compiler warnings. Computationally and memory |
| * wise there's no difference (except for the need to load the |
| * {@link Reference} Class but that happens only once). |
| */ |
| private static class Reference<E> |
| { |
| |
| private E item; |
| |
| public void set(E item) |
| { |
| this.item = item; |
| } |
| |
| public E get() |
| { |
| return item; |
| } |
| } |
| |
| /** |
| * A {@link Trie} is a set of {@link TrieEntry} nodes |
| */ |
| static class TrieEntry<K,V> extends BasicEntry<K, V> |
| { |
| |
| private static final long serialVersionUID = 4596023148184140013L; |
| |
| /** The index this entry is comparing. */ |
| protected int bitIndex; |
| |
| /** The parent of this entry. */ |
| protected TrieEntry<K,V> parent; |
| |
| /** The left child of this entry. */ |
| protected TrieEntry<K,V> left; |
| |
| /** The right child of this entry. */ |
| protected TrieEntry<K,V> right; |
| |
| /** The entry who uplinks to this entry. */ |
| protected TrieEntry<K,V> predecessor; |
| |
| public TrieEntry(K key, V value, int bitIndex) |
| { |
| super(key, value); |
| |
| this.bitIndex = bitIndex; |
| |
| this.parent = null; |
| this.left = this; |
| this.right = null; |
| this.predecessor = this; |
| } |
| |
| /** |
| * Whether or not the entry is storing a key. |
| * Only the root can potentially be empty, all other |
| * nodes must have a key. |
| */ |
| public boolean isEmpty() |
| { |
| return key == null; |
| } |
| |
| /** |
| * Neither the left nor right child is a loopback |
| */ |
| public boolean isInternalNode() |
| { |
| return left != this && right != this; |
| } |
| |
| /** |
| * Either the left or right child is a loopback |
| */ |
| public boolean isExternalNode() |
| { |
| return !isInternalNode(); |
| } |
| } |
| |
| |
| /** |
| * This is a entry set view of the {@link Trie} as returned |
| * by {@link Map#entrySet()} |
| */ |
| private class EntrySet extends AbstractSet<Map.Entry<K,V>> |
| { |
| @Override |
| public Iterator<Map.Entry<K,V>> iterator() |
| { |
| return new EntryIterator(); |
| } |
| |
| @Override |
| public boolean contains(Object o) |
| { |
| if (!(o instanceof Map.Entry)) |
| return false; |
| |
| TrieEntry<K,V> candidate = getEntry(((Map.Entry<?, ?>)o).getKey()); |
| return candidate != null && candidate.equals(o); |
| } |
| |
| @Override |
| public boolean remove(Object o) |
| { |
| int size = size(); |
| AbstractPatriciaTrie.this.remove(o); |
| return size != size(); |
| } |
| |
| @Override |
| public int size() |
| { |
| return AbstractPatriciaTrie.this.size(); |
| } |
| |
| @Override |
| public void clear() |
| { |
| AbstractPatriciaTrie.this.clear(); |
| } |
| |
| /** |
| * An {@link Iterator} that returns {@link Entry} Objects |
| */ |
| private class EntryIterator extends TrieIterator<Map.Entry<K,V>> |
| { |
| @Override |
| public Map.Entry<K,V> next() |
| { |
| return nextEntry(); |
| } |
| } |
| } |
| |
| /** |
| * This is a key set view of the {@link Trie} as returned |
| * by {@link Map#keySet()} |
| */ |
| private class KeySet extends AbstractSet<K> |
| { |
| @Override |
| public Iterator<K> iterator() |
| { |
| return new KeyIterator(); |
| } |
| |
| @Override |
| public int size() |
| { |
| return AbstractPatriciaTrie.this.size(); |
| } |
| |
| @Override |
| public boolean contains(Object o) |
| { |
| return containsKey(o); |
| } |
| |
| @Override |
| public boolean remove(Object o) |
| { |
| int size = size(); |
| AbstractPatriciaTrie.this.remove(o); |
| return size != size(); |
| } |
| |
| @Override |
| public void clear() |
| { |
| AbstractPatriciaTrie.this.clear(); |
| } |
| |
| /** |
| * An {@link Iterator} that returns Key Objects |
| */ |
| private class KeyIterator extends TrieIterator<K> |
| { |
| @Override |
| public K next() |
| { |
| return nextEntry().getKey(); |
| } |
| } |
| } |
| |
| /** |
| * This is a value view of the {@link Trie} as returned |
| * by {@link Map#values()} |
| */ |
| private class Values extends AbstractCollection<V> |
| { |
| @Override |
| public Iterator<V> iterator() |
| { |
| return new ValueIterator(); |
| } |
| |
| @Override |
| public int size() |
| { |
| return AbstractPatriciaTrie.this.size(); |
| } |
| |
| @Override |
| public boolean contains(Object o) |
| { |
| return containsValue(o); |
| } |
| |
| @Override |
| public void clear() |
| { |
| AbstractPatriciaTrie.this.clear(); |
| } |
| |
| @Override |
| public boolean remove(Object o) |
| { |
| for (Iterator<V> it = iterator(); it.hasNext(); ) |
| { |
| V value = it.next(); |
| if (Tries.areEqual(value, o)) |
| { |
| it.remove(); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * An {@link Iterator} that returns Value Objects |
| */ |
| private class ValueIterator extends TrieIterator<V> |
| { |
| @Override |
| public V next() |
| { |
| return nextEntry().getValue(); |
| } |
| } |
| } |
| |
| /** |
| * An iterator for the entries. |
| */ |
| abstract class TrieIterator<E> implements Iterator<E> |
| { |
| /** |
| * For fast-fail |
| */ |
| protected int expectedModCount = AbstractPatriciaTrie.this.modCount; |
| |
| protected TrieEntry<K, V> next; // the next node to return |
| protected TrieEntry<K, V> current; // the current entry we're on |
| |
| /** |
| * Starts iteration from the root |
| */ |
| protected TrieIterator() |
| { |
| next = AbstractPatriciaTrie.this.nextEntry(null); |
| } |
| |
| /** |
| * Starts iteration at the given entry |
| */ |
| protected TrieIterator(TrieEntry<K, V> firstEntry) |
| { |
| next = firstEntry; |
| } |
| |
| /** |
| * Returns the next {@link TrieEntry} |
| */ |
| protected TrieEntry<K,V> nextEntry() |
| { |
| if (expectedModCount != AbstractPatriciaTrie.this.modCount) |
| throw new ConcurrentModificationException(); |
| |
| TrieEntry<K,V> e = next; |
| if (e == null) |
| throw new NoSuchElementException(); |
| |
| next = findNext(e); |
| current = e; |
| return e; |
| } |
| |
| /** |
| * @see PatriciaTrie#nextEntry(TrieEntry) |
| */ |
| protected TrieEntry<K, V> findNext(TrieEntry<K, V> prior) |
| { |
| return AbstractPatriciaTrie.this.nextEntry(prior); |
| } |
| |
| @Override |
| public boolean hasNext() |
| { |
| return next != null; |
| } |
| |
| @Override |
| public void remove() |
| { |
| if (current == null) |
| throw new IllegalStateException(); |
| |
| if (expectedModCount != AbstractPatriciaTrie.this.modCount) |
| throw new ConcurrentModificationException(); |
| |
| TrieEntry<K, V> node = current; |
| current = null; |
| AbstractPatriciaTrie.this.removeEntry(node); |
| |
| expectedModCount = AbstractPatriciaTrie.this.modCount; |
| } |
| } |
| } |