001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.shared.ldap.util;
021    
022    
023    import java.io.Externalizable;
024    import java.io.IOException;
025    import java.io.ObjectInput;
026    import java.io.ObjectOutput;
027    import java.util.AbstractCollection;
028    import java.util.AbstractSet;
029    import java.util.ArrayList;
030    import java.util.Collection;
031    import java.util.Collections;
032    import java.util.ConcurrentModificationException;
033    import java.util.HashMap;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.Map;
037    import java.util.NoSuchElementException;
038    import java.util.Set;
039    
040    
041    /**
042     * A map of objects whose mapping entries are sequenced based on the order in
043     * which they were added. This data structure has fast <i>O(1)</i> search time,
044     * deletion time, and insertion time.
045     * <p>
046     * Although this map is sequenced, it cannot implement {@link java.util.List}
047     * because of incompatible interface definitions. The remove methods in List and
048     * Map have different return values (see: {@link java.util.List#remove(Object)}
049     * and {@link java.util.Map#remove(Object)}).
050     * <p>
051     * This class is not thread safe. When a thread safe implementation is required,
052     * use {@link java.util.Collections#synchronizedMap(Map)} as it is documented,
053     * or use explicit synchronization controls.
054     * 
055     * @since Commons Collections 2.0
056     * @version $Revision: 664290 $ $Date: 2008-06-07 08:28:06 +0200 (Sam, 07 jui 2008) $
057     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
058     */
059    public class SequencedHashMap implements Map, Cloneable, Externalizable
060    {
061    
062        /**
063         * {@link java.util.Map.Entry} that doubles as a node in the linked list of
064         * sequenced mappings.
065         */
066        private static class Entry implements Map.Entry, KeyValue
067        {
068            // Note: This class cannot easily be made clonable. While the actual
069            // implementation of a clone would be simple, defining the semantics is
070            // difficult. If a shallow clone is implemented, then entry.next.prev !=
071            // entry, which is unintuitive and probably breaks all sorts of
072            // assumptions
073            // in code that uses this implementation. If a deep clone is
074            // implemented, then what happens when the linked list is cyclical (as
075            // is
076            // the case with SequencedHashMap)? It's impossible to know in the clone
077            // when to stop cloning, and thus you end up in a recursive loop,
078            // continuously cloning the "next" in the list.
079    
080            private final Object key;
081    
082            private Object value;
083    
084            // package private to allow the SequencedHashMap to access and
085            // manipulate
086            // them.
087            Entry next = null;
088    
089            Entry prev = null;
090    
091    
092            public Entry(Object key, Object value)
093            {
094                this.key = key;
095                this.value = value;
096            }
097    
098    
099            // per Map.Entry.getKey()
100            public Object getKey()
101            {
102                return this.key;
103            }
104    
105    
106            // per Map.Entry.getValue()
107            public Object getValue()
108            {
109                return this.value;
110            }
111    
112    
113            // per Map.Entry.setValue()
114            public Object setValue( Object value )
115            {
116                Object oldValue = this.value;
117                this.value = value;
118                return oldValue;
119            }
120    
121    
122            /**
123             * Compute the instance's hash code
124             * @return the instance's hash code 
125             */
126            public int hashCode()
127            {
128                // implemented per api docs for Map.Entry.hashCode()
129                return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^ ( getValue() == null ? 0 : getValue().hashCode() ) );
130            }
131    
132    
133            public boolean equals( Object obj )
134            {
135                if ( obj == null )
136                    return false;
137                if ( obj == this )
138                    return true;
139                if ( !( obj instanceof Map.Entry ) )
140                    return false;
141    
142                Map.Entry other = ( Map.Entry ) obj;
143    
144                // implemented per api docs for Map.Entry.equals(Object)
145                return ( ( getKey() == null ? other.getKey() == null : getKey().equals( other.getKey() ) ) && ( getValue() == null ? other
146                    .getValue() == null
147                    : getValue().equals( other.getValue() ) ) );
148            }
149    
150    
151            public String toString()
152            {
153                return "[" + getKey() + "=" + getValue() + "]";
154            }
155        }
156    
157    
158        /**
159         * Construct an empty sentinel used to hold the head (sentinel.next) and the
160         * tail (sentinel.prev) of the list. The sentinel has a <code>null</code>
161         * key and value.
162         */
163        private static final Entry createSentinel()
164        {
165            Entry s = new Entry( null, null );
166            s.prev = s;
167            s.next = s;
168            return s;
169        }
170    
171        /**
172         * Sentinel used to hold the head and tail of the list of entries.
173         */
174        private Entry sentinel;
175    
176        /**
177         * Map of keys to entries
178         */
179        private HashMap entries;
180    
181        /**
182         * Holds the number of modifications that have occurred to the map,
183         * excluding modifications made through a collection view's iterator (e.g.
184         * entrySet().iterator().remove()). This is used to create a fail-fast
185         * behavior with the iterators.
186         */
187        private transient long modCount = 0;
188    
189    
190        /**
191         * Construct a new sequenced hash map with default initial size and load
192         * factor.
193         */
194        public SequencedHashMap()
195        {
196            sentinel = createSentinel();
197            entries = new HashMap();
198        }
199    
200    
201        /**
202         * Construct a new sequenced hash map with the specified initial size and
203         * default load factor.
204         * 
205         * @param initialSize
206         *            the initial size for the hash table
207         * @see HashMap#HashMap(int)
208         */
209        public SequencedHashMap(int initialSize)
210        {
211            sentinel = createSentinel();
212            entries = new HashMap( initialSize );
213        }
214    
215    
216        /**
217         * Construct a new sequenced hash map with the specified initial size and
218         * load factor.
219         * 
220         * @param initialSize
221         *            the initial size for the hash table
222         * @param loadFactor
223         *            the load factor for the hash table.
224         * @see HashMap#HashMap(int,float)
225         */
226        public SequencedHashMap(int initialSize, float loadFactor)
227        {
228            sentinel = createSentinel();
229            entries = new HashMap( initialSize, loadFactor );
230        }
231    
232    
233        /**
234         * Construct a new sequenced hash map and add all the elements in the
235         * specified map. The order in which the mappings in the specified map are
236         * added is defined by {@link #putAll(Map)}.
237         */
238        public SequencedHashMap(Map m)
239        {
240            this();
241            putAll( m );
242        }
243    
244    
245        /**
246         * Removes an internal entry from the linked list. This does not remove it
247         * from the underlying map.
248         */
249        private void removeEntry( Entry entry )
250        {
251            entry.next.prev = entry.prev;
252            entry.prev.next = entry.next;
253        }
254    
255    
256        /**
257         * Inserts a new internal entry to the tail of the linked list. This does
258         * not add the entry to the underlying map.
259         */
260        private void insertEntry( Entry entry )
261        {
262            entry.next = sentinel;
263            entry.prev = sentinel.prev;
264            sentinel.prev.next = entry;
265            sentinel.prev = entry;
266        }
267    
268    
269        // per Map.size()
270    
271        /**
272         * Implements {@link Map#size()}.
273         */
274        public int size()
275        {
276            // use the underlying Map's size since size is not maintained here.
277            return entries.size();
278        }
279    
280    
281        /**
282         * Implements {@link Map#isEmpty()}.
283         */
284        public boolean isEmpty()
285        {
286            // for quick check whether the map is entry, we can check the linked
287            // list
288            // and see if there's anything in it.
289            return sentinel.next == sentinel;
290        }
291    
292    
293        /**
294         * Implements {@link Map#containsKey(Object)}.
295         */
296        public boolean containsKey( Object key )
297        {
298            // pass on to underlying map implementation
299            return entries.containsKey( key );
300        }
301    
302    
303        /**
304         * Implements {@link Map#containsValue(Object)}.
305         */
306        public boolean containsValue( Object value )
307        {
308            // unfortunately, we cannot just pass this call to the underlying map
309            // because we are mapping keys to entries, not keys to values. The
310            // underlying map doesn't have an efficient implementation anyway, so
311            // this
312            // isn't a big deal.
313    
314            // do null comparison outside loop so we only need to do it once. This
315            // provides a tighter, more efficient loop at the expense of slight
316            // code duplication.
317            if ( value == null )
318            {
319                for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
320                {
321                    if ( pos.getValue() == null )
322                        return true;
323                }
324            }
325            else
326            {
327                for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
328                {
329                    if ( value.equals( pos.getValue() ) )
330                        return true;
331                }
332            }
333            return false;
334        }
335    
336    
337        /**
338         * Implements {@link Map#get(Object)}.
339         */
340        public Object get( Object o )
341        {
342            // find entry for the specified key object
343            Entry entry = ( Entry ) entries.get( o );
344            if ( entry == null )
345                return null;
346    
347            return entry.getValue();
348        }
349    
350    
351        /**
352         * Return the entry for the "oldest" mapping. That is, return the Map.Entry
353         * for the key-value pair that was first put into the map when compared to
354         * all the other pairings in the map. This behavior is equivalent to using
355         * <code>entrySet().iterator().next()</code>, but this method provides an
356         * optimized implementation.
357         * 
358         * @return The first entry in the sequence, or <code>null</code> if the
359         *         map is empty.
360         */
361        public Map.Entry getFirst()
362        {
363            // sentinel.next points to the "first" element of the sequence -- the
364            // head
365            // of the list, which is exactly the entry we need to return. We must
366            // test
367            // for an empty list though because we don't want to return the
368            // sentinel!
369            return ( isEmpty() ) ? null : sentinel.next;
370        }
371    
372    
373        /**
374         * Return the key for the "oldest" mapping. That is, return the key for the
375         * mapping that was first put into the map when compared to all the other
376         * objects in the map. This behavior is equivalent to using
377         * <code>getFirst().getKey()</code>, but this method provides a slightly
378         * optimized implementation.
379         * 
380         * @return The first key in the sequence, or <code>null</code> if the map
381         *         is empty.
382         */
383        public Object getFirstKey()
384        {
385            // sentinel.next points to the "first" element of the sequence -- the
386            // head
387            // of the list -- and the requisite key is returned from it. An empty
388            // list
389            // does not need to be tested. In cases where the list is empty,
390            // sentinel.next will point to the sentinel itself which has a null key,
391            // which is exactly what we would want to return if the list is empty (a
392            // nice convenient way to avoid test for an empty list)
393            return sentinel.next.getKey();
394        }
395    
396    
397        /**
398         * Return the value for the "oldest" mapping. That is, return the value for
399         * the mapping that was first put into the map when compared to all the
400         * other objects in the map. This behavior is equivalent to using
401         * <code>getFirst().getValue()</code>, but this method provides a
402         * slightly optimized implementation.
403         * 
404         * @return The first value in the sequence, or <code>null</code> if the
405         *         map is empty.
406         */
407        public Object getFirstValue()
408        {
409            // sentinel.next points to the "first" element of the sequence -- the
410            // head
411            // of the list -- and the requisite value is returned from it. An empty
412            // list does not need to be tested. In cases where the list is empty,
413            // sentinel.next will point to the sentinel itself which has a null
414            // value,
415            // which is exactly what we would want to return if the list is empty (a
416            // nice convenient way to avoid test for an empty list)
417            return sentinel.next.getValue();
418        }
419    
420    
421        /**
422         * Return the entry for the "newest" mapping. That is, return the Map.Entry
423         * for the key-value pair that was first put into the map when compared to
424         * all the other pairings in the map. The behavior is equivalent to:
425         * 
426         * <pre>
427         * Object obj = null;
428         * Iterator iter = entrySet().iterator();
429         * while ( iter.hasNext() )
430         * {
431         *     obj = iter.next();
432         * }
433         * return ( Map.Entry ) obj;
434         * </pre>
435         * 
436         * However, the implementation of this method ensures an O(1) lookup of the
437         * last key rather than O(n).
438         * 
439         * @return The last entry in the sequence, or <code>null</code> if the map
440         *         is empty.
441         */
442        public Map.Entry getLast()
443        {
444            // sentinel.prev points to the "last" element of the sequence -- the
445            // tail
446            // of the list, which is exactly the entry we need to return. We must
447            // test
448            // for an empty list though because we don't want to return the
449            // sentinel!
450            return ( isEmpty() ) ? null : sentinel.prev;
451        }
452    
453    
454        /**
455         * Return the key for the "newest" mapping. That is, return the key for the
456         * mapping that was last put into the map when compared to all the other
457         * objects in the map. This behavior is equivalent to using
458         * <code>getLast().getKey()</code>, but this method provides a slightly
459         * optimized implementation.
460         * 
461         * @return The last key in the sequence, or <code>null</code> if the map
462         *         is empty.
463         */
464        public Object getLastKey()
465        {
466            // sentinel.prev points to the "last" element of the sequence -- the
467            // tail
468            // of the list -- and the requisite key is returned from it. An empty
469            // list
470            // does not need to be tested. In cases where the list is empty,
471            // sentinel.prev will point to the sentinel itself which has a null key,
472            // which is exactly what we would want to return if the list is empty (a
473            // nice convenient way to avoid test for an empty list)
474            return sentinel.prev.getKey();
475        }
476    
477    
478        /**
479         * Return the value for the "newest" mapping. That is, return the value for
480         * the mapping that was last put into the map when compared to all the other
481         * objects in the map. This behavior is equivalent to using
482         * <code>getLast().getValue()</code>, but this method provides a slightly
483         * optimized implementation.
484         * 
485         * @return The last value in the sequence, or <code>null</code> if the map
486         *         is empty.
487         */
488        public Object getLastValue()
489        {
490            // sentinel.prev points to the "last" element of the sequence -- the
491            // tail
492            // of the list -- and the requisite value is returned from it. An empty
493            // list does not need to be tested. In cases where the list is empty,
494            // sentinel.prev will point to the sentinel itself which has a null
495            // value,
496            // which is exactly what we would want to return if the list is empty (a
497            // nice convenient way to avoid test for an empty list)
498            return sentinel.prev.getValue();
499        }
500    
501    
502        /**
503         * Implements {@link Map#put(Object, Object)}.
504         */
505        public Object put( Object key, Object value )
506        {
507            modCount++;
508    
509            Object oldValue = null;
510    
511            // lookup the entry for the specified key
512            Entry e = ( Entry ) entries.get( key );
513    
514            // check to see if it already exists
515            if ( e != null )
516            {
517                // remove from list so the entry gets "moved" to the end of list
518                removeEntry( e );
519    
520                // update value in map
521                oldValue = e.setValue( value );
522    
523                // Note: We do not update the key here because its unnecessary. We
524                // only
525                // do comparisons using equals(Object) and we know the specified key
526                // and
527                // that in the map are equal in that sense. This may cause a problem
528                // if
529                // someone does not implement their hashCode() and/or equals(Object)
530                // method properly and then use it as a key in this map.
531            }
532            else
533            {
534                // add new entry
535                e = new Entry( key, value );
536                entries.put( key, e );
537            }
538            // assert(entry in map, but not list)
539    
540            // add to list
541            insertEntry( e );
542    
543            return oldValue;
544        }
545    
546    
547        /**
548         * Implements {@link Map#remove(Object)}.
549         */
550        public Object remove( Object key )
551        {
552            Entry e = removeImpl( key );
553            return ( e == null ) ? null : e.getValue();
554        }
555    
556    
557        /**
558         * Fully remove an entry from the map, returning the old entry or null if
559         * there was no such entry with the specified key.
560         */
561        private Entry removeImpl( Object key )
562        {
563            Entry e = ( Entry ) entries.remove( key );
564            if ( e == null )
565                return null;
566            modCount++;
567            removeEntry( e );
568            return e;
569        }
570    
571    
572        /**
573         * Adds all the mappings in the specified map to this map, replacing any
574         * mappings that already exist (as per {@link Map#putAll(Map)}). The order
575         * in which the entries are added is determined by the iterator returned
576         * from {@link Map#entrySet()} for the specified map.
577         * 
578         * @param t
579         *            the mappings that should be added to this map.
580         * @throws NullPointerException
581         *             if <code>t</code> is <code>null</code>
582         */
583        public void putAll( Map t )
584        {
585            Iterator iter = t.entrySet().iterator();
586            while ( iter.hasNext() )
587            {
588                Map.Entry entry = ( Map.Entry ) iter.next();
589                put( entry.getKey(), entry.getValue() );
590            }
591        }
592    
593    
594        /**
595         * Implements {@link Map#clear()}.
596         */
597        public void clear()
598        {
599            modCount++;
600    
601            // remove all from the underlying map
602            entries.clear();
603    
604            // and the list
605            sentinel.next = sentinel;
606            sentinel.prev = sentinel;
607        }
608    
609    
610        /**
611         * Implements {@link Map#equals(Object)}.
612         */
613        public boolean equals( Object obj )
614        {
615            if ( obj == null )
616                return false;
617            if ( obj == this )
618                return true;
619    
620            if ( !( obj instanceof Map ) )
621                return false;
622    
623            return entrySet().equals( ( ( Map ) obj ).entrySet() );
624        }
625    
626    
627        /**
628         * Implements {@link Map#hashCode()}.
629         * @return the instance's hash code 
630         */
631        public int hashCode()
632        {
633            return entrySet().hashCode();
634        }
635    
636    
637        /**
638         * Provides a string representation of the entries within the map. The
639         * format of the returned string may change with different releases, so this
640         * method is suitable for debugging purposes only. If a specific format is
641         * required, use {@link #entrySet()}.{@link Set#iterator() iterator()} and
642         * iterate over the entries in the map formatting them as appropriate.
643         */
644        public String toString()
645        {
646            StringBuffer buf = new StringBuffer();
647            buf.append( '[' );
648            for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
649            {
650                buf.append( pos.getKey() );
651                buf.append( '=' );
652                buf.append( pos.getValue() );
653                if ( pos.next != sentinel )
654                {
655                    buf.append( ',' );
656                }
657            }
658            buf.append( ']' );
659    
660            return buf.toString();
661        }
662    
663    
664        /**
665         * Implements {@link Map#keySet()}.
666         */
667        public Set keySet()
668        {
669            return new AbstractSet()
670            {
671    
672                // required impls
673                public Iterator iterator()
674                {
675                    return new OrderedIterator( KEY );
676                }
677    
678    
679                public boolean remove( Object o )
680                {
681                    Entry e = SequencedHashMap.this.removeImpl( o );
682                    return ( e != null );
683                }
684    
685    
686                // more efficient impls than abstract set
687                public void clear()
688                {
689                    SequencedHashMap.this.clear();
690                }
691    
692    
693                public int size()
694                {
695                    return SequencedHashMap.this.size();
696                }
697    
698    
699                public boolean isEmpty()
700                {
701                    return SequencedHashMap.this.isEmpty();
702                }
703    
704    
705                public boolean contains( Object o )
706                {
707                    return SequencedHashMap.this.containsKey( o );
708                }
709    
710            };
711        }
712    
713    
714        /**
715         * Implements {@link Map#values()}.
716         */
717        public Collection values()
718        {
719            return new AbstractCollection()
720            {
721                // required impl
722                public Iterator iterator()
723                {
724                    return new OrderedIterator( VALUE );
725                }
726    
727    
728                public boolean remove( Object value )
729                {
730                    // do null comparison outside loop so we only need to do it
731                    // once. This
732                    // provides a tighter, more efficient loop at the expense of
733                    // slight
734                    // code duplication.
735                    if ( value == null )
736                    {
737                        for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
738                        {
739                            if ( pos.getValue() == null )
740                            {
741                                SequencedHashMap.this.removeImpl( pos.getKey() );
742                                return true;
743                            }
744                        }
745                    }
746                    else
747                    {
748                        for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
749                        {
750                            if ( value.equals( pos.getValue() ) )
751                            {
752                                SequencedHashMap.this.removeImpl( pos.getKey() );
753                                return true;
754                            }
755                        }
756                    }
757    
758                    return false;
759                }
760    
761    
762                // more efficient impls than abstract collection
763                public void clear()
764                {
765                    SequencedHashMap.this.clear();
766                }
767    
768    
769                public int size()
770                {
771                    return SequencedHashMap.this.size();
772                }
773    
774    
775                public boolean isEmpty()
776                {
777                    return SequencedHashMap.this.isEmpty();
778                }
779    
780    
781                public boolean contains( Object o )
782                {
783                    return SequencedHashMap.this.containsValue( o );
784                }
785            };
786        }
787    
788    
789        /**
790         * Implements {@link Map#entrySet()}.
791         */
792        public Set entrySet()
793        {
794            return new AbstractSet()
795            {
796                // helper
797                private Entry findEntry( Object o )
798                {
799                    if ( o == null )
800                        return null;
801                    if ( !( o instanceof Map.Entry ) )
802                        return null;
803    
804                    Map.Entry e = ( Map.Entry ) o;
805                    Entry entry = ( Entry ) entries.get( e.getKey() );
806                    if ( entry != null && entry.equals( e ) )
807                        return entry;
808                    else
809                        return null;
810                }
811    
812    
813                // required impl
814                public Iterator iterator()
815                {
816                    return new OrderedIterator( ENTRY );
817                }
818    
819    
820                public boolean remove( Object o )
821                {
822                    Entry e = findEntry( o );
823                    if ( e == null )
824                        return false;
825    
826                    return SequencedHashMap.this.removeImpl( e.getKey() ) != null;
827                }
828    
829    
830                // more efficient impls than abstract collection
831                public void clear()
832                {
833                    SequencedHashMap.this.clear();
834                }
835    
836    
837                public int size()
838                {
839                    return SequencedHashMap.this.size();
840                }
841    
842    
843                public boolean isEmpty()
844                {
845                    return SequencedHashMap.this.isEmpty();
846                }
847    
848    
849                public boolean contains( Object o )
850                {
851                    return findEntry( o ) != null;
852                }
853            };
854        }
855    
856        // constants to define what the iterator should return on "next"
857        private static final int KEY = 0;
858    
859        private static final int VALUE = 1;
860    
861        private static final int ENTRY = 2;
862    
863        private static final int REMOVED_MASK = 0x80000000;
864    
865        private class OrderedIterator implements Iterator
866        {
867            /**
868             * Holds the type that should be returned from the iterator. The value
869             * should be either KEY, VALUE, or ENTRY. To save a tiny bit of memory,
870             * this field is also used as a marker for when remove has been called
871             * on the current object to prevent a second remove on the same element.
872             * Essentially, if this value is negative (i.e. the bit specified by
873             * REMOVED_MASK is set), the current position has been removed. If
874             * positive, remove can still be called.
875             */
876            private int returnType;
877    
878            /**
879             * Holds the "current" position in the iterator. When pos.next is the
880             * sentinel, we've reached the end of the list.
881             */
882            private Entry pos = sentinel;
883    
884            /**
885             * Holds the expected modification count. If the actual modification
886             * count of the map differs from this value, then a concurrent
887             * modification has occurred.
888             */
889            private transient long expectedModCount = modCount;
890    
891    
892            /**
893             * Construct an iterator over the sequenced elements in the order in
894             * which they were added. The {@link #next()} method returns the type
895             * specified by <code>returnType</code> which must be either KEY,
896             * VALUE, or ENTRY.
897             */
898            public OrderedIterator(int returnType)
899            {
900                // // Since this is a private inner class, nothing else should have
901                // // access to the constructor. Since we know the rest of the outer
902                // // class uses the iterator correctly, we can leave of the
903                // following
904                // // check:
905                // if(returnType >= 0 && returnType <= 2) {
906                // throw new IllegalArgumentException("Invalid iterator type");
907                // }
908    
909                // Set the "removed" bit so that the iterator starts in a state
910                // where
911                // "next" must be called before "remove" will succeed.
912                this.returnType = returnType | REMOVED_MASK;
913            }
914    
915    
916            /**
917             * Returns whether there is any additional elements in the iterator to
918             * be returned.
919             * 
920             * @return <code>true</code> if there are more elements left to be
921             *         returned from the iterator; <code>false</code> otherwise.
922             */
923            public boolean hasNext()
924            {
925                return pos.next != sentinel;
926            }
927    
928    
929            /**
930             * Returns the next element from the iterator.
931             * 
932             * @return the next element from the iterator.
933             * @throws NoSuchElementException
934             *             if there are no more elements in the iterator.
935             * @throws ConcurrentModificationException
936             *             if a modification occurs in the underlying map.
937             */
938            public Object next()
939            {
940                if ( modCount != expectedModCount )
941                {
942                    throw new ConcurrentModificationException();
943                }
944                if ( pos.next == sentinel )
945                {
946                    throw new NoSuchElementException();
947                }
948    
949                // clear the "removed" flag
950                returnType = returnType & ~REMOVED_MASK;
951    
952                pos = pos.next;
953                switch ( returnType )
954                {
955                    case KEY:
956                        return pos.getKey();
957                    case VALUE:
958                        return pos.getValue();
959                    case ENTRY:
960                        return pos;
961                    default:
962                        // should never happen
963                        throw new Error( "bad iterator type: " + returnType );
964                }
965    
966            }
967    
968    
969            /**
970             * Removes the last element returned from the {@link #next()} method
971             * from the sequenced map.
972             * 
973             * @throws IllegalStateException
974             *             if there isn't a "last element" to be removed. That is,
975             *             if {@link #next()} has never been called, or if
976             *             {@link #remove()} was already called on the element.
977             * @throws ConcurrentModificationException
978             *             if a modification occurs in the underlying map.
979             */
980            public void remove()
981            {
982                if ( ( returnType & REMOVED_MASK ) != 0 )
983                {
984                    throw new IllegalStateException( "remove() must follow next()" );
985                }
986                if ( modCount != expectedModCount )
987                {
988                    throw new ConcurrentModificationException();
989                }
990    
991                SequencedHashMap.this.removeImpl( pos.getKey() );
992    
993                // update the expected mod count for the remove operation
994                expectedModCount++;
995    
996                // set the removed flag
997                returnType = returnType | REMOVED_MASK;
998            }
999        }
1000    
1001    
1002        // APIs maintained from previous version of SequencedHashMap for backwards
1003        // compatibility
1004    
1005        /**
1006         * Creates a shallow copy of this object, preserving the internal structure
1007         * by copying only references. The keys and values themselves are not
1008         * <code>clone()</code>'d. The cloned object maintains the same sequence.
1009         * 
1010         * @return A clone of this instance.
1011         * @throws CloneNotSupportedException
1012         *             if clone is not supported by a subclass.
1013         */
1014        public Object clone() throws CloneNotSupportedException
1015        {
1016            // yes, calling super.clone() silly since we're just blowing away all
1017            // the stuff that super might be doing anyway, but for motivations on
1018            // this, see:
1019            // http://www.javaworld.com/javaworld/jw-01-1999/jw-01-object.html
1020            SequencedHashMap map = ( SequencedHashMap ) super.clone();
1021    
1022            // create new, empty sentinel
1023            map.sentinel = createSentinel();
1024    
1025            // create a new, empty entry map
1026            // note: this does not preserve the initial capacity and load factor.
1027            map.entries = new HashMap();
1028    
1029            // add all the mappings
1030            map.putAll( this );
1031    
1032            // Note: We cannot just clone the hashmap and sentinel because we must
1033            // duplicate our internal structures. Cloning those two will not clone
1034            // all
1035            // the other entries they reference, and so the cloned hash map will not
1036            // be
1037            // able to maintain internal consistency because there are two objects
1038            // with
1039            // the same entries. See discussion in the Entry implementation on why
1040            // we
1041            // cannot implement a clone of the Entry (and thus why we need to
1042            // recreate
1043            // everything).
1044    
1045            return map;
1046        }
1047    
1048    
1049        /**
1050         * Returns the Map.Entry at the specified index
1051         * 
1052         * @throws ArrayIndexOutOfBoundsException
1053         *             if the specified index is <code>&lt; 0</code> or
1054         *             <code>&gt;</code> the size of the map.
1055         */
1056        private Map.Entry getEntry( int index )
1057        {
1058            Entry pos = sentinel;
1059    
1060            if ( index < 0 )
1061            {
1062                throw new ArrayIndexOutOfBoundsException( index + " < 0" );
1063            }
1064    
1065            // loop to one before the position
1066            int i = -1;
1067            while ( i < ( index - 1 ) && pos.next != sentinel )
1068            {
1069                i++;
1070                pos = pos.next;
1071            }
1072            // pos.next is the requested position
1073    
1074            // if sentinel is next, past end of list
1075            if ( pos.next == sentinel )
1076            {
1077                throw new ArrayIndexOutOfBoundsException( index + " >= " + ( i + 1 ) );
1078            }
1079    
1080            return pos.next;
1081        }
1082    
1083    
1084        /**
1085         * Gets the key at the specified index.
1086         * 
1087         * @param index
1088         *            the index to retrieve
1089         * @return the key at the specified index, or null
1090         * @throws ArrayIndexOutOfBoundsException
1091         *             if the <code>index</code> is <code>&lt; 0</code> or
1092         *             <code>&gt;</code> the size of the map.
1093         */
1094        public Object get( int index )
1095        {
1096            return getEntry( index ).getKey();
1097        }
1098    
1099    
1100        /**
1101         * Gets the value at the specified index.
1102         * 
1103         * @param index
1104         *            the index to retrieve
1105         * @return the value at the specified index, or null
1106         * @throws ArrayIndexOutOfBoundsException
1107         *             if the <code>index</code> is <code>&lt; 0</code> or
1108         *             <code>&gt;</code> the size of the map.
1109         */
1110        public Object getValue( int index )
1111        {
1112            return getEntry( index ).getValue();
1113        }
1114    
1115    
1116        /**
1117         * Gets the index of the specified key.
1118         * 
1119         * @param key
1120         *            the key to find the index of
1121         * @return the index, or -1 if not found
1122         */
1123        public int indexOf( Object key )
1124        {
1125            Entry e = ( Entry ) entries.get( key );
1126            if ( e == null )
1127            {
1128                return -1;
1129            }
1130            int pos = 0;
1131            while ( e.prev != sentinel )
1132            {
1133                pos++;
1134                e = e.prev;
1135            }
1136            return pos;
1137        }
1138    
1139    
1140        /**
1141         * Gets an iterator over the keys.
1142         * 
1143         * @return an iterator over the keys
1144         */
1145        public Iterator iterator()
1146        {
1147            return keySet().iterator();
1148        }
1149    
1150    
1151        /**
1152         * Gets the last index of the specified key.
1153         * 
1154         * @param key
1155         *            the key to find the index of
1156         * @return the index, or -1 if not found
1157         */
1158        public int lastIndexOf( Object key )
1159        {
1160            // keys in a map are guaranteed to be unique
1161            return indexOf( key );
1162        }
1163    
1164    
1165        /**
1166         * Returns a List view of the keys rather than a set view. The returned list
1167         * is unmodifiable. This is required because changes to the values of the
1168         * list (using {@link java.util.ListIterator#set(Object)}) will effectively
1169         * remove the value from the list and reinsert that value at the end of the
1170         * list, which is an unexpected side effect of changing the value of a list.
1171         * This occurs because changing the key, changes when the mapping is added
1172         * to the map and thus where it appears in the list.
1173         * <p>
1174         * An alternative to this method is to use {@link #keySet()}
1175         * 
1176         * @see #keySet()
1177         * @return The ordered list of keys.
1178         */
1179        public List sequence()
1180        {
1181            List l = new ArrayList( size() );
1182            Iterator iter = keySet().iterator();
1183            while ( iter.hasNext() )
1184            {
1185                l.add( iter.next() );
1186            }
1187    
1188            return Collections.unmodifiableList( l );
1189        }
1190    
1191    
1192        /**
1193         * Removes the element at the specified index.
1194         * 
1195         * @param index
1196         *            The index of the object to remove.
1197         * @return The previous value corresponding the <code>key</code>, or
1198         *         <code>null</code> if none existed.
1199         * @throws ArrayIndexOutOfBoundsException
1200         *             if the <code>index</code> is <code>&lt; 0</code> or
1201         *             <code>&gt;</code> the size of the map.
1202         */
1203        public Object remove( int index )
1204        {
1205            return remove( get( index ) );
1206        }
1207    
1208    
1209        // per Externalizable.readExternal(ObjectInput)
1210    
1211        /**
1212         * Deserializes this map from the given stream.
1213         * 
1214         * @param in
1215         *            the stream to deserialize from
1216         * @throws IOException
1217         *             if the stream raises it
1218         * @throws ClassNotFoundException
1219         *             if the stream raises it
1220         */
1221        public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1222        {
1223            int size = in.readInt();
1224            for ( int i = 0; i < size; i++ )
1225            {
1226                Object key = in.readObject();
1227                Object value = in.readObject();
1228                put( key, value );
1229            }
1230        }
1231    
1232    
1233        /**
1234         * Serializes this map to the given stream.
1235         * 
1236         * @param out
1237         *            the stream to serialize to
1238         * @throws IOException
1239         *             if the stream raises it
1240         */
1241        public void writeExternal( ObjectOutput out ) throws IOException
1242        {
1243            out.writeInt( size() );
1244            for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
1245            {
1246                out.writeObject( pos.getKey() );
1247                out.writeObject( pos.getValue() );
1248            }
1249        }
1250    
1251        // add a serial version uid, so that if we change things in the future
1252        // without changing the format, we can still deserialize properly.
1253        private static final long serialVersionUID = 3380552487888102930L;
1254    
1255    }