001 /*
002 * Copyright 2010-2015 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.kotlin.util.slicedMap;
018
019 import com.google.common.collect.ArrayListMultimap;
020 import com.google.common.collect.ImmutableMap;
021 import com.google.common.collect.Multimap;
022 import com.intellij.openapi.util.Key;
023 import com.intellij.openapi.util.UserDataHolder;
024 import gnu.trove.THashMap;
025 import kotlin.jvm.functions.Function3;
026 import org.jetbrains.annotations.NotNull;
027
028 import java.util.Collection;
029 import java.util.Collections;
030 import java.util.Map;
031
032 public class SlicedMapImpl implements MutableSlicedMap {
033
034 public static SlicedMapImpl create() {
035 return new SlicedMapImpl();
036 }
037
038 private final Map<Object, UserDataHolderImpl> map = new THashMap<Object, UserDataHolderImpl>(0);
039 private Multimap<WritableSlice<?, ?>, Object> collectiveSliceKeys = null;
040
041 @Override
042 public <K, V> void put(WritableSlice<K, V> slice, K key, V value) {
043 if (!slice.check(key, value)) {
044 return;
045 }
046
047 UserDataHolderImpl holder = map.get(key);
048 if (holder == null) {
049 holder = new UserDataHolderImpl();
050 map.put(key, holder);
051 }
052
053 Key<V> sliceKey = slice.getKey();
054
055 RewritePolicy rewritePolicy = slice.getRewritePolicy();
056 if (rewritePolicy.rewriteProcessingNeeded(key)) {
057 V oldValue = holder.getUserData(sliceKey);
058 if (oldValue != null) {
059 //noinspection unchecked
060 if (!rewritePolicy.processRewrite(slice, key, oldValue, value)) {
061 return;
062 }
063 }
064 }
065
066 if (slice.isCollective()) {
067 if (collectiveSliceKeys == null) {
068 collectiveSliceKeys = ArrayListMultimap.create();
069 }
070
071 collectiveSliceKeys.put(slice, key);
072 }
073
074 holder.putUserData(sliceKey, value);
075 slice.afterPut(this, key, value);
076 }
077
078 @Override
079 public void clear() {
080 map.clear();
081 collectiveSliceKeys = null;
082 }
083
084 @Override
085 public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
086 UserDataHolderImpl holder = map.get(key);
087
088 V value = holder == null ? null : holder.getUserData(slice.getKey());
089
090 return slice.computeValue(this, key, value, value == null);
091 }
092
093 @Override
094 @SuppressWarnings("unchecked")
095 public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
096 assert slice.isCollective() : "Keys are not collected for slice " + slice;
097
098 if (collectiveSliceKeys == null) return Collections.emptyList();
099 return (Collection<K>) collectiveSliceKeys.get(slice);
100 }
101
102 @Override
103 public void forEach(@NotNull Function3<WritableSlice, Object, Object, Void> f) {
104 for (Map.Entry<Object, UserDataHolderImpl> entry : map.entrySet()) {
105 Object key = entry.getKey();
106 UserDataHolderImpl holder = entry.getValue();
107
108 if (holder == null) continue;
109
110 for (Key<?> sliceKey : holder.getKeys()) {
111 Object value = holder.getUserData(sliceKey);
112
113 f.invoke(((AbstractWritableSlice) sliceKey).getSlice(), key, value);
114 }
115 }
116 }
117
118 @NotNull
119 @Override
120 public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
121 ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
122
123 for (Map.Entry<Object, UserDataHolderImpl> entry : map.entrySet()) {
124
125 UserDataHolder holder = entry.getValue();
126
127 V value = holder.getUserData(slice.getKey());
128
129 if (value != null) {
130 //noinspection unchecked
131 builder.put((K) entry.getKey(), value);
132 }
133 }
134 return builder.build();
135 }
136 }