View Javadoc

1   /*
2    * $Id: TernarySearchTreeMap.java 211 2010-11-22 19:21:26Z roland $
3    * Copyright (C) 2007 Roland Krueger
4    * Created on 15.10.2003
5    *
6    * Author: Roland Krueger (www.rolandkrueger.info)
7    *
8    * This file is part of RoKlib.
9    *
10   * This library is free software; you can redistribute it and/or
11   * modify it under the terms of the GNU Lesser General Public License
12   * as published by the Free Software Foundation; either version 2.1 of
13   * the License, or (at your option) any later version.
14   *
15   * This library is distributed in the hope that it will be useful, but
16   * WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   * Lesser General Public License for more details.
19   *
20   * You should have received a copy of the GNU Lesser General Public
21   * License along with this library; if not, write to the Free Software
22   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23   * USA
24   */
25  package info.rolandkrueger.roklib.util;
26  
27  import info.rolandkrueger.roklib.util.helper.CheckForNull;
28  
29  import java.io.Serializable;
30  import java.util.AbstractCollection;
31  import java.util.AbstractMap;
32  import java.util.AbstractSet;
33  import java.util.Collection;
34  import java.util.Comparator;
35  import java.util.Iterator;
36  import java.util.Map;
37  import java.util.NoSuchElementException;
38  import java.util.Set;
39  import java.util.SortedMap;
40  import java.util.SortedSet;
41  import java.util.Stack;
42  import java.util.TreeSet;
43  
44  /*
45   * TODO: 
46   * - hashCode() fuer TST Map.Entry schreiben 
47   * - TSTEntrySet.contains optimieren: TSTM.indexOf verwenden 
48   * - in Beschreibung: Map darf keine null-keys enthalten 
49   * - anmerken, dass prefix matching und spellchecking nur mit den Keys moeglich ist. 
50   * - update documentation
51   * - matchAlmost() is not thread-safe/iterator is not thread-safe
52   * - matchAlmost() should have the same return type as getPrefixSubtreeIterator()
53   * - check for concurrent modifications while an iterator is active
54   * - write a subtree iterator, which returns all entries which are NOT matching a 
55   *   particular prefix, the same for match almost
56   */
57  
58  /**
59   * This class implements a ternary search tree that can be used to store and
60   * access a large amount of data efficiently and with low memory requirements.<BR>
61   * <BR>
62   * The search tree's implementation is based on Wally Flint's article on Ternary
63   * Search Trees on the Javaworld webpage. The article can be found <a
64   * href="http://www.javaworld.com/javaworld/jw-02-2001/jw-0216-ternary.html"
65   * target="_blank">here</a>. The code of method <code>get</code> is adapted from
66   * a code example in that article.<BR>
67   * <BR>
68   * This class can be used to store either key/value mappings or simple string
69   * values. Note that in the first case the key's string representation is used
70   * as the actual key. After a key/value pair has been stored in the tree the key
71   * object's data except for its string representation is no longer known to the
72   * data structure. So it is not possible to restore the key object from the
73   * search tree thereafter.<BR>
74   * <BR>
75   * If the search tree is used to store values without an assigned key the
76   * single-argument version of <code>put</code> can be used. Note that as above
77   * only the value's string representation is preserved within the data
78   * structure.<BR>
79   * <BR>
80   * The search tree's iterator is best used if single values are stored in the
81   * data structure. It returns the tree's data elements in sorted order. If an
82   * iterator is applied with the data structure handling key/value pairs, only
83   * the values are returned sorted by their keys.
84   * 
85   * @author Roland Krueger
86   * @version CVS $Id: TernarySearchTreeMap.java 211 2010-11-22 19:21:26Z roland $
87   */
88  public class TernarySearchTreeMap<V> extends AbstractMap<CharSequence, V> implements Serializable,
89      ITernarySearchTreeMap<V>
90  {
91    private static final long serialVersionUID = 8532235443989332299L;
92  
93    private enum NodeType implements Serializable
94    {
95      NONE, LOKID, EQKID, HIKID
96    };
97  
98    private TSTNode<V> mRootNode;
99    private TreeSet<CharSequence> mMatchingKeys; // needed for method
100                                                // matchAlmost()
101   private int mLengthTolerance; // needed as a global variable for
102                                 // matchAlmost()
103   private Comparator<? super CharSequence> mComparator;
104   private boolean mContainsEmptyStringKey = false;
105   private V mEmptyStringKeyValue = null;
106 
107   // statistical data
108   private int mNodeCount;
109 
110   /**
111    * Private default constructor.
112    */
113   public TernarySearchTreeMap ()
114   {
115     mRootNode = new TSTNode<V> ();
116   }
117 
118   public TernarySearchTreeMap (Map<CharSequence, V> map)
119   {
120     this ();
121     putAll (map);
122   }
123 
124   public TernarySearchTreeMap (SortedMap<CharSequence, V> map)
125   {
126     this ();
127     // FIXME:
128     mComparator = map.comparator ();
129     putAll (map);
130   }
131 
132   public String getMapStructureAsString ()
133   {
134     return mRootNode.toString ();
135   }
136 
137   public void clear ()
138   {
139     mRootNode = new TSTNode<V> ();
140     mContainsEmptyStringKey = false;
141     mEmptyStringKeyValue = null;
142   }
143 
144   public boolean containsKey (Object key)
145   {
146     return get (key) != null;
147   }
148 
149   public boolean containsValue (Object value)
150   {
151     if (mContainsEmptyStringKey)
152     {
153       if (value == null && mEmptyStringKeyValue == null) return true;
154       if (mEmptyStringKeyValue != null && mEmptyStringKeyValue.equals (value)) return true;
155     }
156     for (Map.Entry<CharSequence, V> entry : entrySet ())
157     {
158       if (entry.getValue () == null && value == null) return true;
159       if (entry.getValue ().equals (value)) return true;
160     }
161     return false;
162   }
163 
164   public boolean isEmpty ()
165   {
166     return (size () == 0);
167   }
168   
169   private Iterator<Entry<CharSequence, V>> getIterator (CharSequence fromKey, CharSequence toKey)
170   {
171     return new TSTIterator (fromKey, toKey);
172   }
173 
174   public Set<CharSequence> keySet ()
175   {
176     return new TSTKeySet ();
177   }
178 
179   @SuppressWarnings ("unchecked")
180   private int compare (CharSequence firstKey, CharSequence secondKey)
181   {
182     return (mComparator == null ? ((Comparable<CharSequence>) firstKey).compareTo (secondKey)
183         : mComparator.compare (firstKey, secondKey));
184   }
185 
186   public void putAll (Map<? extends CharSequence, ? extends V> t)
187   {
188     for (CharSequence element : t.keySet ())
189     {
190       put (element, t.get (element));
191     }
192   }
193 
194   public V remove (Object key)
195   {
196     try
197     {
198       String keyString = ((CharSequence) key).toString ();
199       V oldValue;
200       if (keyString.equals ("") && mContainsEmptyStringKey)
201       {
202         mContainsEmptyStringKey = false;
203         oldValue = mEmptyStringKeyValue;
204         mEmptyStringKeyValue = null;
205         return oldValue;
206       }
207       Stack<TSTStackNode<V>> stack = new Stack<TSTStackNode<V>> ();
208       TSTNode<V> currentNode = mRootNode;
209       TSTStackNode<V> currentStackNode = null;
210       int charIndex = 0;
211       int keyStringLength = keyString.length ();
212 
213       while (true)
214       {
215         if (currentNode == null)
216         {
217           return null; // return null if key is not stored in map
218         } else
219         {          
220           currentStackNode = new TSTStackNode<V> (currentNode); 
221         }
222         stack.push (currentStackNode);
223         char splitChar = currentNode.mSplitChar;
224         char keyChar = keyString.charAt (charIndex);
225 
226         if (keyChar == splitChar)
227         {
228           charIndex++;
229           if ((charIndex == keyStringLength) && (currentNode.mData != null))
230           {
231             // we've reached the node that stores a value
232             oldValue = currentNode.mData;
233             currentNode.mData = null;
234             while ((currentNode.mLokid == null) && (currentNode.mEqkid == null)
235                 && (currentNode.mHikid == null))
236             {
237               // delete current node as long as it has no children
238               if ((stack.size () == 0) && (mRootNode.mData != null))
239               {
240                 // We've reached the root node. The map contains has only
241                 // one element left residing in the root.
242                 return oldValue;
243               } else if (stack.size () == 0)
244               {
245                 mRootNode.mSplitChar = '\0';
246                 return oldValue;
247               }
248               currentNode = stack.pop ().mNode; // get parent node
249               currentNode.mSubarrayLength--; // decrease the size of the node's subarray.
250               charIndex--; // take previous character for comparison
251 
252               // we've completely erased all of key's characters from the tree.
253               // and delete the correct child node
254               if (charIndex < 0) break;
255               if (keyChar == splitChar
256                   && currentNode.mEqkid != null && currentNode.mEqkid.mSubarrayLength == 0)
257                 currentNode.mEqkid = null;
258               else if (keyChar < splitChar
259                   && currentNode.mLokid != null && currentNode.mLokid.mSubarrayLength == 0)
260                 currentNode.mLokid = null;
261               else if (currentNode.mHikid != null && currentNode.mHikid.mSubarrayLength == 0)
262                 currentNode.mHikid = null;
263             }
264             while (stack.size () > 0)
265             {
266               currentNode = stack.pop ().mNode;
267               currentNode.mSubarrayLength--; // decrease the size of the node's
268               // subarray.
269             }
270             return oldValue;
271           } else
272           {
273             currentNode = currentNode.mEqkid;
274           }
275         } else if (keyChar < splitChar)
276         {
277           currentNode = currentNode.mLokid;
278         } else
279         {
280           currentNode = currentNode.mHikid;
281         }
282       }
283     } finally
284     {
285       if (isEmpty ())
286       {
287         clear ();
288       }
289     }
290   }
291 
292   public Collection<V> values ()
293   {
294     return new TSTValuesCollection ();
295   }
296 
297   public Comparator<? super CharSequence> comparator ()
298   {
299     return mComparator;
300   }
301 
302   public CharSequence firstKey ()
303   {
304     return getKeyAt (0);
305   }
306 
307   public CharSequence lastKey ()
308   {
309     return getKeyAt (size () - 1);
310   }
311 
312   public SortedMap<CharSequence, V> headMap (CharSequence toKey)
313   {
314     CheckForNull.check (toKey);
315     return new TSTSubMap (null, toKey);
316   }
317 
318   public SortedMap<CharSequence, V> subMap (CharSequence fromKey, CharSequence toKey)
319   {
320     if (fromKey == null || toKey == null)
321       throw new NullPointerException ("One of the submap endpoints is null.");
322     return new TSTSubMap (fromKey, toKey);
323   }
324 
325   public SortedMap<CharSequence, V> tailMap (CharSequence fromKey)
326   {
327     CheckForNull.check (fromKey);
328     return new TSTSubMap (fromKey, null);
329   }
330 
331   /* (non-Javadoc)
332    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#matchAlmost(java.lang.CharSequence, int, int)
333    */
334   public SortedSet<CharSequence> matchAlmost (CharSequence key, int distance, int lengthTolerance)
335   {
336     mMatchingKeys = new TreeSet<CharSequence> ();
337     this.mLengthTolerance = lengthTolerance;
338     matchAlmost (key.toString (), 0, mRootNode, distance, new StringBuilder (), key.toString ()
339         .length ());
340     TreeSet<CharSequence> result = mMatchingKeys;
341     mMatchingKeys = null;
342     return result;
343   }
344 
345   private void matchAlmost (String key, int i, TSTNode<V> currentNode, int distance,
346       StringBuilder prefix, int keyLength)
347   {
348     int nextDist;
349     prefix.setLength (i);
350     if ((currentNode == null) || (distance < 0) || (i >= keyLength + mLengthTolerance))
351     {
352       return;
353     }
354 
355     matchAlmost (key, i, currentNode.mLokid, distance, prefix, keyLength);
356 
357     if (i < keyLength)
358       nextDist = (key.charAt (i) == currentNode.mSplitChar) ? distance : distance - 1;
359     else
360       nextDist = distance - 1;
361     if (distance < 0) return;
362     prefix.append (currentNode.mSplitChar);
363 
364     if ((Math.abs (keyLength - i - 1) <= mLengthTolerance) && (nextDist >= 0)
365         && (currentNode.mData != null))
366     {
367       mMatchingKeys.add (prefix.toString ());
368     }
369 
370     matchAlmost (key, i + 1, currentNode.mEqkid, nextDist, prefix, keyLength);
371     matchAlmost (key, i, currentNode.mHikid, distance, prefix, keyLength);
372   }
373 
374   public V get (Object key)
375   {
376     Entry<CharSequence, V> result = getEntry (key);
377     if (result == null) return null;
378     return result.getValue ();
379   }
380 
381   /* (non-Javadoc)
382    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#getEntry(java.lang.Object)
383    */
384   public Entry<CharSequence, V> getEntry (Object key)
385   {
386     CheckForNull.check (key);
387     String keyString = ((CharSequence) key).toString ();
388     if (keyString.equals ("") && mContainsEmptyStringKey)
389       return new TSTEntry<CharSequence, V> (keyString, mEmptyStringKeyValue);
390     else if (keyString.equals ("") && ! mContainsEmptyStringKey) return null;
391 
392     Stack<TSTStackNode<V>> stack = new Stack<TSTStackNode<V>> ();
393     TSTStackNode<V> currentStackNode = null;
394     TSTNode<V> currentNode = mRootNode;
395     int charIndex = 0;
396     int keyStringLength = keyString.length ();
397     while (true)
398     {
399       if (currentNode == null)
400       {
401         return null; // given key is not contained in map
402       } else
403       {          
404         currentStackNode = new TSTStackNode<V> (currentNode); 
405       }
406       
407       stack.push (currentStackNode);
408       char splitChar = currentNode.mSplitChar;
409       char keyChar = keyString.charAt (charIndex);
410 
411       if (keyChar == splitChar)
412       {
413         charIndex++;
414         if (charIndex == keyStringLength)
415         {
416           return currentNode.mData == null ? null : new TSTEntry<CharSequence, V> (keyString, currentNode.mData);
417         } else
418         {
419           currentNode = currentNode.mEqkid;
420         }
421       } else if (keyChar < splitChar)
422       {
423         currentNode = currentNode.mLokid;
424       } else
425       {
426         currentNode = currentNode.mHikid;
427       }
428     }
429   }
430 
431   /* (non-Javadoc)
432    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#getValueAt(int)
433    */
434   public V getValueAt (int index) throws IndexOutOfBoundsException
435   {
436     return getElementAt (index).getValue ();
437   }
438 
439   /* (non-Javadoc)
440    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#getKeyAt(int)
441    */
442   public CharSequence getKeyAt (int index) throws IndexOutOfBoundsException
443   {
444     return getElementAt (index).getKey ();
445   }
446 
447   /**
448    * Returns either the key at the specified position if <code>getKey</code> is
449    * true or the value otherwise.
450    * 
451    * @param index
452    * @return Object
453    * @throws IndexOutOfBoundsException
454    */
455   private TSTEntry<CharSequence, V> getElementAt (int index) throws IndexOutOfBoundsException
456   {
457     if ((index < 0) || (index > this.size ())) throw new IndexOutOfBoundsException ();
458     if (mContainsEmptyStringKey && index == 0)
459     {
460       return new TSTEntry<CharSequence, V> ("", mEmptyStringKeyValue);
461     }
462 
463     TSTNode<V> currentNode = mRootNode; // start at the root
464     if (mContainsEmptyStringKey)
465     {
466       index--;
467     }
468 
469     StringBuilder buf = new StringBuilder ();
470     int offset, loLength, eqLength, hiLength;
471 
472     index++; // we need an index numbering starting with 1
473     while (true)
474     {
475       offset = 0;
476       // get children's subarray length values
477       if (currentNode.mLokid != null)
478         loLength = currentNode.mLokid.mSubarrayLength;
479       else
480         loLength = 0;
481       if (currentNode.mEqkid != null)
482         eqLength = currentNode.mEqkid.mSubarrayLength;
483       else
484         eqLength = 0;
485       if (currentNode.mHikid != null)
486         hiLength = currentNode.mHikid.mSubarrayLength;
487       else
488         hiLength = 0;
489 
490       // check end condition
491       if ((loLength + eqLength + hiLength == currentNode.mSubarrayLength - 1)
492           && (index - loLength == 1)) break;
493 
494       if (currentNode.mData != null)
495       {
496         // if the currently examined node contains data itself, it is part of
497         // the subset it comprises. Then an additional offset is needed in the
498         // following index calculations.
499         offset = 1;
500       }
501 
502       // now choose the next branch that contains the element with the specified
503       // index.
504       if (loLength + offset >= index)
505       {
506         currentNode = currentNode.mLokid;
507       } else if (loLength + eqLength + offset >= index)
508       {
509         // adjust index to subarray boundaries
510         index -= loLength + offset;
511         buf.append (currentNode.mSplitChar);
512         currentNode = currentNode.mEqkid;
513       } else
514       {
515         index -= loLength + eqLength + offset;
516         currentNode = currentNode.mHikid;
517       }
518     } // end of while
519 
520     return new TSTEntry<CharSequence, V> (buf.append (currentNode.mSplitChar).toString (),
521         currentNode.mData);
522   }
523 
524   /* (non-Javadoc)
525    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#indexOf(java.lang.CharSequence)
526    */
527   public int indexOf (CharSequence key)
528   {
529     String keyString = key.toString ();
530     if (mContainsEmptyStringKey && keyString.equals (""))
531     {
532       return 0;
533     }
534 
535     TSTNode<V> currentNode = mRootNode;
536     int charIndex = 0;
537     int index = mContainsEmptyStringKey ? 1 : 0;
538     int keyStringLength = keyString.length ();
539     int offset, loLength, eqLength;
540 
541     while (true)
542     {
543       if (currentNode == null) return - 1;
544       offset = 0;
545       // get children's subarray length values
546       if (currentNode.mLokid != null)
547         loLength = currentNode.mLokid.mSubarrayLength;
548       else
549         loLength = 0;
550       if (currentNode.mEqkid != null)
551         eqLength = currentNode.mEqkid.mSubarrayLength;
552       else
553         eqLength = 0;
554 
555       if (currentNode.mData != null)
556       {
557         // if the currently examined node contains data itself, it is part of
558         // the subset it comprises. Then an additional offset is needed in the
559         // following index calculations.
560         offset = 1;
561       }
562 
563       if (keyString.charAt (charIndex) == currentNode.mSplitChar)
564       {
565         charIndex++;
566         if ((charIndex == keyStringLength) && (currentNode.mData != null))
567         {
568           // we have reached the correct node
569           return index;
570         } else if ((charIndex == keyStringLength) && (currentNode.mData == null))
571         {
572           // we have worked through the search string, but it's no valid key as
573           // the node we have
574           // reached so far contains no data
575           return - 1;
576         } else
577         {
578           index += loLength + offset;
579           currentNode = currentNode.mEqkid;
580         }
581       } else if (keyString.charAt (charIndex) < currentNode.mSplitChar)
582       {
583         currentNode = currentNode.mLokid;
584       } else
585       {
586         index += loLength + eqLength + offset;
587         currentNode = currentNode.mHikid;
588       }
589     }
590   }
591 
592   /* (non-Javadoc)
593    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#getKeysForPrefix(java.lang.CharSequence)
594    */
595   public Iterable<CharSequence> getKeysForPrefix (final CharSequence prefix)
596   {
597     return new Iterable<CharSequence> ()
598     {
599       public Iterator<CharSequence> iterator ()
600       {
601         return new Iterator<CharSequence> ()
602         {
603           Iterator<Entry<CharSequence, V>> it = getPrefixSubtreeIterator (prefix.toString ());
604 
605           public boolean hasNext ()
606           {
607             return it.hasNext ();
608           }
609 
610           public CharSequence next ()
611           {
612             return it.next ().getKey ();
613           }
614 
615           public void remove ()
616           {
617             it.remove ();
618           }
619         };
620       }
621     };
622   }
623 
624   /* (non-Javadoc)
625    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#getPrefixSubtreeIterator(java.lang.String)
626    */
627   public Iterator<Entry<CharSequence, V>> getPrefixSubtreeIterator (String prefix)
628   {
629     return getPrefixSubtreeIterator (prefix, false);
630   }
631 
632   /* (non-Javadoc)
633    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#getPrefixSubtreeIterator(java.lang.String, boolean)
634    */
635   public Iterator<Entry<CharSequence, V>> getPrefixSubtreeIterator (String pPrefix,
636       boolean inverseSearch)
637   {
638     CheckForNull.check (pPrefix);
639     String prefix = pPrefix;
640     StringBuilder prefixBuilder = new StringBuilder ();
641     if (! inverseSearch && prefix.equals (""))
642       return new TSTIterator ();
643     else if (inverseSearch && prefix.equals ("")) return new TSTIterator (null, "", false, null, null);
644 
645     TSTNode<V> currentNode = mRootNode;
646     int charIndex = 0;
647     int prefixLength = prefix.length ();
648 
649     while (charIndex != prefixLength)
650     {
651       if (currentNode == null)
652       {
653         if (inverseSearch)
654           return new TSTIterator ();
655         else
656           return new TSTIterator (null, "", false, null, null);
657       }
658       char splitChar = currentNode.mSplitChar;
659       char currentChar = prefix.charAt (charIndex);
660           
661       if (currentChar == splitChar)
662       {
663         charIndex++;
664         prefixBuilder.append (currentNode.mSplitChar);
665         if ((charIndex == prefixLength) && (currentNode.mData != null))
666         {
667           return new TSTIterator (currentNode, currentNode.mEqkid, prefixBuilder.toString (), inverseSearch, null, null);
668         } else if (charIndex == prefixLength)
669         {
670           return new TSTIterator (currentNode.mEqkid, prefixBuilder.toString (), inverseSearch, null, null);
671         } else
672         {
673           currentNode = currentNode.mEqkid;
674         }
675       } else if (currentChar < splitChar)
676       {
677         currentNode = currentNode.mLokid;
678       } else
679       {
680         currentNode = currentNode.mHikid;
681       }
682     }
683     if (inverseSearch)
684       return new TSTIterator ();
685     else
686       return new TSTIterator (null, "", inverseSearch, null, null);
687   }
688 
689   public V put (CharSequence key, V value)
690   {
691     CheckForNull.check (key, value);
692     if (key.equals (""))
693     {
694       mEmptyStringKeyValue = value;
695       mContainsEmptyStringKey = true;
696       return value;
697     }
698     V prevValue = null; // stores previous value associated with specified key
699     TSTNode<V> currentNode = mRootNode;
700 
701     TSTNode<V> prevNode = new TSTNode<V> (); // node that was examined during
702     // preceding iteration
703     String keyString = key.toString ();
704     int keyStringLength = keyString.length ();
705     // needed to calculate the element's position within the ArrayList
706     int charIndex = 0;
707     // specifies which child has been selected in preceding iteration
708     NodeType prevBranch = NodeType.NONE;
709     boolean keyAlreadyInList = containsKey (key);
710     char currentChar = keyString.charAt (charIndex);
711 
712     while (true)
713     {
714       if ((currentNode == null) || (mRootNode.mSplitChar == '\0'))
715       {
716         // we reached a leaf node, thus create new node
717         if (currentNode == mRootNode)
718         {
719           mRootNode.mSplitChar = currentChar;
720         } else
721         {
722           currentNode = new TSTNode<V> (currentChar);
723           switch (prevBranch)
724           {
725           case HIKID:
726             prevNode.mHikid = currentNode;
727             break;
728           case EQKID:
729             prevNode.mEqkid = currentNode;
730             break;
731           case LOKID:
732             prevNode.mLokid = currentNode;
733             break;
734           default: // case NONE:
735           }
736         }
737       }
738       if (currentChar == currentNode.mSplitChar)
739       {
740         ++charIndex;
741         if (charIndex == keyStringLength)
742         {
743           // we have reached the element's final destination
744           // put the data element into the tree
745           if (prevValue == null)
746           {
747             prevValue = currentNode.mData;
748           }
749           currentNode.mData = value;
750           if (! keyAlreadyInList) ++currentNode.mSubarrayLength; // increase the
751                                                                  // length of
752                                                                  // the
753                                                                  // current node's subarray.
754           return prevValue; // we're done...
755         } else
756         {
757           currentChar = keyString.charAt (charIndex);
758           if (! keyAlreadyInList) ++currentNode.mSubarrayLength; // increase the
759                                                                  // length of
760                                                                  // the
761                                                                  // current node's subarray.
762           prevNode = currentNode;
763           currentNode = currentNode.mEqkid;
764           prevBranch = NodeType.EQKID;
765         }
766       } else if (currentChar < currentNode.mSplitChar)
767       {
768         if (! keyAlreadyInList) ++currentNode.mSubarrayLength; // increase the
769                                                                // length of the
770                                                                // current
771                                                                // node's subarray.
772         prevNode = currentNode;
773         currentNode = currentNode.mLokid;
774         prevBranch = NodeType.LOKID;
775       } else
776       {
777         if (! keyAlreadyInList) ++currentNode.mSubarrayLength; // increase the
778                                                                // length of the
779                                                                // current
780                                                                // node's subarray.
781         prevNode = currentNode;
782         currentNode = currentNode.mHikid;
783         prevBranch = NodeType.HIKID;
784       }
785     }
786   }
787 
788   /* (non-Javadoc)
789    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#predecessor(java.lang.Object)
790    */
791   public Entry<CharSequence, V> predecessor (Object keyObject)
792   {
793     CharSequence key = (CharSequence) keyObject;
794     
795     if (key.equals ("")) return null; // the empty string is always the first entry in the map
796     SortedMap<CharSequence, V> headMap = headMap (key);
797     CharSequence prevKey = headMap.lastKey ();
798     return prevKey == null ? null : getEntry (prevKey);
799   }
800 
801   /* (non-Javadoc)
802    * @see info.rolandkrueger.roklib.util.ITernarySearchTreeMap#successor(java.lang.Object)
803    */
804   public Entry<CharSequence, V> successor (Object keyObject)
805   {
806     CharSequence key = (CharSequence) keyObject;
807     
808     Iterator<Map.Entry<CharSequence, V>> it = tailMap (key).entrySet ().iterator ();
809     if (containsKey (key)) 
810     {
811       it.next ();
812     }      
813     return it.hasNext () ? it.next () : null;
814   }
815 
816   public int size ()
817   {
818     if (mContainsEmptyStringKey) return mRootNode.mSubarrayLength + 1;
819     return mRootNode.mSubarrayLength;
820   }
821 
822   /**
823    * @see java.util.Map#entrySet()
824    */
825   public Set<Entry<CharSequence, V>> entrySet ()
826   {
827     return new TSTEntrySet (null, null);
828   }
829 
830   private class TSTStackNode<NodeValue> implements Serializable
831   {
832     private static final long serialVersionUID = - 9203260538139494037L;
833     
834     public TSTStackNode (TSTNode<NodeValue> node)
835     {
836       mNode = node;
837     }
838     
839     public TSTNode<NodeValue> mNode;
840     public boolean mNeedsRecheck = false;
841     public int mCharIndex;
842     public boolean mRecheckHiKid = false;
843   }
844   
845   private class TSTNode<NodeValue> implements Serializable
846   {
847     private static final long serialVersionUID = - 692198357972673845L;
848     
849     public TSTNode<NodeValue> mLokid, mEqkid, mHikid;
850     public NodeValue mData;
851     public int mSubarrayLength = 0; // this field takes the number of data elements
852 
853     // that are stored in the subtree that is attached to the specific node.
854     // The node itself is included if it stores a data element. This is
855     // needed for accessing the individual data elements by an index.
856     public char mSplitChar = '\0';
857 
858     public TSTNode ()
859     {
860       super ();
861     }
862 
863     public TSTNode (char splitChar)
864     {
865       mSplitChar = splitChar;
866     }
867 
868     @Override
869     public String toString ()
870     {
871       return toString (0);
872     }
873 
874     private String toString (int level)
875     {
876       StringBuilder indentBuf = new StringBuilder ();
877       for (int i = 0; i < level; ++i)
878         indentBuf.append ("  ");
879       String indent = indentBuf.toString ();
880 
881       return String.format ("{SL=%d; ch=%s; data=%s;\n%slo=%s,\n%seq=%s,\n%shi=%s}",
882           mSubarrayLength, mSplitChar, mData == null ? "" : mData.toString (), indent,
883           mLokid == null ? "0" : mLokid.toString (level + 1), indent,
884           mEqkid == null ? "0" : mEqkid.toString (level + 1), indent,
885           mHikid == null ? "0" : mHikid.toString (level + 1));
886     }
887   } // End of private class TSTNode
888 
889   class TSTIterator implements Iterator<Entry<CharSequence, V>>, Serializable
890   {
891     private static final long serialVersionUID = - 7270329414991887561L;
892     
893     private Stack<TSTItStackNode> mStack;
894     private String mPrefix;
895     private String mPrefixForInverseSearch;
896     private StringBuilder mBuffer;
897     private boolean mWentToNextItem = false; // specifies whether or not tree
898                                              // has been traversed to the next
899                                              // item
900     private TSTNode<V> mFirstElement = null;
901     private String mPreviouslyReturnedKey; // key that was returned by the last
902                                            // call of next(). Needed for
903                                            // remove()
904     private boolean mPrevKeyRemoved = false; // needed for remove() to raise
905     private boolean mProvideEmptyKeyValue = false;
906     private boolean mIteratorEmpty = false;
907     private boolean mInverseSearch;
908     private int mPrefixForInverseSearchLength;
909     private CharSequence mFromKey;
910     private CharSequence mExclusiveToKey;
911 
912     public TSTIterator ()
913     {
914       this (null, null);
915     }
916     
917     public TSTIterator (CharSequence fromKey, CharSequence toKey)
918     {
919       this (mRootNode, "", false, fromKey, toKey);
920     }
921 
922     public TSTIterator (TSTNode<V> startNode, String prefix, boolean inverse, CharSequence fromKey, CharSequence toKey)
923     {
924       this (null, startNode, prefix, inverse, fromKey, toKey);
925     }
926 
927     public TSTIterator (TSTNode<V> firstElement, TSTNode<V> startNode, String prefix,
928         boolean inverse, CharSequence fromKey, CharSequence toKey)
929     {
930       if (fromKey != null && toKey != null && compare (fromKey, toKey) > 0)
931         throw new IllegalArgumentException ("Invalid parameters: fromKey > toKey");
932       
933       mFromKey = fromKey;
934       mExclusiveToKey = toKey;
935       if ("".equals (mExclusiveToKey)) mIteratorEmpty = true;
936       if (inverse)
937       {
938         mPrefixForInverseSearch = prefix;
939         mPrefixForInverseSearchLength = prefix.length ();
940         init (mRootNode, "");
941       } else
942       {
943         mFirstElement = firstElement;
944         init (startNode, prefix);
945       }
946       mInverseSearch = inverse;
947     }
948 
949     private void init (TSTNode<V> startNode, String prefix)
950     {
951       mBuffer = new StringBuilder ();
952       mStack = new Stack<TSTItStackNode> ();
953       mPrefix = "";
954       if (startNode != null) mStack.push (new TSTItStackNode (startNode));
955       if (prefix != null) mPrefix = prefix;
956       if (mPrefix.equals ("") && mContainsEmptyStringKey)
957       {
958         if (mFromKey == null || (mFromKey != null && "".equals (mFromKey)))
959         {
960           mProvideEmptyKeyValue = true;
961         }
962       }
963     }
964 
965     private void goToNextElement ()
966     {
967       TSTItStackNode currentNode;
968       // go to next smallest element in TST
969       currentNode = mStack.pop ();
970 
971       while (true)
972       {
973         if ((currentNode.mNode.mData != null) && (currentNode.mReturned == false)
974             && (currentNode.mVisited == TSTItStackNode.LOKID))
975         {
976           mStack.push (currentNode);
977           if (! mInverseSearch || mBuffer.length () + 1 < mPrefixForInverseSearchLength) return;
978           if (! (mBuffer.toString () + currentNode.mNode.mSplitChar)
979               .startsWith (mPrefixForInverseSearch)) return;
980         }
981         if ((currentNode.mNode.mLokid != null) && (currentNode.mVisited == TSTItStackNode.NONE))
982         {
983           currentNode.mVisited = TSTItStackNode.LOKID;
984           mStack.push (currentNode);
985           currentNode = new TSTItStackNode (currentNode.mNode.mLokid);
986         } else if ((currentNode.mNode.mLokid == null)
987             && (currentNode.mVisited == TSTItStackNode.NONE))
988           currentNode.mVisited = TSTItStackNode.LOKID;
989         else if ((currentNode.mNode.mEqkid != null)
990             && (currentNode.mVisited < TSTItStackNode.LOEQKID))
991         {
992           mBuffer.append (currentNode.mNode.mSplitChar);
993           currentNode.mVisited = TSTItStackNode.LOEQKID;
994           mStack.push (currentNode);
995           currentNode = new TSTItStackNode (currentNode.mNode.mEqkid);
996         } else if ((currentNode.mNode.mEqkid == null)
997             && (currentNode.mVisited < TSTItStackNode.LOEQKID))
998           currentNode.mVisited = TSTItStackNode.LOEQKID;
999         else if ((currentNode.mNode.mHikid != null) && (currentNode.mVisited < TSTItStackNode.ALL))
1000         {
1001           currentNode.mVisited = TSTItStackNode.ALL;
1002           mStack.push (currentNode);
1003           currentNode = new TSTItStackNode (currentNode.mNode.mHikid);
1004         } else if ((currentNode.mNode.mHikid == null)
1005             && (currentNode.mVisited < TSTItStackNode.ALL))
1006           currentNode.mVisited = TSTItStackNode.ALL;
1007         else if (currentNode.mVisited == TSTItStackNode.ALL)
1008         {
1009           if (mStack.empty ()) return;
1010           currentNode = mStack.pop ();
1011           if (currentNode.mVisited == TSTItStackNode.LOEQKID)
1012           {
1013             mBuffer.setLength (mBuffer.length () - 1);
1014           }
1015         }
1016       }
1017     }
1018 
1019     public TSTEntry<CharSequence, V> next ()
1020     {
1021       if (! hasNext ()) throw new NoSuchElementException ();
1022       mPrevKeyRemoved = false; // reset that variable
1023 
1024       if (mProvideEmptyKeyValue)
1025       {
1026         mProvideEmptyKeyValue = false;
1027         mPreviouslyReturnedKey = "";
1028         return new TSTEntry<CharSequence, V> ("", mEmptyStringKeyValue);
1029       }
1030 
1031       if (mFirstElement != null)
1032       {
1033         V dummy = mFirstElement.mData;
1034         mFirstElement = null;
1035         mPreviouslyReturnedKey = mPrefix;
1036         return new TSTEntry<CharSequence, V> (mPrefix, dummy);
1037       }
1038       if (! mWentToNextItem)
1039       {
1040         goToNextElement ();
1041       } else
1042       {
1043         mWentToNextItem = false;
1044       }
1045       TSTItStackNode stNode = mStack.peek ();
1046       String key = new StringBuilder ().append (mPrefix).append (mBuffer)
1047           .append (stNode.mNode.mSplitChar).toString ();
1048       stNode.mReturned = true;
1049       mPreviouslyReturnedKey = key;
1050       return new TSTEntry<CharSequence, V> (key, stNode.mNode.mData);
1051     }
1052 
1053     public boolean hasNext ()
1054     {
1055       if (mIteratorEmpty) return false;
1056       if (mFirstElement != null || mProvideEmptyKeyValue) return true;
1057       if ((! mWentToNextItem) && (mStack.size () > 0))
1058       {
1059         goToNextElement ();
1060         mWentToNextItem = true;
1061       }
1062       mIteratorEmpty = mStack.isEmpty ();
1063       String currentKey = null;
1064       if (! mIteratorEmpty) 
1065       {
1066         currentKey = new StringBuilder ().append (mPrefix).append (mBuffer)
1067         .append (mStack.peek ().mNode.mSplitChar).toString ();
1068       }
1069       
1070       if (! mIteratorEmpty && mFromKey != null)
1071       {
1072         while (compare (mFromKey, currentKey) > 0)
1073         {
1074           goToNextElement ();
1075           mIteratorEmpty = mStack.isEmpty ();
1076           if (mIteratorEmpty) return false;
1077           mStack.peek ().mReturned = true;
1078           currentKey = new StringBuilder ().append (mPrefix).append (mBuffer)
1079           .append (mStack.peek ().mNode.mSplitChar).toString ();
1080         }
1081       }
1082       
1083       if (! mIteratorEmpty && mExclusiveToKey != null)
1084       {
1085         if (compare (currentKey, mExclusiveToKey) >= 0)
1086           return false;
1087       }
1088       return ! mIteratorEmpty;
1089     }
1090 
1091     public void remove () throws IllegalStateException
1092     {
1093       if (mIteratorEmpty) throw new NoSuchElementException ();
1094       if (! mPrevKeyRemoved)
1095       {
1096         if (mPreviouslyReturnedKey != null)
1097         {
1098           TernarySearchTreeMap.this.remove (mPreviouslyReturnedKey);
1099         } else
1100           throw new IllegalStateException (
1101               "Iterator.next() must be called prior to Iterator.remove().");
1102       } else
1103         throw new IllegalStateException (
1104             "Iterator's previously returned key has already been removed.");
1105       mPrevKeyRemoved = true;
1106     }
1107 
1108     private class TSTItStackNode implements Serializable
1109     {
1110       private static final long serialVersionUID = - 5471137639654374101L;
1111 
1112       public final static int NONE = 0;
1113       public final static int LOKID = 1;
1114       public final static int LOEQKID = 2;
1115       public final static int ALL = 3;
1116       public TSTNode<V> mNode;
1117       public int mVisited; // which child nodes have already been visited
1118       public boolean mReturned; // has data for specific node already been
1119                                 // returned
1120 
1121       public TSTItStackNode (TSTNode<V> node)
1122       {
1123         this (node, NONE);
1124       }
1125 
1126       public TSTItStackNode (TSTNode<V> node, int visited)
1127       {
1128         mReturned = false;
1129         mNode = node;
1130         mVisited = visited;
1131       }
1132     }
1133   } // End of private class TSTIterator
1134 
1135   private class TSTValuesCollection extends AbstractCollection<V> implements Serializable
1136   {
1137     private static final long serialVersionUID = 8889125197129391125L;
1138    
1139     private CharSequence mFromKey;
1140     private CharSequence mExclusiveToKey;
1141     
1142     public TSTValuesCollection ()
1143     {
1144       this (null, null);
1145     }
1146     
1147     public TSTValuesCollection (CharSequence fromKey, CharSequence toKey)
1148     {
1149       if (fromKey != null && toKey != null && compare (fromKey, toKey) > 0)
1150         throw new IllegalArgumentException ("Invalid parameters: fromKey > toKey");
1151       mFromKey = fromKey;
1152       mExclusiveToKey = toKey;
1153     }
1154     
1155     @Override
1156     public boolean add (V o)
1157     {
1158       throw new UnsupportedOperationException (
1159           "TernarySearchTree's value collection: add() not allowed!");
1160     }
1161 
1162     @Override
1163     public boolean addAll (Collection<? extends V> c)
1164     {
1165       throw new UnsupportedOperationException (
1166           "TernarySearchTree's value collection: addAll() not allowed!");
1167     }
1168 
1169     @Override
1170     public void clear ()
1171     {
1172       if (mFromKey == null && mExclusiveToKey == null)
1173       {
1174         TernarySearchTreeMap.this.clear ();
1175       } else
1176       {
1177         for (Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey); it.hasNext ();)
1178         {
1179           it.next ();
1180           it.remove ();
1181         }
1182       }      
1183     }
1184 
1185     @Override
1186     public boolean contains (Object o)
1187     {
1188       for (Map.Entry<CharSequence, V> entry : new TSTSubMap (mFromKey, mExclusiveToKey).entrySet ())
1189       {
1190         if (entry.getValue () == null && o == null) return true;
1191         if (entry.getValue ().equals (o)) return true;
1192       }
1193       return false;
1194     }
1195 
1196     @Override
1197     public boolean containsAll (Collection<?> c)
1198     {
1199       for (Object o : c)
1200       {
1201         if (! contains (o)) return false;
1202       }
1203       return true;
1204     }
1205 
1206     @SuppressWarnings ("rawtypes")
1207     @Override
1208     public boolean equals (Object obj)
1209     {
1210       if (this == obj) return true;
1211       if (! (obj instanceof Collection<?>)) return false;
1212       Collection other = (Collection) obj;
1213       if (other.size () != size ()) return false;
1214       for (Object o : other)
1215       {
1216         if (! contains (o)) return false;
1217       }
1218       return true;
1219     }
1220 
1221     @Override
1222     public boolean isEmpty ()
1223     {
1224       return size () == 0;
1225     }
1226 
1227     public Iterator<V> iterator ()
1228     {
1229       final Iterator<Entry<CharSequence, V>> entryIterator = new TSTIterator (mFromKey, mExclusiveToKey);
1230       return new Iterator<V> ()
1231       {
1232         public boolean hasNext ()
1233         {
1234           return entryIterator.hasNext ();
1235         }
1236 
1237         public V next ()
1238         {
1239           return entryIterator.next ().getValue ();
1240         }
1241 
1242         public void remove ()
1243         {
1244           entryIterator.remove ();
1245         }
1246       };
1247     }
1248 
1249     @Override
1250     public boolean remove (Object o)
1251     {
1252       for (Iterator<V> it = iterator (); it.hasNext ();)
1253       {
1254         V value = it.next ();
1255         if (value.equals (o))
1256         {
1257           it.remove ();
1258           return true;
1259         }
1260       }
1261       return false;
1262     }
1263 
1264     @Override
1265     public boolean removeAll (Collection<?> c)
1266     {
1267       boolean changed = false;
1268       for (Iterator<V> it = iterator (); it.hasNext ();)
1269       {
1270         V value = it.next ();
1271         if (c.contains (value))
1272         {
1273           it.remove ();
1274           changed = true;
1275         }
1276       }
1277       return changed;
1278     }
1279 
1280     @Override
1281     public boolean retainAll (Collection<?> c)
1282     {
1283       boolean changed = false;
1284       for (Iterator<V> it = iterator (); it.hasNext ();)
1285       {
1286         V value = it.next ();
1287         if (! c.contains (value))
1288         {
1289           it.remove ();
1290           changed = true;
1291         }
1292       }
1293       return changed;
1294     }
1295 
1296     public int size ()
1297     {
1298       if (mFromKey == null && mExclusiveToKey == null)
1299       {
1300         return TernarySearchTreeMap.this.size ();
1301       }
1302       else
1303       {
1304         int count = 0;
1305         for (Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey); it.hasNext ();)
1306         {
1307           it.next ();
1308           ++count;
1309         }
1310         return count;
1311       }
1312     }
1313   }
1314 
1315   class TSTKeySet extends AbstractSet<CharSequence> implements Set<CharSequence>, Serializable
1316   {
1317     private static final long serialVersionUID = - 2892939426138635211L;
1318   
1319     private CharSequence mFromKey;
1320     private CharSequence mExclusiveToKey;
1321 
1322     public TSTKeySet ()
1323     {
1324       this (null, null);
1325     }
1326 
1327     public TSTKeySet (CharSequence fromKey, CharSequence toKey)
1328     {
1329       assert (!(fromKey != null && toKey != null && compare (fromKey, toKey) > 0));
1330 
1331       mFromKey = fromKey;
1332       mExclusiveToKey = toKey;
1333     }
1334 
1335     public boolean add (CharSequence o)
1336     {
1337       throw new UnsupportedOperationException ("TernarySearchTree's key set: add() not allowed!");
1338     }
1339 
1340     public boolean addAll (Collection<? extends CharSequence> c)
1341     {
1342       throw new UnsupportedOperationException ("TernarySearchTree's key set: addAll() not allowed!");
1343     }
1344 
1345     public void clear ()
1346     {
1347       if (mFromKey == null && mExclusiveToKey == null)
1348         TernarySearchTreeMap.this.clear ();
1349       for (Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey); it.hasNext ();)
1350       {
1351         it.next ();
1352         it.remove ();
1353       }
1354     }
1355 
1356     public boolean contains (Object o)
1357     {
1358       String key = ((CharSequence) o).toString ();
1359       if (mFromKey != null && compare (mFromKey, key) > 0) return false;
1360       if (mExclusiveToKey != null && 
1361           (compare (key, mExclusiveToKey) > 0 || compare (key, mExclusiveToKey) == 0)) return false;
1362       
1363       return TernarySearchTreeMap.this.containsKey (o);
1364     }
1365 
1366     public boolean containsAll (Collection<?> c)
1367     {
1368       for (Object o : c)
1369       {
1370         if (! contains (o)) return false;
1371       }
1372       return true;
1373     }
1374 
1375     public boolean isEmpty ()
1376     {
1377       return size () == 0;
1378     }
1379 
1380     public Iterator<CharSequence> iterator ()
1381     {
1382       final Iterator<Entry<CharSequence, V>> entryIterator = new TSTIterator (mFromKey, mExclusiveToKey);
1383       return new Iterator<CharSequence> ()
1384       {
1385         public boolean hasNext ()
1386         {
1387           return entryIterator.hasNext ();
1388         }
1389 
1390         public CharSequence next ()
1391         {
1392           return entryIterator.next ().getKey ();
1393         }
1394 
1395         public void remove ()
1396         {
1397           entryIterator.remove ();
1398         }
1399       };
1400     }
1401 
1402     public boolean remove (Object o)
1403     {
1404       return contains (o) && TernarySearchTreeMap.this.remove (o) != null;
1405     }
1406 
1407     public boolean removeAll (Collection<?> c)
1408     {
1409       boolean changed = false;
1410 
1411       for (Object o : c)
1412       {
1413         if (contains (o) && remove (o)) changed = true;
1414       }
1415 
1416       return changed;
1417     }
1418 
1419     public boolean retainAll (Collection<?> c)
1420     {
1421       boolean changed = false;
1422 
1423       for (Iterator<CharSequence> it = iterator (); it.hasNext ();)
1424       {
1425         CharSequence element = it.next ();
1426         if (! c.contains (element))
1427         {
1428           changed = true;
1429           remove (element);
1430         }
1431       }
1432 
1433       return changed;
1434     }
1435 
1436     public int size ()
1437     {
1438       if ("".equals (mExclusiveToKey)) return 0;
1439       if (mFromKey == null && mExclusiveToKey == null)
1440       {
1441         return TernarySearchTreeMap.this.size ();
1442       } else
1443       {
1444         int count = 0;
1445         for (Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey); it.hasNext ();)
1446         {
1447           it.next ();
1448           ++count;
1449         }
1450         return count;
1451       }
1452     }
1453   }
1454 
1455   private class TSTEntrySet extends AbstractSet<Entry<CharSequence, V>> implements
1456   Set<Entry<CharSequence, V>>, Serializable
1457   {
1458     private static final long serialVersionUID = - 7845332141119069118L;
1459    
1460     private CharSequence mFromKey;
1461     private CharSequence mExclusiveToKey;
1462     
1463     public TSTEntrySet (CharSequence fromKey, CharSequence toKey)
1464     {
1465       if (fromKey != null && toKey != null && compare (fromKey, toKey) > 0)
1466         throw new IllegalArgumentException ("Invalid parameters: fromKey > toKey");
1467       mFromKey = fromKey;
1468       mExclusiveToKey = toKey;
1469     }
1470     
1471     @Override
1472     public boolean addAll (Collection<? extends java.util.Map.Entry<CharSequence, V>> c)
1473     {
1474       throw new UnsupportedOperationException (new String ("TernarySearchTreeMap's entry set: addAll() not allowed!"));
1475     }
1476     
1477     public boolean add (Entry<CharSequence, V> o)
1478     {
1479       throw new UnsupportedOperationException (new String (
1480           "TernarySearchTreeMap's entry set: add() not allowed!"));
1481     }
1482 
1483     public void clear ()
1484     {
1485       for (Iterator<Entry<CharSequence, V>> it = iterator (); it.hasNext ();)
1486       {
1487         it.next ();
1488         it.remove ();
1489       }
1490     }
1491 
1492     @SuppressWarnings ("unchecked")
1493     public boolean contains (Object entry)
1494     {
1495       if (! (entry instanceof Map.Entry<?, ?>))
1496         return false;
1497       else
1498         return containsEntry ((Map.Entry<CharSequence, V>) entry);
1499     }
1500 
1501     private boolean containsEntry (Map.Entry<CharSequence, V> entry)
1502     {
1503       if (mFromKey != null && compare (mFromKey, entry.getKey ()) > 0) return false;
1504       if (mExclusiveToKey != null && 
1505           (compare (entry.getKey (), mExclusiveToKey) > 0 || compare (entry.getKey (), mExclusiveToKey) == 0)) return false;
1506       if ( ! containsKey (entry.getKey ())) return false;
1507       V value = TernarySearchTreeMap.this.get (entry.getKey ());
1508       if (value == null || ! value.equals (entry.getValue ())) return false;
1509       return true;
1510     }
1511 
1512 //    public boolean containsAll (Collection<?> c)
1513 //    {
1514 //      for (Object o : c)
1515 //      {
1516 //        if (! contains (o)) return false;
1517 //      }
1518 //      return true;
1519 //    }
1520 
1521 //    public boolean isEmpty ()
1522 //    {
1523 //      return size () == 0;
1524 //    }
1525 
1526     public Iterator<Entry<CharSequence, V>> iterator ()
1527     {
1528       return new TSTIterator (mFromKey, mExclusiveToKey);
1529     }
1530 
1531     @SuppressWarnings ({ "rawtypes", "unchecked" })
1532     public boolean remove (Object o)
1533     {
1534       if (! (o instanceof Map.Entry)) return false;
1535       Map.Entry<CharSequence, V> entry = (Map.Entry<CharSequence, V>) o;
1536       if (mFromKey != null && compare (mFromKey, entry.getKey ()) > 0) return false;
1537       if (mExclusiveToKey != null && 
1538           (compare (entry.getKey (), mExclusiveToKey) > 0 || compare (entry.getKey (), mExclusiveToKey) == 0)) return false;
1539       Object key = ((Map.Entry) o).getKey ();
1540       Map.Entry mapEntry = getEntry (key);
1541       if (mapEntry == null) return false;
1542       if (! mapEntry.getValue ().equals (entry.getValue ())) return false;
1543       return TernarySearchTreeMap.this.remove (key) != null;
1544     }
1545 
1546 //    public boolean removeAll (Collection<?> c)
1547 //    {
1548 //      boolean changed = false;
1549 //      for (Object o : c)
1550 //      {
1551 //        if (remove (o)) changed = true;
1552 //      }
1553 //      return changed;
1554 //    }
1555 
1556 //    @SuppressWarnings ("rawtypes")
1557 //    public boolean retainAll (Collection<?> c)
1558 //    {
1559 //      boolean changed = false;
1560 //      for (Map.Entry element : this)
1561 //      {
1562 //        if (! c.contains (element))
1563 //        {
1564 //          changed = true;
1565 //          remove (element);
1566 //        }
1567 //      }
1568 //      return changed;
1569 //    }
1570 
1571     public int size ()
1572     {
1573       if (mFromKey == null && mExclusiveToKey == null)
1574         return TernarySearchTreeMap.this.size ();
1575       int count = 0;
1576       for (Iterator<Entry<CharSequence, V>> it = iterator (); it.hasNext ();)
1577       {
1578         it.next ();
1579         ++count;
1580       }
1581       return count;
1582     }
1583   } // End of private class TSTEntrySet
1584 
1585   private class TSTEntry<EntryK extends CharSequence, EntryV extends V> implements Map.Entry<EntryK, EntryV>, Serializable
1586   {
1587     private static final long serialVersionUID = - 5785604459208599077L;
1588 
1589     private EntryK mKey;
1590     private EntryV mValue;
1591 
1592     public TSTEntry (EntryK key, EntryV value)
1593     {
1594       mKey = key;
1595       mValue = value;
1596     }
1597 
1598     @SuppressWarnings ("rawtypes")
1599     @Override
1600     public boolean equals (Object other)
1601     {
1602       if (! (other instanceof Map.Entry)) return false;
1603       Map.Entry entry = (Map.Entry) other;
1604       return (entry.getKey () == null ? getKey () == null : entry.getKey ().equals (getKey ()))
1605           && (entry.getValue () == null ? getValue () == null : entry.getValue ().equals (
1606               getValue ()));
1607     }
1608 
1609     public EntryK getKey ()
1610     {
1611       return mKey;
1612     }
1613 
1614     public EntryV getValue ()
1615     {
1616       return mValue;
1617     }
1618 
1619     @Override
1620     public int hashCode ()
1621     {
1622       return (getKey () == null ? 0 : getKey ().hashCode ())
1623           ^ (getValue () == null ? 0 : getValue ().hashCode ());
1624     }
1625 
1626     public EntryV setValue (EntryV value)
1627     {
1628       CheckForNull.check (value);
1629       EntryV oldValue = mValue;
1630       mValue = value;
1631       TernarySearchTreeMap.this.put (mKey, value);
1632       return oldValue;
1633     }
1634 
1635     @Override
1636     public String toString ()
1637     {
1638       return mKey.toString () + "=" + mValue.toString ();
1639     }
1640   } // End of private class TSTEntry
1641 
1642   private class TSTSubMap extends AbstractMap<CharSequence, V> implements SortedMap<CharSequence, V>, Serializable
1643   {
1644     private static final long serialVersionUID = - 6603551293795525865L;
1645 
1646     private CharSequence mFromKey;
1647     private CharSequence mExclusiveToKey;
1648 
1649     public TSTSubMap (CharSequence fromKey, CharSequence toKey)
1650     {
1651       if (fromKey != null && toKey != null && compare (fromKey, toKey) > 0)
1652         throw new IllegalArgumentException ("Invalid parameters: fromKey > toKey");
1653 
1654       mFromKey = fromKey;
1655       mExclusiveToKey = toKey;
1656     }
1657 
1658     private boolean isInRange (Object keyObj)
1659     {
1660       CharSequence key = (CharSequence) keyObj;
1661       if (mContainsEmptyStringKey && "".equals (key.toString ()))
1662       {
1663         if ((mFromKey != null && ! "".equals (mFromKey)) ||
1664             (mExclusiveToKey != null && "".equals (mExclusiveToKey)))
1665         {
1666           return false;
1667         }
1668         if (mFromKey != null && "".equals (mFromKey))
1669         {
1670           return true;
1671         }
1672       }
1673       if (mFromKey != null && compare (key, mFromKey) < 0) return false;
1674       if (mExclusiveToKey == null) return true;
1675       if (compare (key, mExclusiveToKey) >= 0) return false;
1676       return true;
1677     }
1678 
1679     public Comparator<? super CharSequence> comparator ()
1680     {
1681       return TernarySearchTreeMap.this.comparator ();
1682     }
1683 
1684     public CharSequence firstKey ()
1685     {
1686       Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey);
1687       if (it.hasNext ())
1688         return it.next ().getKey ();
1689       return null;
1690     }
1691 
1692     public CharSequence lastKey ()
1693     {
1694       return lastKey (true);
1695     }
1696 
1697     private CharSequence lastKey (boolean throwException)
1698     {
1699       Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey);
1700       Entry<CharSequence, V> entry = null;
1701       while (it.hasNext ())
1702       {
1703         entry = it.next ();
1704       }
1705       return entry == null ? null : entry.getKey ();
1706     }
1707 
1708     public SortedMap<CharSequence, V> headMap (CharSequence toKey)
1709     {
1710       if (mExclusiveToKey != null && compare (toKey, mExclusiveToKey) > 0)
1711         throw new IllegalArgumentException ("toKey out of range");
1712 
1713       return new TSTSubMap (mFromKey, toKey);
1714     }
1715 
1716     public SortedMap<CharSequence, V> subMap (CharSequence fromKey, CharSequence toKey)
1717     {
1718       if (mFromKey != null && compare (mFromKey, fromKey) > 0)
1719         throw new IllegalArgumentException ("fromKey out of range");
1720       if (mExclusiveToKey != null && compare (toKey, mExclusiveToKey) > 0)
1721         throw new IllegalArgumentException ("toKey out of range");
1722 
1723       return new TSTSubMap (fromKey, toKey);
1724     }
1725 
1726     public SortedMap<CharSequence, V> tailMap (CharSequence fromKey)
1727     {
1728       if (mFromKey != null && compare (mFromKey, fromKey) > 0)
1729         throw new IllegalArgumentException ("fromKey out of range");
1730       return new TSTSubMap (fromKey, mExclusiveToKey);
1731     }
1732 
1733     public void clear ()
1734     {
1735       for (CharSequence key : this.keySet ())
1736       {
1737         TernarySearchTreeMap.this.remove (key);
1738       }
1739     }
1740 
1741     public boolean containsKey (Object key)
1742     {
1743       if (! isInRange (key)) return false;
1744       return (TernarySearchTreeMap.this.containsKey (key));
1745     }
1746 
1747     public boolean containsValue (Object value)
1748     {
1749       for (Map.Entry<CharSequence, V> entry : entrySet ())
1750       {
1751         if (entry.getValue ().equals (value)) return true;
1752       }
1753       return false;
1754     }
1755 
1756     public V get (Object key)
1757     {
1758       if (! isInRange (key)) return null;
1759       return TernarySearchTreeMap.this.get (key);
1760     }
1761 
1762     public boolean isEmpty ()
1763     {
1764       return size () == 0;
1765     }
1766 
1767     public V put (CharSequence key, V value)
1768     {
1769       if (! isInRange (key))
1770         throw new IllegalArgumentException ("The key " + key
1771             + " is not within the bounds of this submap.");
1772       return TernarySearchTreeMap.this.put (key, value);
1773     }
1774 
1775     public void putAll (Map<? extends CharSequence, ? extends V> t)
1776     {
1777       for (CharSequence key : t.keySet ())
1778       {
1779         put (key, t.get (key));
1780       }
1781     }
1782 
1783     public V remove (Object key)
1784     {
1785       if (key == null) throw new NullPointerException ("key is null");
1786       if (! containsKey (key)) return null; // check if the key is in range
1787       return TernarySearchTreeMap.this.remove (key);
1788     }
1789 
1790     public int size ()
1791     {
1792       if (TernarySearchTreeMap.this.isEmpty () || "".equals (mExclusiveToKey)) return 0;
1793       int count = 0;
1794       for (Iterator<Entry<CharSequence, V>> it = new TSTIterator (mFromKey, mExclusiveToKey); it.hasNext ();)
1795       {
1796         it.next ();
1797         ++count;
1798       }
1799       return count;
1800     }
1801 
1802     public Set<Map.Entry<CharSequence, V>> entrySet ()
1803     {
1804       return new TSTEntrySet (mFromKey, mExclusiveToKey);
1805     }
1806 
1807     public Set<CharSequence> keySet ()
1808     {
1809       return new TSTKeySet (mFromKey, mExclusiveToKey);
1810     }
1811 
1812     public Collection<V> values ()
1813     {
1814       return new TSTValuesCollection (mFromKey, mExclusiveToKey);
1815     }
1816   } // End of class TernarySearchTreeMap.SubMap
1817 } // End of class TernarySearchTreeMap
1818