View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript-Util: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2005 Kevin Rogers
4   //
5   // This file is part of MillScript-Util.
6   //
7   // MillScript-Util is free software; you can redistribute it and/or modify it under
8   // the terms of the GNU General Public License as published by the Free
9   // Software Foundation; either version 2 of the License, or (at your option)
10  // any later version.
11  //
12  // MillScript-Util is distributed in the hope that it will be useful, but WITHOUT
13  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  // more details.
16  //
17  // You should have received a copy of the GNU General Public License along with
18  // MillScript-Util; if not, write to the Free Software Foundation, Inc., 59 Temple
19  // Place, Suite 330, Boston, MA  02111-1307  USA
20  ////////////////////////////////////////////////////////////////////////////////
21  package org.millscript.commons.util.map;
22  
23  import java.io.Serializable;
24  
25  import org.millscript.commons.alert.alerts.Fault;
26  import org.millscript.commons.util.IList;
27  import org.millscript.commons.util.IMap;
28  import org.millscript.commons.util.ListIterator;
29  import org.millscript.commons.util.MapIterator;
30  import org.millscript.commons.util.Maplet;
31  import org.millscript.commons.util.alerts.KeyAlreadyExistsAlert;
32  import org.millscript.commons.util.alerts.ListIndexOutOfBoundsAlert;
33  import org.millscript.commons.util.iterator.AbstractListIterator;
34  import org.millscript.commons.util.iterator.AbstractMapIterator;
35  import org.millscript.commons.util.iterator.ArrayListIterator;
36  import org.millscript.commons.util.list.AbstractIList;
37  import org.millscript.commons.util.list.IArrayList;
38  import org.millscript.commons.util.maplet.IMaplet;
39  
40  /**
41   * This class provides an immutable <code>Map</code> implementation that uses
42   * an array of hash code buckets to store the data.
43   */
44  public class ILinkedHashMap< K, V > extends AbstractIMap< K, V > implements Cloneable, Serializable {
45  
46      /**
47       * This is the ID from the release 0.1.0 for future compatibility.
48       */
49      private static final long serialVersionUID = 2779589907033092111L;
50  
51      /**
52       * This class implements a map iterator which iterates over an array
53       * of hash map buckets.
54       */
55      public static class LinkedHashMapIterator< K, V > extends AbstractMapIterator< K, V > {
56  
57          /**
58           * The maplet for the current point in the iteration.
59           */
60          private LinkedHashMaplet< K, V > currentMaplet;
61  
62          /**
63           * The next maplet in the iteration. If this is <code>null</code> there
64           * are no more elements in the iteration.
65           */
66          private LinkedHashMaplet< K, V > nextMaplet;
67  
68          /**
69           * Constructs a new hash map iterator to iterate over the specified
70           * array of hash map buckets.
71           *
72           * @param first the first element to be returned in the iteration
73           * @param share if <code>true</code> the specified object array will be
74           * shared otherwise the array will be copied.
75           */
76          public LinkedHashMapIterator( final LinkedHashMaplet< K, V > first, final boolean share ) {
77              if ( share ) {
78                  this.nextMaplet = first;
79              } else if ( first != null ) {
80                  this.nextMaplet = first.deepCopy();
81              }
82          }
83  
84          /**
85           * @see org.millscript.commons.util.iterator.AbstractMapIterator#advance()
86           */
87          @Override
88          protected void advance() {
89              this.currentMaplet = this.nextMaplet;
90              if ( this.nextMaplet != null ) {
91                  this.nextMaplet = this.nextMaplet.nextByInsertion;
92              }
93          }
94  
95          /**
96           * @see org.millscript.commons.util.iterator.AbstractMapIterator#getKey()
97           */
98          @Override
99          protected K getKey() {
100             return currentMaplet.getKey();
101         }
102 
103         /**
104          * @see org.millscript.commons.util.iterator.AbstractMapIterator#getValue()
105          */
106         @Override
107         protected V getValue() {
108             return currentMaplet.getValue();
109         }
110 
111         /**
112          * @see org.millscript.commons.util.MapIterator#hasNext()
113          */
114         public boolean hasNext() {
115             return this.nextMaplet != null;
116         }
117 
118         /**
119          * @see org.millscript.commons.util.iterator.AbstractMapIterator#outOfBounds()
120          */
121         @Override
122         protected boolean outOfBounds() {
123             return currentMaplet == null;
124         }
125 
126     }
127 
128     /**
129      * This class implements a map iterator which iterates over an array
130      * of hash map buckets, returning each maplet key.
131      */
132     public static class LinkedHashMapKeysIterator< K > extends LinkedHashMapXIterator< K > {
133 
134         /**
135          * Constructs a new hash map iterator to iterate over the specified
136          * array of hash map buckets returning the maplet keys.
137          *
138          * @param start the first LinkedHashMaplet in the iteration
139          * @param num   the number of elements in the iteration
140          */
141         public LinkedHashMapKeysIterator( final LinkedHashMaplet< ?, ? > start, final int num ) {
142             super( start, num );
143         }
144 
145         /**
146          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXIterator#getAppropriateMapletPart(org.millscript.commons.util.Maplet)
147          */
148         @Override
149         @SuppressWarnings( "unchecked" )
150         K getAppropriateMapletPart( final Maplet< ?, ? > maplet ) {
151             return (K) maplet.getKey();
152         }
153         
154     }
155 
156     /**
157      * This class implements an immutable list which is backed by an array of
158      * hash map buckets, but NOTE the values in the list are actually the
159      * individual hash maplet keys.
160      */
161     public static class LinkedHashMapKeysList< K > extends LinkedHashMapXList< K > {
162 
163         /**
164          * Constructs a new hash maplet array list of the individual maplet
165          * keys.
166          *
167          * @param start the LinkedHashMap element with index 1
168          * @param num   the number of elements in the list 
169          */
170         public LinkedHashMapKeysList( final LinkedHashMaplet< ?, ? > start, final int num ) {
171             super( start, num );
172         }
173 
174         /**
175          * Constructs a new hash maplet array list of the individual maplet
176          * keys.
177          *
178          * @param start the LinkedHashMap element with index 1
179          * @param first the index(one based, inclusive) of the first name
180          * @param last  the index(one based, inclusive) of the last name 
181          */
182         public LinkedHashMapKeysList( final LinkedHashMaplet< ?, ? > start, final int first, final int last ) {
183             super( start, first, last );
184         }
185 
186         /**
187          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXList#getAppropriateMapletPart(org.millscript.commons.util.Maplet)
188          */
189         @Override
190         @SuppressWarnings( "unchecked" )
191         K getAppropriateMapletPart( final Maplet< ?, ? > maplet ) {
192             return (K) maplet.getKey();
193         }
194 
195         /**
196          * @see org.millscript.commons.util.map.IHashMap.LinkedHashMapKeysIterator.LinkedHashMapXList#sharedIterator()
197          */
198         @Override
199         ListIterator< K > sharedIterator() {
200             return new LinkedHashMapKeysIterator< K >( this.firstHashMaplet, this.size );
201         }
202 
203         /**
204          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXList#sharedSlice(int, int)
205          */
206         @Override
207         IList< K > sharedSlice( final int first, final int last ) {
208             return new LinkedHashMapKeysList< K >( this.firstHashMaplet, first, last );
209         }
210 
211     }
212 
213     /**
214      * This class provides an immutable implementation of a <code>Maplet</code>
215      * which also acts as a bucket in our hashmap.
216      */
217     private static class LinkedHashMaplet< K, V > extends IMaplet< K, V > {
218 
219         /**
220          * This is the ID from the release 0.1.0 for future compatibility.
221          */
222         private static final long serialVersionUID = 7458320862201860770L;
223 
224         /**
225          * The next hash maplet in this chain.
226          */
227         LinkedHashMaplet< K, V > next;
228 
229         /**
230          * The next hash maplet in the insertion order.
231          */
232         LinkedHashMaplet< K, V > nextByInsertion;
233 
234         /**
235          * Constructs a new hash maplet with the given key and value.
236          *
237          * @param previous  the previous LinkedHashMaplet that was inserted
238          * into this map
239          * @param k the maplets key
240          * @param v the maplets value
241          */
242         private LinkedHashMaplet( final LinkedHashMaplet< K, V > previous, final K k, final V v ) {
243             super( k, v );
244             if ( previous != null ) {
245                 previous.nextByInsertion = this;
246             }
247         }
248 
249         /**
250          * Constructs a new hash maplet taking its key and value from the
251          * specified maplet.
252          *
253          * @param previous  the previous LinkedHashMaplet that was inserted
254          * into this map
255          * @param m the maplet to copy the key and value from
256          */
257         private LinkedHashMaplet( final LinkedHashMaplet< K, V > previous, final Maplet< K, V > m ) {
258             super( m );
259             if ( previous != null ) {
260                 previous.nextByInsertion = this;
261             }
262         }
263 
264         LinkedHashMaplet< K, V > deepCopy() {
265             return new LinkedHashMaplet< K, V >(
266                 this.nextByInsertion == null ? null : this.nextByInsertion.deepCopy(),
267                 this
268             );
269         }
270 
271     }
272 
273     /**
274      * This class implements a map iterator which iterates over an array
275      * of hash map buckets, returning each maplet as the value.
276      */
277     public static class LinkedHashMapMapletIterator< K, V > extends LinkedHashMapXIterator< Maplet< K, V > > {
278 
279         /**
280          * Constructs a new hash map iterator to iterate over the specified
281          * array of hash map buckets returning the maplet as the value.
282          *
283          * @param start the first LinkedHashMaplet in the iteration
284          * @param num   the number of elements in the iteration
285          */
286         public LinkedHashMapMapletIterator( final LinkedHashMaplet< ?, ? > start, final int num ) {
287             super( start, num );
288         }
289 
290         /**
291          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXIterator#getAppropriateMapletPart(org.millscript.commons.util.Maplet)
292          */
293         @Override
294         @SuppressWarnings( "unchecked" )
295         Maplet< K, V > getAppropriateMapletPart( final Maplet< ?, ? > maplet ) {
296             return (Maplet< K, V >) maplet;
297         }
298         
299     }
300 
301     /**
302      * This class implements an immutable list which is backed by an array of
303      * hash map buckets, but NOTE the values in the list are actually the
304      * individual hash maplets themselves.
305      */
306     public static class LinkedHashMapMapletList< K, V > extends LinkedHashMapXList< Maplet< K, V > > {
307 
308         /**
309          * Constructs a new hash maplet array list of the individual maplets.
310          *
311          * @param start the LinkedHashMap element with index 1
312          * @param num   the number of elements in the list 
313          */
314         public LinkedHashMapMapletList( final LinkedHashMaplet< ?, ? > start, final int num ) {
315             super( start, num );
316         }
317 
318         /**
319          * Constructs a new hash maplet array list of the individual maplets.
320          *
321          * @param start the LinkedHashMap element with index 1
322          * @param first the index(one based, inclusive) of the first name
323          * @param last  the index(one based, inclusive) of the last name 
324          */
325         public LinkedHashMapMapletList( final LinkedHashMaplet< ?, ? > start, final int first, final int last ) {
326             super( start, first, last );
327         }
328 
329         /**
330          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXList#getAppropriateMapletPart(org.millscript.commons.util.Maplet)
331          */
332         @Override
333         @SuppressWarnings( "unchecked" )
334         Maplet< K, V > getAppropriateMapletPart( final Maplet< ?, ? > maplet ) {
335             return (Maplet< K, V >) maplet;
336         }
337 
338         /**
339          * @see org.millscript.commons.util.map.IHashMap.LinkedHashMapKeysIterator.LinkedHashMapXList#sharedIterator()
340          */
341         @Override
342         ListIterator< Maplet< K, V > > sharedIterator() {
343             return new LinkedHashMapMapletIterator< K, V >( this.firstHashMaplet, this.size );
344         }
345 
346         /**
347          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXList#sharedSlice(int, int)
348          */
349         @Override
350         IList< Maplet< K, V > > sharedSlice( final int first, final int last ) {
351             return new LinkedHashMapMapletList< K, V >( this.firstHashMaplet, first, last );
352         }
353 
354     }
355 
356     /**
357      * This class implements a map iterator which iterates over an array
358      * of hash map buckets, returning each maplet value.
359      */
360     public static class LinkedHashMapValuesIterator< V > extends LinkedHashMapXIterator< V > {
361 
362         /**
363          * Constructs a new hash map iterator to iterate over the specified
364          * array of hash map buckets returning the maplet values.
365          *
366          * @param start the first LinkedHashMaplet in the iteration
367          * @param num   the number of elements in the iteration
368          */
369         public LinkedHashMapValuesIterator( final LinkedHashMaplet< ?, ? > start, final int num ) {
370             super( start, num );
371         }
372 
373         /**
374          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXIterator#getAppropriateMapletPart(org.millscript.commons.util.Maplet)
375          */
376         @Override
377         @SuppressWarnings( "unchecked" )
378         V getAppropriateMapletPart( final Maplet< ?, ? > maplet ) {
379             return (V) maplet.getValue();
380         }
381         
382     }
383 
384     /**
385      * This class implements an immutable list which is backed by an array of
386      * hash map buckets, but NOTE the values in the list are actually the
387      * individual hash maplet values.
388      */
389     public static class LinkedHashMapValuesList< V > extends LinkedHashMapXList< V > {
390 
391         /**
392          * Constructs a new hash maplet array list of the individual maplet
393          * values.
394          *
395          * @param start the LinkedHashMap element with index 1
396          * @param num   the number of elements in the list 
397          */
398         public LinkedHashMapValuesList( final LinkedHashMaplet< ?, ? > start, final int num ) {
399             super( start, num );
400         }
401 
402         /**
403          * Constructs a new hash maplet array list of the individual maplet
404          * values.
405          *
406          * @param start the LinkedHashMap element with index 1
407          * @param first the index(one based, inclusive) of the first name
408          * @param last  the index(one based, inclusive) of the last name 
409          */
410         public LinkedHashMapValuesList( final LinkedHashMaplet< ?, ? > start, final int first, final int last ) {
411             super( start, first, last );
412         }
413 
414         /**
415          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXList#getAppropriateMapletPart(org.millscript.commons.util.Maplet)
416          */
417         @Override
418         @SuppressWarnings( "unchecked" )
419         V getAppropriateMapletPart( final Maplet< ?, ? > maplet ) {
420             return (V) maplet.getValue();
421         }
422 
423         /**
424          * @see org.millscript.commons.util.map.IHashMap.LinkedHashMapKeysIterator.LinkedHashMapXList#sharedIterator()
425          */
426         @Override
427         ListIterator< V > sharedIterator() {
428             return new LinkedHashMapValuesIterator< V >( this.firstHashMaplet, this.size );
429         }
430 
431         /**
432          * @see org.millscript.commons.util.map.ILinkedHashMap.LinkedHashMapXList#sharedSlice(int, int)
433          */
434         @Override
435         IList< V > sharedSlice( final int first, final int last ) {
436             return new LinkedHashMapValuesList< V >( this.firstHashMaplet, first, last );
437         }
438 
439     }
440 
441     /**
442      * This class implements a map iterator which iterates over an array
443      * of hash map buckets, returning either the keys or values.
444      */
445     private static abstract class LinkedHashMapXIterator< V > extends AbstractListIterator< V > {
446 
447         /**
448          * The maplet for the current point in the iteration.
449          */
450         private LinkedHashMaplet< ?, ? > currentMaplet;
451 
452         /**
453          * The next maplet in the iteration. If this is <code>null</code> there
454          * are no more elements in the iteration.
455          */
456         private LinkedHashMaplet< ?, ? > nextMaplet;
457 
458         /**
459          * The number of elements this iterator will return.
460          */
461         private final int size;
462 
463         /**
464          * Constructs a new hash map iterator to iterate over the specified
465          * array of hash map buckets returning either the maplet keys or
466          * values.
467          *
468          * @param start the first LinkedHashMaplet in the iteration
469          * @param num   the number of elements in the iteration
470          */
471         public LinkedHashMapXIterator( final LinkedHashMaplet< ?, ? > start, final int num ) {
472             this.nextMaplet = start;
473             this.size = num;
474         }
475 
476         /**
477          * @see org.millscript.commons.util.iterator.AbstractMapIterator#advance()
478          */
479         @Override
480         protected void advance() {
481             // Increment the position
482             super.advance();
483             this.currentMaplet = this.nextMaplet;
484             if ( super.position >= this.size ) {
485                 this.nextMaplet = null;
486             } else if ( this.nextMaplet != null ) {
487                 this.nextMaplet = this.nextMaplet.nextByInsertion;
488             }
489         }
490 
491         /**
492          * Returns the appropriate part of the specified maplet for this list.
493          *
494          * @param maplet    the maplet to get the appropriate part from
495          * @return  the appropriate part of the specified maplet
496          */
497         abstract V getAppropriateMapletPart( Maplet< ?, ? > maplet );
498 
499         /**
500          * @see org.millscript.commons.util.iterator.AbstractMapIterator#getValue()
501          */
502         @Override
503         protected V getValue() {
504             return this.getAppropriateMapletPart( this.currentMaplet );
505         }
506 
507         /**
508          * @see org.millscript.commons.util.MapIterator#hasNext()
509          */
510         public boolean hasNext() {
511             return this.nextMaplet != null;
512         }
513 
514         /**
515          * @see org.millscript.commons.util.iterator.AbstractMapIterator#outOfBounds()
516          */
517         @Override
518         protected boolean outOfBounds() {
519             return this.currentMaplet == null;
520         }
521 
522     }
523 
524     /**
525      * The class provides the skeletal implementation of a list backed by an
526      * array of hash map buckets, where the list values are either the maplet
527      * keys or values.
528      */
529     private static abstract class LinkedHashMapXList< V > extends AbstractIList< V > {
530 
531         /**
532          * The first link in the shared part of the maplet chain.
533          */
534         final LinkedHashMaplet< ?, ? > firstHashMaplet;
535 
536         /**
537          * The number of values in the list.
538          */
539         final int size;
540 
541         /**
542          * Constructs a new hash maplet array list of either the individual
543          * maplet keys or values.
544          *
545          * @param start the LinkedHashMap element with index 1
546          * @param num   the number of elements in the list 
547          */
548         private LinkedHashMapXList( final LinkedHashMaplet< ?, ? > start, final int num ) {
549             this.firstHashMaplet = start;
550             this.size = num;
551         }
552 
553         /**
554          * Constructs a new hash maplet array list of either the individual
555          * maplet keys or values.
556          *
557          * @param start the LinkedHashMap element with index 1
558          * @param first the index(one based, inclusive) of the first name
559          * @param last  the index(one based, inclusive) of the last name 
560          */
561         private LinkedHashMapXList( final LinkedHashMaplet< ?, ? > start, final int first, final int last ) {
562             LinkedHashMaplet< ?, ? > firstMaplet = null;
563             int totalMaplets = 1;
564             // Find the first link in the shared chain
565             for ( LinkedHashMaplet< ?, ? > maplet = start; maplet != null; totalMaplets++, maplet = maplet.nextByInsertion ) {
566                 if ( totalMaplets == first ) {
567                     firstMaplet = maplet;
568                 }
569             }
570             if ( first > last ) {
571                 this.firstHashMaplet = null;
572                 this.size = 0;
573             } else if ( first < 1 || first > totalMaplets ) {
574                 throw new ListIndexOutOfBoundsAlert(
575                     "First index in slice must be between 1 and the number of entries in the map"
576                 ).culprit(
577                     "index",
578                     first
579                 ).decorate( start ).mishap();
580             } else if ( last > totalMaplets ) {
581                 throw new ListIndexOutOfBoundsAlert(
582                     "Last index in slice must not be greater than the number of entries in the list"
583                 ).culprit(
584                     "index",
585                     last
586                 ).decorate( start ).mishap();
587             } else {
588                 this.firstHashMaplet = firstMaplet;
589                 this.size = last - first + 1;
590             }
591         }
592 
593         /**
594          * @see org.millscript.commons.util.list.AbstractIList#doGet(int)
595          */
596         @Override
597         protected V doGet( final int pos ) {
598             int currentPos = 1;
599             for ( LinkedHashMaplet< ?, ? > maplet = this.firstHashMaplet; maplet != null; maplet = maplet.nextByInsertion ) {
600                 if ( pos == currentPos++ ) {
601                     return this.getAppropriateMapletPart( maplet );
602                 }
603             }
604             throw new Fault(
605                 "Specified position is neither out of bounds or within the list!"
606             ).culprit( "index", pos ).decorate( this ).mishap();
607         }
608 
609         /**
610          * @see org.millscript.commons.util.list.AbstractIList#doSlice(int, int, boolean)
611          */
612         @Override
613         @SuppressWarnings( "unchecked" )
614         protected IList< V > doSlice( final int first, final int last, final boolean share ) {
615             if ( share ) {
616                 return this.sharedSlice( first, last );
617             } else {
618                 // Return a copy of the slice of the list.
619                 final V[] objects = (V[]) new Object[ this.size() ];
620                 LinkedHashMaplet< ?, ? > hmp = this.firstHashMaplet;
621                 int outerListCount = 1;
622                 // Find the first link in the shared chain
623                 for ( ; outerListCount <= first; outerListCount++ ) {
624                     hmp = hmp.next;
625                 }
626                 // Now copy each required link into the array
627                 int newArrayPos = 0;
628                 for ( ; hmp != null && outerListCount <= last; hmp = hmp.next, outerListCount++ ) {
629                     objects[ newArrayPos++ ] = this.getAppropriateMapletPart( hmp );
630                 }
631                 return new IArrayList< V >( objects, true );
632             }
633         }
634 
635         /**
636          * Returns the appropriate part of the specified maplet for this list.
637          *
638          * @param maplet    the maplet to get the appropriate part from
639          * @return  the appropriate part of the specified maplet
640          */
641         abstract V getAppropriateMapletPart( Maplet< ?, ? > maplet );
642 
643         /**
644          * @see org.millscript.commons.util.IList#indexOf(java.lang.Object)
645          */
646         public int indexOf( final V value ) {
647             int pos = 1;
648             LinkedHashMaplet< ?, ? > hmp = this.firstHashMaplet;
649             if ( value == null ) {
650                 for ( ; hmp != null && pos <= this.size; hmp = hmp.nextByInsertion, pos++ ) {
651                     if ( this.getAppropriateMapletPart( hmp ) == null ) {
652                         return pos;
653                     }
654                 }
655             } else {
656                 for ( ; hmp != null && pos <= this.size; hmp = hmp.nextByInsertion, pos++ ) {
657                     if ( value.equals( this.getAppropriateMapletPart( hmp ) ) ) {
658                         return pos;
659                     }
660                 }
661             }
662             return 0;
663         }
664 
665         /**
666          * @see org.millscript.commons.util.IMap#iterator(boolean)
667          */
668         @SuppressWarnings( "unchecked" )
669         public ListIterator< V > iterator( final boolean share ) {
670             if ( share ) {
671                 return this.sharedIterator();
672             } else {
673                 // We can do the generic copy here, as we can always access the
674                 // appropriate part of each maplet
675                 final V[] objects = (V[]) new Object[ this.size() ];
676                 int pos = 0;
677                 LinkedHashMaplet< ?, ? > current = this.firstHashMaplet;
678                 while ( current != null ) {
679                     objects[ pos++ ] = this.getAppropriateMapletPart( current );
680                     current = current.nextByInsertion;
681                 }
682                 return new ArrayListIterator< V >( objects, true );
683             }
684         }
685 
686         /**
687          * Returns a list iterator which shares backing store with this list.
688          *
689          * @return  a list iterator which shares this lists backing store
690          */
691         abstract ListIterator< V > sharedIterator();
692 
693         /**
694          * Returns a slice of this list which shares backing store with this
695          * list.
696          *
697          * @param first the index(one based) of the first element in the slice
698          * @param last  the index(one based) of the last element in the slice
699          * @return  a slice of this list which shares this lists backing store
700          */
701         abstract IList< V > sharedSlice( int first, int last );
702 
703         /**
704          * @see org.millscript.commons.util.IMap#size()
705          */
706         public int size() {
707             return this.size;
708         }
709         
710     }
711 
712     /**
713      * The first LinkedHashMaplet in the insertion order chain.
714      */
715     private final LinkedHashMaplet< K, V > firstElement;
716 
717     /**
718      * The number of maplets in this mapping.
719      */
720     private final int size;
721 
722     /**
723      * The backing store for this hash map is a fixed array of LinkedHashMaplet
724      * buckets.
725      */
726     private final LinkedHashMaplet< K, V >[] store;
727 
728     /**
729      * Constructs a new, emtpy immutable hash map.
730      */
731     public ILinkedHashMap() {
732         this.firstElement = null;
733         this.size = 0;
734         this.store = null;
735     }
736 
737     /**
738      * Constructs a new immutable hash map which contains all the mappings from
739      * the specified map.
740      *
741      * @param map   the <code>Map</code> to copy mappings from
742      */
743     @SuppressWarnings( "unchecked" )
744     public ILinkedHashMap( final IMap< K, V > map ) {
745         if ( map.size() == 0 ) {
746             this.firstElement = null;
747             this.size = 0;
748             this.store = null;
749         } else {
750             final MapIterator< K, V > it = map.iterator( true );
751             int num = 1;
752             this.store = new LinkedHashMaplet[ map.size() ];
753             this.firstElement = new LinkedHashMaplet< K, V >( null, it.currentKey(), it.currentValue() );
754             LinkedHashMaplet< K, V > lastInserted = this.firstElement;
755             this.store[ it.nextKey() == null ? 0 : ( it.currentKey().hashCode() & 0x7FFFFFFF ) % this.store.length ] = lastInserted;
756             while ( it.hasNext() ) {
757                 final int pos = it.nextKey() == null ? 0 : ( it.currentKey().hashCode() & 0x7FFFFFFF ) % this.store.length;
758                 if ( this.store[ pos ] == null ) {
759                     lastInserted = new LinkedHashMaplet< K, V >( lastInserted, it.currentKey(), it.currentValue() );
760                     this.store[ pos ] = lastInserted;
761                 } else {
762                     for ( LinkedHashMaplet< K, V > hmp = this.store[ pos ]; hmp != null; hmp = hmp.next ) {
763                         if ( hmp.getKey() == null ? it.currentKey() == null : hmp.getKey().equals( it.currentKey() ) ) {
764                             throw new KeyAlreadyExistsAlert(
765                                 "Specified key is already present in this map"
766                             ).culprit(
767                                 "existing maplet",
768                                 hmp
769                             ).culprit(
770                                 "duplicate maplet",
771                                 it.currentMaplet()
772                             ).mishap();
773                         } else if ( hmp.next == null ) {
774                             lastInserted = new LinkedHashMaplet< K, V >( lastInserted, it.currentKey(), it.currentValue() );
775                             hmp.next = lastInserted;
776                             break;
777                         }
778                     }
779                 }
780                 num++;
781             }
782             this.size = num;
783         }
784     }
785 
786     /**
787      * @see java.lang.Object#clone()
788      */
789     @Override
790     public Object clone() throws CloneNotSupportedException {
791         // Nothing special required for this clone
792         return super.clone();
793     }
794 
795     /**
796      * @see org.millscript.commons.util.IMap#contains(java.lang.Object, java.lang.Object)
797      */
798     public boolean contains( final K key, final V value ) {
799         final int pos = key == null ? 0 : ( key.hashCode() & 0x7FFFFFFF ) % this.store.length;
800         for ( LinkedHashMaplet< K, V > hmp = this.store[ pos ]; hmp != null; hmp = hmp.next ) {
801             if ( key == null ? hmp.getKey() == null : key.equals( hmp.getKey() ) ) {
802                 // The keys are equal, so return true if the values are both
803                 // null or equal and false otherwise
804                 return hmp.getValue() == null ? value == null
805                                               : hmp.getValue().equals( value );
806             }
807         }
808         return false;
809     }
810 
811     /**
812      * @see org.millscript.commons.util.IMap#containsKey(java.lang.Object)
813      */
814     public boolean containsKey( final K key ) {
815         final int pos = key == null ? 0 : ( key.hashCode() & 0x7FFFFFFF ) % this.store.length;
816         for ( LinkedHashMaplet< K, V > hmp = this.store[ pos ]; hmp != null; hmp = hmp.next ) {
817             if ( key == null ? hmp.getKey() == null : key.equals( hmp.getKey() ) ) {
818                 return true;
819             }
820         }
821         return false;
822     }
823 
824     /**
825      * @see org.millscript.commons.util.IMap#containsValue(java.lang.Object)
826      */
827     public boolean containsValue( final V value ) {
828         // We can't do better than a linear search
829         LinkedHashMaplet< K, V > current = this.firstElement;
830         while ( current != null ) {
831             if ( value == null ? current.getValue() == null : value.equals( current.getValue() ) ) {
832                 return true;
833             } else {
834                 current = current.nextByInsertion;
835             }
836         }
837         return false;
838     }
839 
840     /**
841      * @see org.millscript.commons.util.IMap#get(java.lang.Object)
842      */
843     public V get( final K key ) {
844         final int pos = key == null ? 0 : ( key.hashCode() & 0x7FFFFFFF ) % this.store.length;
845         for ( LinkedHashMaplet< K, V > hmp = this.store[ pos ]; hmp != null; hmp = hmp.next ) {
846             if ( key == null ? hmp.getKey() == null : key.equals( hmp.getKey() ) ) {
847                 return hmp.getValue();
848             }
849         }
850         return this.getDefault().get( this, key );
851     }
852 
853     /**
854      * @see org.millscript.commons.util.IMap#isEmtpy()
855      */
856     @Override
857     public boolean isEmtpy() {
858         return this.firstElement != null;
859     }
860 
861     /**
862      * @see org.millscript.commons.util.IMap#iterator(boolean)
863      */
864     @SuppressWarnings( "unchecked" )
865     public MapIterator< K, V > iterator( final boolean share ) {
866         if ( share ) {
867             return new LinkedHashMapIterator< K, V >( this.firstElement, share );
868         } else {
869             // It's more efficient to copy the hash map backing store into a
870             // fixed array of plain maplets, rather than copying the array of
871             // buckets.
872             final Maplet< K, V >[] maplets = new Maplet[ this.size() ];
873             LinkedHashMaplet< K, V > current = this.firstElement;
874             for ( int i = 0; current != null; current = current.nextByInsertion ) {
875                 maplets[ i++ ] = new IMaplet< K, V >( current );
876             }
877             return new IMapletArrayMap.MapletArrayMapIterator< K, V >( maplets, true );
878         }
879     }
880 
881     /**
882      * @see org.millscript.commons.util.map.AbstractIMap#sharedKeyList()
883      */
884     @Override
885     protected IList< K > sharedKeyList() {
886         return new LinkedHashMapKeysList< K >( this.firstElement, this.size() );
887     }
888 
889     /**
890      * @see org.millscript.commons.util.map.AbstractIMap#sharedMapletList()
891      */
892     @Override
893     protected IList< Maplet< K, V > > sharedMapletList() {
894         return new LinkedHashMapMapletList< K, V >( this.firstElement, this.size() );
895     }
896 
897     /**
898      * @see org.millscript.commons.util.map.AbstractIMap#sharedValueList()
899      */
900     @Override
901     protected IList< V > sharedValueList() {
902         return new LinkedHashMapValuesList< V >( this.firstElement, this.size() );
903     }
904 
905     /**
906      * @see org.millscript.commons.util.IMap#size()
907      */
908     public int size() {
909         return this.size;
910     }
911 
912 }