blob: 21c15099a15ef48522d42fb6046c68a81ba4eb31 [file] [log] [blame]
Stuart McCullochf3173222012-06-07 21:57:32 +00001package aQute.lib.collections;
2
3import java.util.*;
4
Stuart McCulloch4482c702012-06-15 13:27:53 +00005public class MultiMap<K, V> extends HashMap<K,List<V>> {
Stuart McCullochf3173222012-06-07 21:57:32 +00006 private static final long serialVersionUID = 1L;
Stuart McCulloch4482c702012-06-15 13:27:53 +00007 final boolean noduplicates;
8 final Class< ? > keyClass;
9 final Class< ? > valueClass;
10
11 final Set<V> EMPTY = Collections.emptySet();
12
Stuart McCullochf3173222012-06-07 21:57:32 +000013 public MultiMap() {
14 noduplicates = false;
15 keyClass = Object.class;
16 valueClass = Object.class;
17 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000018
19 public MultiMap(Class<K> keyClass, Class<V> valueClass, boolean noduplicates) {
20 this.noduplicates = noduplicates;
Stuart McCullochf3173222012-06-07 21:57:32 +000021 this.keyClass = keyClass;
22 this.valueClass = valueClass;
23 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000024
25 @SuppressWarnings("unchecked")
26 public boolean add(K key, V value) {
Stuart McCullochf3173222012-06-07 21:57:32 +000027 assert keyClass.isInstance(key);
28 assert valueClass.isInstance(value);
Stuart McCulloch4482c702012-06-15 13:27:53 +000029
Stuart McCullochf3173222012-06-07 21:57:32 +000030 List<V> set = get(key);
Stuart McCulloch4482c702012-06-15 13:27:53 +000031 if (set == null) {
32 set = new ArrayList<V>();
33 if (valueClass != Object.class) {
34 set = Collections.checkedList(set, (Class<V>) valueClass);
Stuart McCullochf3173222012-06-07 21:57:32 +000035 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000036 put(key, set);
37 } else {
Stuart McCullochf3173222012-06-07 21:57:32 +000038 if (noduplicates) {
Stuart McCulloch4482c702012-06-15 13:27:53 +000039 if (set.contains(value))
Stuart McCullochf3173222012-06-07 21:57:32 +000040 return false;
41 }
42 }
43 return set.add(value);
44 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000045
46 @SuppressWarnings("unchecked")
47 public boolean addAll(K key, Collection< ? extends V> value) {
Stuart McCullochf3173222012-06-07 21:57:32 +000048 assert keyClass.isInstance(key);
49 List<V> set = get(key);
Stuart McCulloch4482c702012-06-15 13:27:53 +000050 if (set == null) {
51 set = new ArrayList<V>();
52 if (valueClass != Object.class) {
53 set = Collections.checkedList(set, (Class<V>) valueClass);
Stuart McCullochf3173222012-06-07 21:57:32 +000054 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000055 put(key, set);
56 } else if (noduplicates) {
57 boolean r = false;
58 for (V v : value) {
Stuart McCullochf3173222012-06-07 21:57:32 +000059 assert valueClass.isInstance(v);
Stuart McCulloch4482c702012-06-15 13:27:53 +000060 if (!set.contains(value))
61 r |= set.add(v);
Stuart McCullochf3173222012-06-07 21:57:32 +000062 }
63 return r;
64 }
65 return set.addAll(value);
66 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000067
68 public boolean remove(K key, V value) {
Stuart McCullochf3173222012-06-07 21:57:32 +000069 assert keyClass.isInstance(key);
70 assert valueClass.isInstance(value);
Stuart McCulloch4482c702012-06-15 13:27:53 +000071
Stuart McCullochf3173222012-06-07 21:57:32 +000072 List<V> set = get(key);
Stuart McCulloch4482c702012-06-15 13:27:53 +000073 if (set == null) {
Stuart McCullochf3173222012-06-07 21:57:32 +000074 return false;
75 }
76 boolean result = set.remove(value);
Stuart McCulloch4482c702012-06-15 13:27:53 +000077 if (set.isEmpty())
Stuart McCullochf3173222012-06-07 21:57:32 +000078 remove(key);
79 return result;
80 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000081
82 public boolean removeAll(K key, Collection<V> value) {
Stuart McCullochf3173222012-06-07 21:57:32 +000083 assert keyClass.isInstance(key);
84 List<V> set = get(key);
Stuart McCulloch4482c702012-06-15 13:27:53 +000085 if (set == null) {
Stuart McCullochf3173222012-06-07 21:57:32 +000086 return false;
87 }
88 boolean result = set.removeAll(value);
Stuart McCulloch4482c702012-06-15 13:27:53 +000089 if (set.isEmpty())
Stuart McCullochf3173222012-06-07 21:57:32 +000090 remove(key);
91 return result;
92 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000093
Stuart McCullochf3173222012-06-07 21:57:32 +000094 public Iterator<V> iterate(K key) {
95 assert keyClass.isInstance(key);
96 List<V> set = get(key);
Stuart McCulloch4482c702012-06-15 13:27:53 +000097 if (set == null)
Stuart McCullochf3173222012-06-07 21:57:32 +000098 return EMPTY.iterator();
99 return set.iterator();
100 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000101
Stuart McCullochf3173222012-06-07 21:57:32 +0000102 public Iterator<V> all() {
103 return new Iterator<V>() {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000104 Iterator<List<V>> master = values().iterator();
105 Iterator<V> current = null;
106
Stuart McCullochf3173222012-06-07 21:57:32 +0000107 public boolean hasNext() {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000108 if (current == null || !current.hasNext()) {
109 if (master.hasNext()) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000110 current = master.next().iterator();
111 return current.hasNext();
112 }
113 return false;
114 }
115 return true;
116 }
117
118 public V next() {
119 return current.next();
120 }
121
122 public void remove() {
123 current.remove();
124 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000125
Stuart McCullochf3173222012-06-07 21:57:32 +0000126 };
127 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000128
Stuart McCullochf3173222012-06-07 21:57:32 +0000129 public Map<K,V> flatten() {
130 Map<K,V> map = new LinkedHashMap<K,V>();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000131 for (Map.Entry<K,List<V>> entry : entrySet()) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000132 List<V> v = entry.getValue();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000133 if (v == null || v.isEmpty())
Stuart McCullochf3173222012-06-07 21:57:32 +0000134 continue;
135
136 map.put(entry.getKey(), v.get(0));
137 }
138 return map;
139 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000140
Stuart McCullochf3173222012-06-07 21:57:32 +0000141 public MultiMap<V,K> transpose() {
142 MultiMap<V,K> inverted = new MultiMap<V,K>();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000143 for (Map.Entry<K,List<V>> entry : entrySet()) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000144 K key = entry.getKey();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000145
Stuart McCullochf3173222012-06-07 21:57:32 +0000146 List<V> value = entry.getValue();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000147 if (value == null)
Stuart McCullochf3173222012-06-07 21:57:32 +0000148 continue;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000149
150 for (V v : value)
Stuart McCullochf3173222012-06-07 21:57:32 +0000151 inverted.add(v, key);
152 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000153
Stuart McCullochf3173222012-06-07 21:57:32 +0000154 return inverted;
155 }
156}