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