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.IllegalArgumentAlert;
26  import org.millscript.commons.util.IList;
27  import org.millscript.commons.util.ListIterator;
28  import org.millscript.commons.util.MapIterator;
29  import org.millscript.commons.util.Maplet;
30  import org.millscript.commons.util.iterator.AbstractListIterator;
31  import org.millscript.commons.util.iterator.AbstractMapIterator;
32  import org.millscript.commons.util.list.AbstractIList;
33  import org.millscript.commons.util.list.IArrayList;
34  import org.millscript.commons.util.maplet.IMaplet;
35  
36  /**
37   * This class provides an immutable <code>Map</code> implementation which is
38   * backed by two Object arrays, one containing names, the other the matching
39   * values.
40   */
41  public class ITwoArrayMap< K, V > extends AbstractIMap< K, V > implements Cloneable, Serializable {
42  
43      /**
44       * This is the ID from the release 0.1.0 for future compatibility.
45       */
46      private static final long serialVersionUID = 1664870800915663382L;
47  
48      /**
49       * This class provides a map interator implementation which iterates over a
50       * slice of a shared object array.
51       */
52      public static class SharedMapletListIterator< K, V > extends AbstractListIterator< Maplet< K, V > > {
53  
54          /**
55           * The index of the first element in the iteration.
56           */
57          private final int firstIndex;
58  
59          /**
60           * The backing store containing a sequence of name objects matching the
61           * values.
62           */
63          private final K[] keys;
64  
65          /**
66           * The number of elements in the iteration.
67           */
68          private final int size;
69  
70          /**
71           * The backing store containing a sequence of value objects matching the
72           * names.
73           */
74          private final V[] values;
75  
76          /**
77           * Constructs a new shared array map iterator to iterate over a slice of
78           * the specified object arrays.
79           *
80           * @param k the array of keys
81           * @param v the array of values
82           * @param start the index(one based, inclusive) to start iterating from
83           * @param end   the index(one based, inclusive) to stop iterating at
84           */
85          public SharedMapletListIterator( final K[] k, final V[] v, final int start, final int end ) {
86              this.firstIndex = start - 1;
87              this.size = end - start + 1;
88              this.keys = k;
89              this.values = v;
90          }
91  
92          /**
93           * @see org.millscript.commons.util.iterator.AbstractMapIterator#getValue()
94           */
95          @Override
96          protected Maplet< K, V > getValue() {
97              return new IMaplet< K, V >(
98                  this.keys[ this.firstIndex + super.position - 1 ],
99                  this.values[ this.firstIndex + super.position - 1 ]
100             );
101         }
102 
103         /**
104          * @see org.millscript.commons.util.MapIterator#hasNext()
105          */
106         public boolean hasNext() {
107             return super.position < this.size;
108         }
109 
110         /**
111          * @see org.millscript.commons.util.iterator.AbstractMapIterator#outOfBounds()
112          */
113         @Override
114         protected boolean outOfBounds() {
115             return super.position == 0 || super.position > this.size;
116         }
117 
118     }
119 
120     public static class SharedTwoArrayMapletList< K, V > extends AbstractIList< Maplet< K, V > > {
121 
122         /**
123          * The index of the first element in the iteration.
124          */
125         private final int firstIndex;
126 
127         /**
128          * The backing store containing a sequence of name objects matching the
129          * values.
130          */
131         private final K[] keys;
132 
133         /**
134          * The index of the last element in the iteration.
135          */
136         private final int lastIndex;
137 
138         /**
139          * The backing store containing a sequence of value objects matching the
140          * names.
141          */
142         private final V[] values;
143 
144         /**
145          * Constructs a new array iterator to iterate over the alternating
146          * key-value objects in the specified array.
147          *
148          * @param k the array of keys
149          * @param v the array of values
150          * @param start the index(one based, inclusive) to start iterating from
151          * @param end   the index(one based, inclusive) to stop iterating at
152          */
153         public SharedTwoArrayMapletList( final K[] k, final V[] v, final int start, final int end ) {
154             // Validate the incoming arrays
155             if ( k.length != v.length ) {
156                 throw new IllegalArgumentAlert(
157                     "Backing store arrays must be the same length"
158                 ).culprit(
159                     "keys array",
160                     k
161                 ).culprit(
162                     "keys array length",
163                     k.length
164                 ).culprit(
165                     "values array",
166                     v
167                 ).culprit(
168                     "values array length",
169                     v.length
170                 ).mishap();
171             }
172             // Setup the backing store
173             this.firstIndex = start;
174             this.lastIndex = end;
175             this.keys = k;
176             this.values = v;
177         }
178 
179         /**
180          * @see org.millscript.commons.util.list.AbstractIList#doGet(int)
181          */
182         @Override
183         protected Maplet< K, V > doGet( final int pos ) {
184             return new IMaplet< K, V >( this.keys[ pos - 1 ], this.values[ pos - 1 ] );
185         }
186 
187         /**
188          * @see org.millscript.commons.util.list.AbstractIList#doSlice(int, int, boolean)
189          */
190         @Override
191         @SuppressWarnings( "unchecked" )
192         protected IList< Maplet< K, V > > doSlice( final int first, final int last, final boolean share ) {
193             if ( share ) {
194                 return new SharedTwoArrayMapletList< K, V >(
195                     this.keys,
196                     this.values,
197                     this.firstIndex + first - 1,
198                     this.lastIndex + last - 1
199                 );
200             } else {
201                 final K[] ks = (K[]) new Object[ this.size() ];
202                 System.arraycopy( this.keys, this.firstIndex - 1, ks, 0, ks.length );
203                 final V[] vs = (V[]) new Object[ this.size() ];
204                 System.arraycopy( this.values, this.firstIndex - 1, vs, 0, vs.length );
205                 return new TwoArrayMapletList< K, V >( ks, vs, true );
206             }
207         }
208 
209         /**
210          * @see org.millscript.commons.util.IList#indexOf(java.lang.Object)
211          */
212         public int indexOf( final Maplet< K, V > value ) {
213             if ( value != null ) {
214                 final Object key = value.getKey();
215                 for ( int i = this.firstIndex - 1; i < this.size(); i++ ) {
216                     if ( key == null ? this.keys[ i ] == null : key.equals( this.keys[ i ] ) ) {
217                         final Object val = value.getValue();
218                         if ( val == null ? this.values[ i ] == null : val.equals( this.values[ i ] ) ) {
219                             return i + 1;
220                         }
221                     }
222                 }
223             }
224             return 0;
225         }
226 
227         /**
228          * @see org.millscript.commons.util.IMap#iterator(boolean)
229          */
230         @SuppressWarnings( "unchecked" )
231         public ListIterator< Maplet< K, V > > iterator( final boolean share ) {
232             if ( share ) {
233                 return new SharedMapletListIterator< K, V >( this.keys, this.values, 1, this.size() );
234             } else {
235                 final K[] ks = (K[]) new Object[ this.size() ];
236                 System.arraycopy( this.keys, this.firstIndex - 1, ks, 0, ks.length );
237                 final V[] vs = (V[]) new Object[ this.size() ];
238                 System.arraycopy( this.values, this.firstIndex - 1, vs, 0, vs.length );
239                 return new MapletListIterator< K, V >( ks, vs, true );
240             }
241         }
242 
243         /**
244          * @see org.millscript.commons.util.IMap#size()
245          */
246         public int size() {
247             return this.lastIndex - this.firstIndex + 1;
248         }
249         
250     }
251 
252     /**
253      * This class provides a map interator implementation which iterates over a
254      * slice of a shared object array.
255      */
256     public static class MapletListIterator< K, V > extends AbstractListIterator< Maplet< K, V > > {
257 
258         /**
259          * The backing store containing a sequence of name objects matching the
260          * values.
261          */
262         private final K[] keys;
263 
264         /**
265          * The backing store containing a sequence of value objects matching the
266          * names.
267          */
268         private final V[] values;
269 
270         /**
271          * Constructs a new shared array map iterator to iterate over a slice of
272          * the specified object arrays.
273          *
274          * @param k the array of keys
275          * @param v the array of values
276          * @param share if <code>true</code> the specified object array will be
277          * shared otherwise the array will be copied.
278          */
279         @SuppressWarnings( "unchecked" )
280         public MapletListIterator( final K[] k, final V[] v, final boolean share ) {
281             if ( share ) {
282                 this.keys = k;
283                 this.values = v;
284             } else {
285                 this.keys = (K[]) new Object[ k.length ];
286                 System.arraycopy( k, 0, this.keys, 0, k.length );
287                 this.values = (V[]) new Object[ v.length ];
288                 System.arraycopy( v, 0, this.values, 0, v.length );
289             }
290         }
291 
292         /**
293          * @see org.millscript.commons.util.iterator.AbstractMapIterator#getValue()
294          */
295         @Override
296         protected Maplet< K, V > getValue() {
297             return new IMaplet< K, V >(
298                 this.keys[ super.position - 1 ],
299                 this.values[ super.position - 1 ]
300             );
301         }
302 
303         /**
304          * @see org.millscript.commons.util.MapIterator#hasNext()
305          */
306         public boolean hasNext() {
307             return super.position < this.keys.length;
308         }
309 
310         /**
311          * @see org.millscript.commons.util.iterator.AbstractMapIterator#outOfBounds()
312          */
313         @Override
314         protected boolean outOfBounds() {
315             return super.position == 0 || super.position > this.keys.length;
316         }
317 
318     }
319 
320     /**
321      * This class implements a map iterator which iterates over two arrays,
322      * where one contains all the keys and the other all the values.
323      */
324     public static class TwoArrayMapIterator< K, V > extends AbstractMapIterator< K, V > {
325 
326         /**
327          * The backing store containing a sequence of name objects matching the
328          * values.
329          */
330         private final K[] keys;
331 
332         /**
333          * The current position of the iterator within the array.
334          */
335         private int pos = -1;
336 
337         /**
338          * The backing store containing a sequence of value objects matching the
339          * names.
340          */
341         private final V[] values;
342 
343         /**
344          * Constructs a new array iterator to iterate over the alternating
345          * key-value objects in the specified array.
346          *
347          * @param k the array of keys
348          * @param v the array of values
349          * @param share if <code>true</code> the specified object array will be
350          * shared otherwise the array will be copied.
351          */
352         @SuppressWarnings( "unchecked" )
353         public TwoArrayMapIterator( final K[] k, final V[] v, final boolean share ) {
354             // Validate the incoming arrays
355             if ( k == null && v == null ) {
356                 this.keys = null;
357                 this.values = null;
358             } else if ( k == null || v == null ) {
359                 throw new IllegalArgumentAlert(
360                     "Backing store arrays should both be null or not null"
361                 ).culprit(
362                     "keys array",
363                     k
364                 ).culprit(
365                     "values array",
366                     v
367                 ).mishap();
368             } else if ( k.length != v.length ) {
369                 throw new IllegalArgumentAlert(
370                     "Backing store arrays must be the same length"
371                 ).culprit(
372                     "keys array",
373                     k
374                 ).culprit(
375                     "keys array length",
376                     k.length
377                 ).culprit(
378                     "values array",
379                     v
380                 ).culprit(
381                     "values array length",
382                     v.length
383                 ).mishap();
384             } else if ( share ) {
385                 this.keys = k;
386                 this.values = v;
387             } else {
388                 this.keys = (K[]) new Object[ k.length ];
389                 System.arraycopy( k, 0, this.keys, 0, k.length );
390                 this.values = (V[]) new Object[ v.length ];
391                 System.arraycopy( v, 0, this.values, 0, v.length );
392             }
393         }
394 
395         /**
396          * @see org.millscript.commons.util.iterator.AbstractMapIterator#advance()
397          */
398         @Override
399         protected void advance() {
400             if ( this.keys != null ) {
401                 this.pos++;
402             }
403         }
404 
405         /**
406          * @see org.millscript.commons.util.iterator.AbstractMapIterator#getKey()
407          */
408         @Override
409         protected K getKey() {
410             return this.keys[ this.pos ];
411         }
412 
413         /**
414          * @see org.millscript.commons.util.iterator.AbstractMapIterator#getValue()
415          */
416         @Override
417         protected V getValue() {
418             return this.values[ this.pos ];
419         }
420 
421         /**
422          * @see org.millscript.commons.util.MapIterator#hasNext()
423          */
424         public boolean hasNext() {
425             return this.keys != null && this.pos + 1 < keys.length;
426         }
427 
428         /**
429          * @see org.millscript.commons.util.iterator.AbstractMapIterator#outOfBounds()
430          */
431         @Override
432         protected boolean outOfBounds() {
433             return this.keys == null || this.pos < 0 || this.pos >= this.keys.length;
434         }
435 
436     }
437 
438     public static class TwoArrayMapletList< K, V > extends AbstractIList< Maplet< K, V > > {
439 
440         /**
441          * The backing store containing a sequence of name objects matching the
442          * values.
443          */
444         private final K[] keys;
445 
446         /**
447          * The backing store containing a sequence of value objects matching the
448          * names.
449          */
450         private final V[] values;
451 
452         /**
453          * Constructs a new array iterator to iterate over the alternating
454          * key-value objects in the specified array.
455          *
456          * @param k the array of keys
457          * @param v the array of values
458          * @param share if <code>true</code> the specified object array will be
459          * shared otherwise the array will be copied.
460          */
461         @SuppressWarnings( "unchecked" )
462         public TwoArrayMapletList( final K[] k, final V[] v, final boolean share ) {
463             // Validate the incoming arrays
464             if ( k == null && v == null ) {
465                 this.keys = null;
466                 this.values = null;
467             } else if ( k == null || v == null ) {
468                 throw new IllegalArgumentAlert(
469                     "Backing store arrays should both be null or not null"
470                 ).culprit(
471                     "keys array",
472                     k
473                 ).culprit(
474                     "values array",
475                     v
476                 ).mishap();
477             } else if ( k.length != v.length ) {
478                 throw new IllegalArgumentAlert(
479                     "Backing store arrays must be the same length"
480                 ).culprit(
481                     "keys array",
482                     k
483                 ).culprit(
484                     "keys array length",
485                     k.length
486                 ).culprit(
487                     "values array",
488                     v
489                 ).culprit(
490                     "values array length",
491                     v.length
492                 ).mishap();
493             } else if ( share ) {
494                 this.keys = k;
495                 this.values = v;
496             } else {
497                 this.keys = (K[]) new Object[ k.length ];
498                 System.arraycopy( k, 0, this.keys, 0, k.length );
499                 this.values = (V[]) new Object[ v.length ];
500                 System.arraycopy( v, 0, this.values, 0, v.length );
501             }
502         }
503 
504         /**
505          * @see org.millscript.commons.util.list.AbstractIList#doGet(int)
506          */
507         @Override
508         protected Maplet< K, V > doGet( final int pos ) {
509             return new IMaplet< K, V >( this.keys[ pos - 1 ], this.values[ pos - 1 ] );
510         }
511 
512         /**
513          * @see org.millscript.commons.util.list.AbstractIList#doSlice(int, int, boolean)
514          */
515         @Override
516         @SuppressWarnings( "unchecked" )
517         protected IList< Maplet< K, V > > doSlice( final int first, final int last, final boolean share ) {
518             if ( share ) {
519                 return new SharedTwoArrayMapletList< K, V >(
520                     this.keys,
521                     this.values,
522                     first,
523                     last
524                 );
525             } else {
526                 final K[] ks = (K[]) new Object[ this.size() ];
527                 System.arraycopy( this.keys, first - 1, ks, 0, ks.length );
528                 final V[] vs = (V[]) new Object[ this.size() ];
529                 System.arraycopy( this.values, last - 1, vs, 0, vs.length );
530                 return new TwoArrayMapletList< K, V >( ks, vs, true );
531             }
532         }
533 
534         /**
535          * @see org.millscript.commons.util.IList#indexOf(java.lang.Object)
536          */
537         public int indexOf( final Maplet< K, V > value ) {
538             if ( value != null ) {
539                 final Object key = value.getKey();
540                 for ( int i = 0; this.keys != null && i < this.keys.length; i++ ) {
541                     if ( key == null ? this.keys[ i ] == null : key.equals( this.keys[ i ] ) ) {
542                         final Object val = value.getValue();
543                         if ( val == null ? this.values[ i ] == null : val.equals( this.values[ i ] ) ) {
544                             return i + 1;
545                         }
546                     }
547                 }
548             }
549             return 0;
550         }
551 
552         /**
553          * @see org.millscript.commons.util.IMap#iterator(boolean)
554          */
555         public ListIterator< Maplet< K, V > > iterator( final boolean share ) {
556             return new MapletListIterator< K, V >( this.keys, this.values, share );
557         }
558 
559         /**
560          * @see org.millscript.commons.util.IMap#size()
561          */
562         public int size() {
563             return this.keys == null ? 0 : this.keys.length;
564         }
565         
566     }
567 
568     /**
569      * The backing store containing a sequence of key objects matching the
570      * values.
571      */
572     private final K[] keys;
573 
574     /**
575      * The backing store containing a sequence of value objects matching the
576      * names.
577      */
578     private final V[] values;
579 
580     /**
581      * Constructs a new empty immutable two array map. 
582      */
583     public ITwoArrayMap() {
584         this.keys = null;
585         this.values = null;
586     }
587 
588     /**
589      * Constructs a new immutable two array map with a copy of the specified
590      * backing arrays containing keys and matching values.
591      *
592      * @param k the backing array of key objects to copy
593      * @param v the backing array of matching value objects to copy
594      * @param share if <code>true</code> the specified object array will be
595      * shared otherwise the array will be copied.
596      */
597     @SuppressWarnings( "unchecked" )
598     public ITwoArrayMap( final K[] k, final V[] v, final boolean share ) {
599         // Validate the incoming arrays
600         if ( k.length != v.length ) {
601             throw new IllegalArgumentAlert(
602                 "Backing store arrays must be the same length"
603             ).culprit(
604                 "keys array",
605                 k
606             ).culprit(
607                 "keys array length",
608                 k.length
609             ).culprit(
610                 "values array",
611                 v
612             ).culprit(
613                 "values array length",
614                 v.length
615             ).mishap();
616         }
617         // Setup the backing store
618         if ( share ) {
619             this.keys = k;
620             this.values = v;
621         } else {
622             this.keys = (K[]) new Object[ k.length ];
623             System.arraycopy( k, 0, this.keys, 0, k.length );
624             this.values = (V[]) new Object[ v.length ];
625             System.arraycopy( v, 0, this.values, 0, v.length );
626         }
627     }
628 
629     /**
630      * @see java.lang.Object#clone()
631      */
632     @Override
633     public Object clone() throws CloneNotSupportedException {
634         // Nothing special required for this clone
635         return super.clone();
636     }
637 
638     /**
639      * @see org.millscript.commons.util.IMap#contains(java.lang.Object, java.lang.Object)
640      */
641     public boolean contains( final K key, final V value ) {
642         for ( int i = 0; this.keys != null && i < this.keys.length; i++ ) {
643             final K sKey = this.keys[ i ];
644             final V sValue = this.values[ i ];
645             if ( sKey == null ? key == null : sKey.equals( key ) ) {
646                 // The keys are equal, so return true if the values are both
647                 // null or equal and false otherwise
648                 return sValue == null ? value == null
649                                       : sValue.equals( value );
650             }
651         }
652         return false;
653     }
654 
655     /**
656      * @see org.millscript.commons.util.IMap#containsKey(java.lang.Object)
657      */
658     public boolean containsKey( final K key ) {
659         for ( int i = 0; this.keys != null && i < this.keys.length; i++ ) {
660             if ( key == null ? this.keys[ i ] == null : key.equals( this.keys[ i ] ) ) {
661                 return true;
662             }
663         }
664         return false;
665     }
666 
667     /**
668      * @see org.millscript.commons.util.IMap#containsValue(java.lang.Object)
669      */
670     public boolean containsValue( final V value ) {
671         for ( int i = 0; this.values != null && i < this.values.length; i++ ) {
672             if ( value == null ? this.values[ i ] == null : value.equals( this.values[ i ] ) ) {
673                 return true;
674             }
675         }
676         return false;
677     }
678 
679     /**
680      * @see org.millscript.commons.util.IMap#get(java.lang.Object)
681      */
682     public V get( final K key ) {
683         for ( int i = 0; this.keys != null && i < this.keys.length; i++ ) {
684             if ( key == null ? this.keys[ i ] == null : key.equals( this.keys[ i ] ) ) {
685                 return this.values[ i ];
686             }
687         }
688         return this.getDefault().get( this, key );
689     }
690 
691     /**
692      * @see org.millscript.commons.util.IMap#iterator(boolean)
693      */
694     public MapIterator< K, V > iterator( final boolean share ) {
695         return new TwoArrayMapIterator< K, V >( this.keys, this.values, share );
696     }
697 
698     /**
699      * @see org.millscript.commons.util.IMap#keyList(boolean)
700      */
701     @Override
702     public IList< K > keyList( final boolean share ) {
703         // This is much more efficient than the super implementation
704         return new IArrayList< K >( this.keys, share );
705     }
706 
707     /**
708      * @see org.millscript.commons.util.map.AbstractIMap#sharedKeyList()
709      */
710     @Override
711     protected IList< K > sharedKeyList() {
712         // It's unlikely these methods would ever get called
713         return new IArrayList< K >( this.keys, true );
714     }
715 
716     /**
717      * @see org.millscript.commons.util.map.AbstractIMap#sharedMapletList()
718      */
719     @Override
720     protected IList< Maplet< K, V > > sharedMapletList() {
721         return new TwoArrayMapletList< K, V >( this.keys, this.values, true );
722     }
723 
724     /**
725      * @see org.millscript.commons.util.map.AbstractIMap#sharedValueList()
726      */
727     @Override
728     protected IList< V > sharedValueList() {
729         // It's unlikely these methods would ever get called
730         return new IArrayList< V >( this.values, true );
731     }
732 
733     /**
734      * @see org.millscript.commons.util.IMap#size()
735      */
736     public int size() {
737         return this.keys == null ? 0 : this.keys.length;
738     }
739 
740     /**
741      * @see org.millscript.commons.util.IMap#valueList(boolean)
742      */
743     @Override
744     public IList< V > valueList( final boolean share ) {
745         // This is much more efficient than the super implementation
746         return new IArrayList< V >( this.values, share );
747     }
748 
749 }