Package org.ardverk.collection

Source Code of org.ardverk.collection.AbstractPatriciaTrie$TrieEntry

/*
* Copyright 2005-2012 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.
*/

package org.ardverk.collection;

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.ardverk.collection.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<K, V>(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() {
    super();
  }
 
  public AbstractPatriciaTrie(KeyAnalyzer<? super K> keyAnalyzer) {
    super(keyAnalyzer);
  }
 
  public AbstractPatriciaTrie(Map<? extends K, ? extends V> m) {
    super();
    putAll(m);
  }
 
  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<K, V>(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;
     
      if (!isBitSet(entry.key, current.bitIndex)) {
        current = current.left;
      } else {
        current = 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.<K>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<Map.Entry<K,V>>();
    if (!selectR(root.left, -1, key, reference)) {
      return reference.get();
    }
    return null;
  }
 
  @Override
  public Map.Entry<K,V> select(K key, Cursor<? super K, ? super V> cursor) {
    Reference<Map.Entry<K, V>> reference
      = new Reference<Map.Entry<K,V>>();
    selectR(root.left, -1, key, cursor, reference);
    return reference.get();
  }

  /**
   * This is equivalent to the other {@link #selectR(TrieEntry, int,
   * Object, int, 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<K, V>(
                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<K, V>(
              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.<K>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.<K>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;
     
      if (!isBitSet(key, current.bitIndex)) {
        current = current.left;
      } else {
        current = 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;
      if (!isBitSet(key, current.bitIndex)) {
        current = current.left;
      } else {
        current = 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) {
    if (node == null) {
      return firstEntry();
    } else {
      return 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.
    if (isEmpty()) {
      return null;
    }
   
    return 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();
    }
   
    @Override
    public String toString() {
      StringBuilder buffer = new StringBuilder();
     
      if (bitIndex == -1) {
        buffer.append("RootEntry(");
      } else {
        buffer.append("Entry(");
      }
     
      buffer.append("key=").append(getKey()).append(" [").append(bitIndex).append("], ");
      buffer.append("value=").append(getValue()).append(", ");
      //buffer.append("bitIndex=").append(bitIndex).append(", ");
     
      if (parent != null) {
        if (parent.bitIndex == -1) {
          buffer.append("parent=").append("ROOT");
        } else {
          buffer.append("parent=").append(parent.getKey()).append(" [").append(parent.bitIndex).append("]");
        }
      } else {
        buffer.append("parent=").append("null");
      }
      buffer.append(", ");
     
      if (left != null) {
        if (left.bitIndex == -1) {
          buffer.append("left=").append("ROOT");
        } else {
          buffer.append("left=").append(left.getKey()).append(" [").append(left.bitIndex).append("]");
        }
      } else {
        buffer.append("left=").append("null");
      }
      buffer.append(", ");
     
      if (right != null) {
        if (right.bitIndex == -1) {
          buffer.append("right=").append("ROOT");
        } else {
          buffer.append("right=").append(right.getKey()).append(" [").append(right.bitIndex).append("]");
        }
      } else {
        buffer.append("right=").append("null");
      }
      buffer.append(", ");
     
      if (predecessor != null) {
        if(predecessor.bitIndex == -1) {
          buffer.append("predecessor=").append("ROOT");
        } else {
          buffer.append("predecessor=").append(predecessor.getKey()).append(" [").append(predecessor.bitIndex).append("]");
        }
      }
     
      buffer.append(")");
      return buffer.toString();
    }
  }
 

  /**
   * 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;
    }
  }
}
TOP

Related Classes of org.ardverk.collection.AbstractPatriciaTrie$TrieEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.