1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.collections.map;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.util.Map;
24
25 import org.apache.commons.collections.IterableMap;
26 import org.apache.commons.collections.MapIterator;
27 import org.apache.commons.collections.keyvalue.MultiKey;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 public class MultiKeyMap<K, V> extends AbstractMapDecorator<MultiKey<? extends K>, V>
79 implements IterableMap<MultiKey<? extends K>, V>, Serializable, Cloneable {
80
81
82 private static final long serialVersionUID = -1788199231038721040L;
83
84
85
86
87
88
89
90
91
92
93
94
95 public static <K, V> MultiKeyMap<K, V> multiKeyMap(final AbstractHashedMap<MultiKey<? extends K>, V> map) {
96 if (map == null) {
97 throw new IllegalArgumentException("Map must not be null");
98 }
99 if (map.size() > 0) {
100 throw new IllegalArgumentException("Map must be empty");
101 }
102 return new MultiKeyMap<K, V>(map);
103 }
104
105
106
107
108
109 public MultiKeyMap() {
110 this(new HashedMap<MultiKey<? extends K>, V>());
111 }
112
113
114
115
116
117
118
119
120
121 protected MultiKeyMap(final AbstractHashedMap<MultiKey<? extends K>, V> map) {
122 super(map);
123 this.map = map;
124 }
125
126
127
128
129
130
131
132
133
134 public V get(final Object key1, final Object key2) {
135 final int hashCode = hash(key1, key2);
136 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
137 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
138 while (entry != null) {
139 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
140 return entry.getValue();
141 }
142 entry = entry.next;
143 }
144 return null;
145 }
146
147
148
149
150
151
152
153
154 public boolean containsKey(final Object key1, final Object key2) {
155 final int hashCode = hash(key1, key2);
156 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
157 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
158 while (entry != null) {
159 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
160 return true;
161 }
162 entry = entry.next;
163 }
164 return false;
165 }
166
167
168
169
170
171
172
173
174
175 public V put(final K key1, final K key2, final V value) {
176 final int hashCode = hash(key1, key2);
177 final int index = decorated().hashIndex(hashCode, decorated().data.length);
178 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
179 while (entry != null) {
180 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
181 final V oldValue = entry.getValue();
182 decorated().updateEntry(entry, value);
183 return oldValue;
184 }
185 entry = entry.next;
186 }
187 decorated().addMapping(index, hashCode, new MultiKey<K>(key1, key2), value);
188 return null;
189 }
190
191
192
193
194
195
196
197
198 public V remove(final Object key1, final Object key2) {
199 final int hashCode = hash(key1, key2);
200 final int index = decorated().hashIndex(hashCode, decorated().data.length);
201 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
202 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
203 while (entry != null) {
204 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
205 final V oldValue = entry.getValue();
206 decorated().removeMapping(entry, index, previous);
207 return oldValue;
208 }
209 previous = entry;
210 entry = entry.next;
211 }
212 return null;
213 }
214
215
216
217
218
219
220
221
222 protected int hash(final Object key1, final Object key2) {
223 int h = 0;
224 if (key1 != null) {
225 h ^= key1.hashCode();
226 }
227 if (key2 != null) {
228 h ^= key2.hashCode();
229 }
230 h += ~(h << 9);
231 h ^= h >>> 14;
232 h += h << 4;
233 h ^= h >>> 10;
234 return h;
235 }
236
237
238
239
240
241
242
243
244
245 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
246 final Object key1, final Object key2) {
247 final MultiKey<? extends K> multi = entry.getKey();
248 return
249 multi.size() == 2 &&
250 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
251 (key2 == multi.getKey(1) || key1 != null && key2.equals(multi.getKey(1)));
252 }
253
254
255
256
257
258
259
260
261
262
263 public V get(final Object key1, final Object key2, final Object key3) {
264 final int hashCode = hash(key1, key2, key3);
265 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
266 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
267 while (entry != null) {
268 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
269 return entry.getValue();
270 }
271 entry = entry.next;
272 }
273 return null;
274 }
275
276
277
278
279
280
281
282
283
284 public boolean containsKey(final Object key1, final Object key2, final Object key3) {
285 final int hashCode = hash(key1, key2, key3);
286 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
287 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
288 while (entry != null) {
289 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
290 return true;
291 }
292 entry = entry.next;
293 }
294 return false;
295 }
296
297
298
299
300
301
302
303
304
305
306 public V put(final K key1, final K key2, final K key3, final V value) {
307 final int hashCode = hash(key1, key2, key3);
308 final int index = decorated().hashIndex(hashCode, decorated().data.length);
309 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
310 while (entry != null) {
311 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
312 final V oldValue = entry.getValue();
313 decorated().updateEntry(entry, value);
314 return oldValue;
315 }
316 entry = entry.next;
317 }
318 decorated().addMapping(index, hashCode, new MultiKey<K>(key1, key2, key3), value);
319 return null;
320 }
321
322
323
324
325
326
327
328
329
330 public V remove(final Object key1, final Object key2, final Object key3) {
331 final int hashCode = hash(key1, key2, key3);
332 final int index = decorated().hashIndex(hashCode, decorated().data.length);
333 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
334 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
335 while (entry != null) {
336 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
337 final V oldValue = entry.getValue();
338 decorated().removeMapping(entry, index, previous);
339 return oldValue;
340 }
341 previous = entry;
342 entry = entry.next;
343 }
344 return null;
345 }
346
347
348
349
350
351
352
353
354
355 protected int hash(final Object key1, final Object key2, final Object key3) {
356 int h = 0;
357 if (key1 != null) {
358 h ^= key1.hashCode();
359 }
360 if (key2 != null) {
361 h ^= key2.hashCode();
362 }
363 if (key3 != null) {
364 h ^= key3.hashCode();
365 }
366 h += ~(h << 9);
367 h ^= h >>> 14;
368 h += h << 4;
369 h ^= h >>> 10;
370 return h;
371 }
372
373
374
375
376
377
378
379
380
381
382 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
383 final Object key1, final Object key2, final Object key3) {
384 final MultiKey<? extends K> multi = entry.getKey();
385 return
386 multi.size() == 3 &&
387 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
388 (key2 == multi.getKey(1) || key2 != null && key2.equals(multi.getKey(1))) &&
389 (key3 == multi.getKey(2) || key3 != null && key3.equals(multi.getKey(2)));
390 }
391
392
393
394
395
396
397
398
399
400
401
402 public V get(final Object key1, final Object key2, final Object key3, final Object key4) {
403 final int hashCode = hash(key1, key2, key3, key4);
404 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
405 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
406 while (entry != null) {
407 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
408 return entry.getValue();
409 }
410 entry = entry.next;
411 }
412 return null;
413 }
414
415
416
417
418
419
420
421
422
423
424 public boolean containsKey(final Object key1, final Object key2, final Object key3, final Object key4) {
425 final int hashCode = hash(key1, key2, key3, key4);
426 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
427 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
428 while (entry != null) {
429 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
430 return true;
431 }
432 entry = entry.next;
433 }
434 return false;
435 }
436
437
438
439
440
441
442
443
444
445
446
447 public V put(final K key1, final K key2, final K key3, final K key4, final V value) {
448 final int hashCode = hash(key1, key2, key3, key4);
449 final int index = decorated().hashIndex(hashCode, decorated().data.length);
450 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
451 while (entry != null) {
452 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
453 final V oldValue = entry.getValue();
454 decorated().updateEntry(entry, value);
455 return oldValue;
456 }
457 entry = entry.next;
458 }
459 decorated().addMapping(index, hashCode, new MultiKey<K>(key1, key2, key3, key4), value);
460 return null;
461 }
462
463
464
465
466
467
468
469
470
471
472 public V remove(final Object key1, final Object key2, final Object key3, final Object key4) {
473 final int hashCode = hash(key1, key2, key3, key4);
474 final int index = decorated().hashIndex(hashCode, decorated().data.length);
475 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
476 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
477 while (entry != null) {
478 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
479 final V oldValue = entry.getValue();
480 decorated().removeMapping(entry, index, previous);
481 return oldValue;
482 }
483 previous = entry;
484 entry = entry.next;
485 }
486 return null;
487 }
488
489
490
491
492
493
494
495
496
497
498 protected int hash(final Object key1, final Object key2, final Object key3, final Object key4) {
499 int h = 0;
500 if (key1 != null) {
501 h ^= key1.hashCode();
502 }
503 if (key2 != null) {
504 h ^= key2.hashCode();
505 }
506 if (key3 != null) {
507 h ^= key3.hashCode();
508 }
509 if (key4 != null) {
510 h ^= key4.hashCode();
511 }
512 h += ~(h << 9);
513 h ^= h >>> 14;
514 h += h << 4;
515 h ^= h >>> 10;
516 return h;
517 }
518
519
520
521
522
523
524
525
526
527
528
529 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
530 final Object key1, final Object key2, final Object key3, final Object key4) {
531 final MultiKey<? extends K> multi = entry.getKey();
532 return
533 multi.size() == 4 &&
534 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
535 (key2 == multi.getKey(1) || key2 != null && key2.equals(multi.getKey(1))) &&
536 (key3 == multi.getKey(2) || key3 != null && key3.equals(multi.getKey(2))) &&
537 (key4 == multi.getKey(3) || key4 != null && key4.equals(multi.getKey(3)));
538 }
539
540
541
542
543
544
545
546
547
548
549
550
551 public V get(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
552 final int hashCode = hash(key1, key2, key3, key4, key5);
553 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
554 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
555 while (entry != null) {
556 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
557 return entry.getValue();
558 }
559 entry = entry.next;
560 }
561 return null;
562 }
563
564
565
566
567
568
569
570
571
572
573
574 public boolean containsKey(final Object key1, final Object key2, final Object key3,
575 final Object key4, final Object key5) {
576 final int hashCode = hash(key1, key2, key3, key4, key5);
577 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry =
578 decorated().data[decorated().hashIndex(hashCode, decorated().data.length)];
579 while (entry != null) {
580 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
581 return true;
582 }
583 entry = entry.next;
584 }
585 return false;
586 }
587
588
589
590
591
592
593
594
595
596
597
598
599 public V put(final K key1, final K key2, final K key3, final K key4, final K key5, final V value) {
600 final int hashCode = hash(key1, key2, key3, key4, key5);
601 final int index = decorated().hashIndex(hashCode, decorated().data.length);
602 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
603 while (entry != null) {
604 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
605 final V oldValue = entry.getValue();
606 decorated().updateEntry(entry, value);
607 return oldValue;
608 }
609 entry = entry.next;
610 }
611 decorated().addMapping(index, hashCode, new MultiKey<K>(key1, key2, key3, key4, key5), value);
612 return null;
613 }
614
615
616
617
618
619
620
621
622
623
624
625 public V remove(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
626 final int hashCode = hash(key1, key2, key3, key4, key5);
627 final int index = decorated().hashIndex(hashCode, decorated().data.length);
628 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry = decorated().data[index];
629 AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> previous = null;
630 while (entry != null) {
631 if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
632 final V oldValue = entry.getValue();
633 decorated().removeMapping(entry, index, previous);
634 return oldValue;
635 }
636 previous = entry;
637 entry = entry.next;
638 }
639 return null;
640 }
641
642
643
644
645
646
647
648
649
650
651
652 protected int hash(final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
653 int h = 0;
654 if (key1 != null) {
655 h ^= key1.hashCode();
656 }
657 if (key2 != null) {
658 h ^= key2.hashCode();
659 }
660 if (key3 != null) {
661 h ^= key3.hashCode();
662 }
663 if (key4 != null) {
664 h ^= key4.hashCode();
665 }
666 if (key5 != null) {
667 h ^= key5.hashCode();
668 }
669 h += ~(h << 9);
670 h ^= h >>> 14;
671 h += h << 4;
672 h ^= h >>> 10;
673 return h;
674 }
675
676
677
678
679
680
681
682
683
684
685
686
687 protected boolean isEqualKey(final AbstractHashedMap.HashEntry<MultiKey<? extends K>, V> entry,
688 final Object key1, final Object key2, final Object key3, final Object key4, final Object key5) {
689 final MultiKey<? extends K> multi = entry.getKey();
690 return
691 multi.size() == 5 &&
692 (key1 == multi.getKey(0) || key1 != null && key1.equals(multi.getKey(0))) &&
693 (key2 == multi.getKey(1) || key2 != null && key2.equals(multi.getKey(1))) &&
694 (key3 == multi.getKey(2) || key3 != null && key3.equals(multi.getKey(2))) &&
695 (key4 == multi.getKey(3) || key4 != null && key4.equals(multi.getKey(3))) &&
696 (key5 == multi.getKey(4) || key5 != null && key5.equals(multi.getKey(4)));
697 }
698
699
700
701
702
703
704
705
706
707
708
709 public boolean removeAll(final Object key1) {
710 boolean modified = false;
711 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
712 while (it.hasNext()) {
713 final MultiKey<? extends K> multi = it.next();
714 if (multi.size() >= 1 &&
715 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0)))) {
716 it.remove();
717 modified = true;
718 }
719 }
720 return modified;
721 }
722
723
724
725
726
727
728
729
730
731
732
733 public boolean removeAll(final Object key1, final Object key2) {
734 boolean modified = false;
735 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
736 while (it.hasNext()) {
737 final MultiKey<? extends K> multi = it.next();
738 if (multi.size() >= 2 &&
739 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) &&
740 (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1)))) {
741 it.remove();
742 modified = true;
743 }
744 }
745 return modified;
746 }
747
748
749
750
751
752
753
754
755
756
757
758
759 public boolean removeAll(final Object key1, final Object key2, final Object key3) {
760 boolean modified = false;
761 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
762 while (it.hasNext()) {
763 final MultiKey<? extends K> multi = it.next();
764 if (multi.size() >= 3 &&
765 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) &&
766 (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1))) &&
767 (key3 == null ? multi.getKey(2) == null : key3.equals(multi.getKey(2)))) {
768 it.remove();
769 modified = true;
770 }
771 }
772 return modified;
773 }
774
775
776
777
778
779
780
781
782
783
784
785
786
787 public boolean removeAll(final Object key1, final Object key2, final Object key3, final Object key4) {
788 boolean modified = false;
789 final MapIterator<MultiKey<? extends K>, V> it = mapIterator();
790 while (it.hasNext()) {
791 final MultiKey<? extends K> multi = it.next();
792 if (multi.size() >= 4 &&
793 (key1 == null ? multi.getKey(0) == null : key1.equals(multi.getKey(0))) &&
794 (key2 == null ? multi.getKey(1) == null : key2.equals(multi.getKey(1))) &&
795 (key3 == null ? multi.getKey(2) == null : key3.equals(multi.getKey(2))) &&
796 (key4 == null ? multi.getKey(3) == null : key4.equals(multi.getKey(3)))) {
797 it.remove();
798 modified = true;
799 }
800 }
801 return modified;
802 }
803
804
805
806
807
808
809
810 protected void checkKey(final MultiKey<?> key) {
811 if (key == null) {
812 throw new NullPointerException("Key must not be null");
813 }
814 }
815
816
817
818
819
820
821 @SuppressWarnings("unchecked")
822 @Override
823 public MultiKeyMap<K, V> clone() {
824 try {
825 return (MultiKeyMap<K, V>) super.clone();
826 } catch (CloneNotSupportedException e) {
827 throw new InternalError();
828 }
829 }
830
831
832
833
834
835
836
837
838
839
840
841 @Override
842 public V put(final MultiKey<? extends K> key, final V value) {
843 checkKey(key);
844 return super.put(key, value);
845 }
846
847
848
849
850
851
852
853
854
855 @Override
856 public void putAll(final Map<? extends MultiKey<? extends K>, ? extends V> mapToCopy) {
857 for (final MultiKey<? extends K> key : mapToCopy.keySet()) {
858 checkKey(key);
859 }
860 super.putAll(mapToCopy);
861 }
862
863
864 @Override
865 public MapIterator<MultiKey<? extends K>, V> mapIterator() {
866 return decorated().mapIterator();
867 }
868
869
870
871
872 @Override
873 protected AbstractHashedMap<MultiKey<? extends K>, V> decorated() {
874 return (AbstractHashedMap<MultiKey<? extends K>, V>) super.decorated();
875 }
876
877
878
879
880
881
882
883
884 private void writeObject(final ObjectOutputStream out) throws IOException {
885 out.defaultWriteObject();
886 out.writeObject(map);
887 }
888
889
890
891
892
893
894
895
896 @SuppressWarnings("unchecked")
897 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
898 in.defaultReadObject();
899 map = (Map<MultiKey<? extends K>, V>) in.readObject();
900 }
901
902 }