OC-11340 parameterized resolving (2)
[idea/community.git] / platform / util / src / com / intellij / util / containers / MostlySingularMultiMap.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * @author max
19  */
20 package com.intellij.util.containers;
21
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.util.ArrayUtil;
24 import com.intellij.util.Function;
25 import com.intellij.util.IncorrectOperationException;
26 import com.intellij.util.Processor;
27 import gnu.trove.THashMap;
28 import org.jetbrains.annotations.NotNull;
29
30 import java.io.Serializable;
31 import java.util.*;
32
33 public class MostlySingularMultiMap<K, V> implements Serializable {
34   private static final long serialVersionUID = 2784448345881807109L;
35
36   protected final Map<K, Object> myMap;
37
38   public MostlySingularMultiMap() {
39     myMap = createMap();
40   }
41
42   @NotNull
43   protected Map<K, Object> createMap() {
44     return new THashMap<K, Object>();
45   }
46
47   public void add(@NotNull K key, @NotNull V value) {
48     Object current = myMap.get(key);
49     if (current == null) {
50       myMap.put(key, value);
51     }
52     else if (current instanceof Object[]) {
53       Object[] curArr = (Object[])current;
54       Object[] newArr = ArrayUtil.append(curArr, value, ArrayUtil.OBJECT_ARRAY_FACTORY);
55       myMap.put(key, newArr);
56     }
57     else {
58       myMap.put(key, new Object[]{current, value});
59     }
60   }
61
62   public boolean remove(@NotNull K key, @NotNull V value) {
63     Object current = myMap.get(key);
64     if (current == null) {
65       return false;
66     }
67     if (current instanceof Object[]) {
68       Object[] curArr = (Object[])current;
69       Object[] newArr = ArrayUtil.remove(curArr, value, ArrayUtil.OBJECT_ARRAY_FACTORY);
70       myMap.put(key, newArr);
71       return newArr.length == curArr.length-1;
72     }
73
74     if (value.equals(current)) {
75       myMap.remove(key);
76       return true;
77     }
78
79     return false;
80   }
81
82   public boolean removeAllValues(@NotNull K key) {
83     return myMap.remove(key) != null;
84   }
85
86   @NotNull
87   public Set<K> keySet() {
88     return myMap.keySet();
89   }
90
91   public boolean isEmpty() {
92     return myMap.isEmpty();
93   }
94
95   public boolean processForKey(@NotNull K key, @NotNull Processor<? super V> p) {
96     return processValue(p, myMap.get(key));
97   }
98
99   private boolean processValue(@NotNull Processor<? super V> p, Object v) {
100     if (v instanceof Object[]) {
101       for (Object o : (Object[])v) {
102         if (!p.process((V)o)) return false;
103       }
104     }
105     else if (v != null) {
106       return p.process((V)v);
107     }
108
109     return true;
110   }
111
112   public boolean processAllValues(@NotNull Processor<? super V> p) {
113     for (Object v : myMap.values()) {
114       if (!processValue(p, v)) return false;
115     }
116
117     return true;
118   }
119
120   public int size() {
121     return myMap.size();
122   }
123
124   public boolean containsKey(@NotNull K key) {
125     return myMap.containsKey(key);
126   }
127
128   public int valuesForKey(@NotNull K key) {
129     Object current = myMap.get(key);
130     if (current == null) return 0;
131     if (current instanceof Object[]) return ((Object[])current).length;
132     return 1;
133   }
134
135   @NotNull
136   public Iterable<V> get(@NotNull K name) {
137     final Object value = myMap.get(name);
138     return rawValueToCollection(value);
139   }
140
141   @NotNull
142   protected List<V> rawValueToCollection(Object value) {
143     if (value == null) return Collections.emptyList();
144
145     if (value instanceof Object[]) {
146       return (List<V>)Arrays.asList((Object[])value);
147     }
148
149     return Collections.singletonList((V)value);
150   }
151
152   public void compact() {
153     ((THashMap)myMap).compact();
154   }
155
156   @Override
157   public String toString() {
158     return "{" + StringUtil.join(myMap.entrySet(), new Function<Map.Entry<K, Object>, String>() {
159       @Override
160       public String fun(Map.Entry<K, Object> entry) {
161         Object value = entry.getValue();
162         String s = (value instanceof Object[] ? Arrays.asList((Object[])value) : Arrays.asList(value)).toString();
163         return entry.getKey() + ": " + s;
164       }
165     }, "; ") + "}";
166   }
167
168   public void clear() {
169     myMap.clear();
170   }
171
172   @NotNull
173   public static <K,V> MostlySingularMultiMap<K,V> emptyMap() {
174     //noinspection unchecked
175     return EMPTY;
176   }
177
178   @NotNull
179   public static <K, V> MostlySingularMultiMap<K, V> newMap() {
180     return new MostlySingularMultiMap<K, V>();
181   }
182   private static final MostlySingularMultiMap EMPTY = new EmptyMap();
183
184   private static class EmptyMap extends MostlySingularMultiMap {
185     @Override
186     public void add(@NotNull Object key, @NotNull Object value) {
187       throw new IncorrectOperationException();
188     }
189
190     @Override
191     public boolean remove(@NotNull Object key, @NotNull Object value) {
192       throw new IncorrectOperationException();
193     }
194
195     @Override
196     public boolean removeAllValues(@NotNull Object key) {
197       throw new IncorrectOperationException();
198     }
199
200     @Override
201     public void clear() {
202       throw new IncorrectOperationException();
203     }
204
205     @NotNull
206     @Override
207     public Set keySet() {
208       return Collections.emptySet();
209     }
210
211     @Override
212     public boolean isEmpty() {
213       return true;
214     }
215
216     @Override
217     public boolean processForKey(@NotNull Object key, @NotNull Processor p) {
218       return true;
219     }
220
221     @Override
222     public boolean processAllValues(@NotNull Processor p) {
223       return true;
224     }
225
226     @Override
227     public int size() {
228       return 0;
229     }
230
231     @Override
232     public int valuesForKey(@NotNull Object key) {
233       return 0;
234     }
235
236     @NotNull
237     @Override
238     public Iterable get(@NotNull Object name) {
239       return ContainerUtil.emptyList();
240     }
241   }
242 }