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