2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 package com.intellij.util.containers;
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;
30 import java.io.Serializable;
33 public class MostlySingularMultiMap<K, V> implements Serializable {
34 private static final long serialVersionUID = 2784448345881807109L;
36 protected final Map<K, Object> myMap;
38 public MostlySingularMultiMap() {
43 protected Map<K, Object> createMap() {
44 return new THashMap<K, Object>();
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);
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);
58 myMap.put(key, new Object[]{current, value});
62 public boolean remove(@NotNull K key, @NotNull V value) {
63 Object current = myMap.get(key);
64 if (current == null) {
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;
74 if (value.equals(current)) {
82 public boolean removeAllValues(@NotNull K key) {
83 return myMap.remove(key) != null;
87 public Set<K> keySet() {
88 return myMap.keySet();
91 public boolean isEmpty() {
92 return myMap.isEmpty();
95 public boolean processForKey(@NotNull K key, @NotNull Processor<? super V> p) {
96 return processValue(p, myMap.get(key));
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;
105 else if (v != null) {
106 return p.process((V)v);
112 public boolean processAllValues(@NotNull Processor<? super V> p) {
113 for (Object v : myMap.values()) {
114 if (!processValue(p, v)) return false;
124 public boolean containsKey(@NotNull K key) {
125 return myMap.containsKey(key);
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;
136 public Iterable<V> get(@NotNull K name) {
137 final Object value = myMap.get(name);
138 return rawValueToCollection(value);
142 protected List<V> rawValueToCollection(Object value) {
143 if (value == null) return Collections.emptyList();
145 if (value instanceof Object[]) {
146 return (List<V>)Arrays.asList((Object[])value);
149 return Collections.singletonList((V)value);
152 public void compact() {
153 ((THashMap)myMap).compact();
157 public String toString() {
158 return "{" + StringUtil.join(myMap.entrySet(), new Function<Map.Entry<K, Object>, String>() {
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;
168 public void clear() {
173 public static <K,V> MostlySingularMultiMap<K,V> emptyMap() {
174 //noinspection unchecked
179 public static <K, V> MostlySingularMultiMap<K, V> newMap() {
180 return new MostlySingularMultiMap<K, V>();
182 private static final MostlySingularMultiMap EMPTY = new EmptyMap();
184 private static class EmptyMap extends MostlySingularMultiMap {
186 public void add(@NotNull Object key, @NotNull Object value) {
187 throw new IncorrectOperationException();
191 public boolean remove(@NotNull Object key, @NotNull Object value) {
192 throw new IncorrectOperationException();
196 public boolean removeAllValues(@NotNull Object key) {
197 throw new IncorrectOperationException();
201 public void clear() {
202 throw new IncorrectOperationException();
207 public Set keySet() {
208 return Collections.emptySet();
212 public boolean isEmpty() {
217 public boolean processForKey(@NotNull Object key, @NotNull Processor p) {
222 public boolean processAllValues(@NotNull Processor p) {
232 public int valuesForKey(@NotNull Object key) {
238 public Iterable get(@NotNull Object name) {
239 return ContainerUtil.emptyList();