add file color for navigation list items which are not PsiElements but could fetch...
[fedora-idea.git] / platform / lang-impl / src / com / intellij / semantic / SemServiceImpl.java
blob571c108e1d812176306e24cdf36e228495f636cb
1 /*
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.
16 package com.intellij.semantic;
18 import com.intellij.ProjectTopics;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.patterns.ElementPattern;
22 import com.intellij.psi.PsiElement;
23 import com.intellij.psi.PsiManager;
24 import com.intellij.psi.impl.PsiManagerEx;
25 import com.intellij.psi.util.PsiModificationTracker;
26 import com.intellij.util.ConcurrencyUtil;
27 import com.intellij.util.NullableFunction;
28 import com.intellij.util.SmartList;
29 import com.intellij.util.containers.ConcurrentHashMap;
30 import com.intellij.util.containers.ContainerUtil;
31 import com.intellij.util.containers.MultiMap;
32 import gnu.trove.THashMap;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
36 import java.util.*;
37 import java.util.concurrent.ConcurrentMap;
39 /**
40 * @author peter
42 @SuppressWarnings({"unchecked"})
43 public class SemServiceImpl extends SemService{
44 private static final Comparator<SemKey> KEY_COMPARATOR = new Comparator<SemKey>() {
45 public int compare(SemKey o1, SemKey o2) {
46 return o2.getUniqueId() - o1.getUniqueId();
49 private final ConcurrentMap<PsiElement, ConcurrentMap<SemKey, List<SemElement>>> myCache = new ConcurrentHashMap<PsiElement, ConcurrentMap<SemKey, List<SemElement>>>();
50 private final Map<SemKey, Collection<NullableFunction<PsiElement, ? extends SemElement>>> myProducers = new THashMap<SemKey, Collection<NullableFunction<PsiElement,? extends SemElement>>>();
51 private final MultiMap<SemKey, SemKey> myInheritors = new MultiMap<SemKey, SemKey>();
52 private final Project myProject;
54 private volatile boolean myInitialized = false;
55 private boolean myBulkChange = false;
57 public SemServiceImpl(Project project, PsiManager psiManager) {
58 myProject = project;
59 project.getMessageBus().connect().subscribe(ProjectTopics.MODIFICATION_TRACKER, new PsiModificationTracker.Listener() {
60 public void modificationCountChanged() {
61 if (!isInsideAtomicChange()) {
62 clearCache();
65 });
66 ((PsiManagerEx)psiManager).registerRunnableToRunOnChange(new Runnable() {
67 public void run() {
68 if (!isInsideAtomicChange()) {
69 clearCache();
72 });
75 public void clearCache() {
76 myCache.clear();
79 @Override
80 public void performAtomicChange(@NotNull Runnable change) {
81 ApplicationManager.getApplication().assertWriteAccessAllowed();
83 final boolean oldValue = myBulkChange;
84 myBulkChange = true;
85 try {
86 change.run();
88 finally {
89 myBulkChange = oldValue;
90 if (!oldValue) {
91 clearCache();
96 @Override
97 public boolean isInsideAtomicChange() {
98 return myBulkChange;
101 private void ensureInitialized() {
102 if (myInitialized) return;
104 final MultiMap<SemKey, NullableFunction<PsiElement, ? extends SemElement>> map = new MultiMap<SemKey, NullableFunction<PsiElement, ? extends SemElement>>();
106 final SemRegistrar registrar = new SemRegistrar() {
107 public <T extends SemElement, V extends PsiElement> void registerSemElementProvider(SemKey<T> key,
108 final ElementPattern<? extends V> place,
109 final NullableFunction<V, T> provider) {
110 map.putValue(key, new NullableFunction<PsiElement, SemElement>() {
111 public SemElement fun(PsiElement element) {
112 if (place.accepts(element)) {
113 return provider.fun((V)element);
115 return null;
120 for (SemContributor contributor : myProject.getExtensions(SemContributor.EP_NAME)) {
121 contributor.registerSemProviders(registrar);
124 synchronized (myCache) {
125 if (myInitialized) return;
127 assert myProducers.isEmpty();
128 assert myInheritors.isEmpty();
130 SemKey[] allKeys = map.keySet().toArray(new SemKey[map.size()]);
131 for (final SemKey key : allKeys) {
132 myProducers.put(key, map.get(key));
133 for (final SemKey concrete : allKeys) {
134 if (concrete.isKindOf(key)) {
135 myInheritors.putValue(key, concrete);
140 for (final SemKey each : myInheritors.keySet()) {
141 final List<SemKey> inheritors = new ArrayList<SemKey>(myInheritors.get(each));
142 Collections.sort(inheritors, KEY_COMPARATOR);
143 myInheritors.put(each, inheritors);
146 myInitialized = true;
150 @Nullable
151 public <T extends SemElement> List<T> getSemElements(SemKey<T> key, @NotNull PsiElement psi) {
152 ensureInitialized();
154 List<T> cached = _getCachedSemElements(key, psi, true);
155 if (cached != null) {
156 return cached;
159 final ConcurrentMap<SemKey, List<SemElement>> map = cacheOrGetMap(psi);
160 LinkedHashSet<T> result = null;
161 for (final SemKey each : myInheritors.get(key)) {
162 List<SemElement> list = ConcurrencyUtil.cacheOrGet(map, each, createSemElements(each, psi));
163 if (!list.isEmpty()) {
164 if (result == null) result = new LinkedHashSet<T>();
165 result.addAll((List<T>)list);
168 return result == null ? Collections.<T>emptyList() : new ArrayList<T>(result);
171 @NotNull
172 private List<SemElement> createSemElements(SemKey key, PsiElement psi) {
173 List<SemElement> result = null;
174 final Collection<NullableFunction<PsiElement, ? extends SemElement>> producers = myProducers.get(key);
175 if (!producers.isEmpty()) {
176 for (final NullableFunction<PsiElement, ? extends SemElement> producer : producers) {
177 final SemElement element = producer.fun(psi);
178 if (element != null) {
179 if (result == null) result = new SmartList<SemElement>();
180 result.add(element);
184 return result == null ? Collections.<SemElement>emptyList() : Collections.unmodifiableList(result);
187 @Nullable
188 public <T extends SemElement> List<T> getCachedSemElements(SemKey<T> key, @NotNull PsiElement psi) {
189 return _getCachedSemElements(key, psi, false);
192 @Nullable
193 private <T extends SemElement> List<T> _getCachedSemElements(SemKey<T> key, PsiElement psi, boolean paranoid) {
194 final ConcurrentMap<SemKey, List<SemElement>> map = myCache.get(psi);
195 if (map == null) return null;
197 List<T> singleList = null;
198 LinkedHashSet<T> result = null;
199 final List<SemKey> inheritors = (List<SemKey>)myInheritors.get(key);
200 //noinspection ForLoopReplaceableByForEach
201 for (int i = 0; i < inheritors.size(); i++) {
202 List<T> cached = (List<T>)map.get(inheritors.get(i));
204 if (cached == null && paranoid) {
205 return null;
208 if (cached != null && cached != Collections.EMPTY_LIST) {
209 if (singleList == null) {
210 singleList = cached;
211 continue;
214 if (result == null) {
215 result = new LinkedHashSet<T>(singleList);
217 result.addAll(cached);
222 if (result == null) {
223 if (singleList != null) {
224 return singleList;
227 return Collections.emptyList();
230 return new ArrayList<T>(result);
233 public <T extends SemElement> void setCachedSemElement(SemKey<T> key, @NotNull PsiElement psi, @Nullable T semElement) {
234 cacheOrGetMap(psi).put(key, ContainerUtil.<SemElement>createMaybeSingletonList(semElement));
237 @Override
238 public <T extends SemElement> void clearCachedSemElements(@NotNull PsiElement psi) {
239 myCache.remove(psi);
242 private ConcurrentMap<SemKey, List<SemElement>> cacheOrGetMap(PsiElement psi) {
243 ConcurrentMap<SemKey, List<SemElement>> map = myCache.get(psi);
244 if (map == null) {
245 map = ConcurrencyUtil.cacheOrGet(myCache, psi, new ConcurrentHashMap<SemKey, List<SemElement>>());
247 return map;