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
.Processor
;
29 import com
.intellij
.util
.SmartList
;
30 import com
.intellij
.util
.containers
.ConcurrentHashMap
;
31 import com
.intellij
.util
.containers
.ContainerUtil
;
32 import com
.intellij
.util
.containers
.MultiMap
;
33 import gnu
.trove
.THashMap
;
34 import org
.jetbrains
.annotations
.NotNull
;
35 import org
.jetbrains
.annotations
.Nullable
;
38 import java
.util
.concurrent
.ConcurrentMap
;
43 @SuppressWarnings({"unchecked"})
44 public class SemServiceImpl
extends SemService
{
45 private static final Comparator
<SemKey
> KEY_COMPARATOR
= new Comparator
<SemKey
>() {
46 public int compare(SemKey o1
, SemKey o2
) {
47 return o2
.getUniqueId() - o1
.getUniqueId();
50 private final ConcurrentMap
<PsiElement
, ConcurrentMap
<SemKey
, List
<SemElement
>>> myCache
= new ConcurrentHashMap
<PsiElement
, ConcurrentMap
<SemKey
, List
<SemElement
>>>();
51 private final Map
<SemKey
, Collection
<NullableFunction
<PsiElement
, ?
extends SemElement
>>> myProducers
= new THashMap
<SemKey
, Collection
<NullableFunction
<PsiElement
,?
extends SemElement
>>>();
52 private final MultiMap
<SemKey
, SemKey
> myInheritors
= new MultiMap
<SemKey
, SemKey
>();
53 private final Project myProject
;
55 private volatile boolean myInitialized
= false;
56 private boolean myBulkChange
= false;
58 public SemServiceImpl(Project project
, PsiManager psiManager
) {
60 project
.getMessageBus().connect().subscribe(ProjectTopics
.MODIFICATION_TRACKER
, new PsiModificationTracker
.Listener() {
61 public void modificationCountChanged() {
62 if (!isInsideAtomicChange()) {
67 ((PsiManagerEx
)psiManager
).registerRunnableToRunOnChange(new Runnable() {
69 if (!isInsideAtomicChange()) {
76 public void clearCache() {
81 public void performAtomicChange(@NotNull Runnable change
) {
82 ApplicationManager
.getApplication().assertWriteAccessAllowed();
84 final boolean oldValue
= myBulkChange
;
90 myBulkChange
= oldValue
;
98 public boolean isInsideAtomicChange() {
102 private void ensureInitialized() {
103 if (myInitialized
) return;
105 final MultiMap
<SemKey
, NullableFunction
<PsiElement
, ?
extends SemElement
>> map
= new MultiMap
<SemKey
, NullableFunction
<PsiElement
, ?
extends SemElement
>>();
107 final SemRegistrar registrar
= new SemRegistrar() {
108 public <T
extends SemElement
, V
extends PsiElement
> void registerSemElementProvider(SemKey
<T
> key
,
109 final ElementPattern
<?
extends V
> place
,
110 final NullableFunction
<V
, T
> provider
) {
111 map
.putValue(key
, new NullableFunction
<PsiElement
, SemElement
>() {
112 public SemElement
fun(PsiElement element
) {
113 if (place
.accepts(element
)) {
114 return provider
.fun((V
)element
);
121 for (SemContributor contributor
: myProject
.getExtensions(SemContributor
.EP_NAME
)) {
122 contributor
.registerSemProviders(registrar
);
125 synchronized (myCache
) {
126 if (myInitialized
) return;
128 assert myProducers
.isEmpty();
129 assert myInheritors
.isEmpty();
131 SemKey
[] allKeys
= map
.keySet().toArray(new SemKey
[map
.size()]);
132 for (final SemKey key
: allKeys
) {
133 myProducers
.put(key
, map
.get(key
));
135 ContainerUtil
.process(allKeys
, new Processor
<SemKey
>() {
136 public boolean process(SemKey key
) {
137 myInheritors
.putValue(key
, key
);
138 for (SemKey parent
: key
.getSupers()) {
139 myInheritors
.putValue(parent
, key
);
145 for (final SemKey each
: myInheritors
.keySet()) {
146 final List
<SemKey
> inheritors
= new ArrayList
<SemKey
>(myInheritors
.get(each
));
147 Collections
.sort(inheritors
, KEY_COMPARATOR
);
148 myInheritors
.put(each
, inheritors
);
151 myInitialized
= true;
156 public <T
extends SemElement
> List
<T
> getSemElements(SemKey
<T
> key
, @NotNull PsiElement psi
) {
159 List
<T
> cached
= _getCachedSemElements(key
, psi
, true);
160 if (cached
!= null) {
164 final ConcurrentMap
<SemKey
, List
<SemElement
>> map
= cacheOrGetMap(psi
);
165 LinkedHashSet
<T
> result
= null;
166 for (final SemKey each
: myInheritors
.get(key
)) {
167 List
<SemElement
> list
= ConcurrencyUtil
.cacheOrGet(map
, each
, createSemElements(each
, psi
));
168 if (!list
.isEmpty()) {
169 if (result
== null) result
= new LinkedHashSet
<T
>();
170 result
.addAll((List
<T
>)list
);
173 return result
== null ? Collections
.<T
>emptyList() : new ArrayList
<T
>(result
);
177 private List
<SemElement
> createSemElements(SemKey key
, PsiElement psi
) {
178 List
<SemElement
> result
= null;
179 final Collection
<NullableFunction
<PsiElement
, ?
extends SemElement
>> producers
= myProducers
.get(key
);
180 if (producers
!= null && !producers
.isEmpty()) {
181 for (final NullableFunction
<PsiElement
, ?
extends SemElement
> producer
: producers
) {
182 final SemElement element
= producer
.fun(psi
);
183 if (element
!= null) {
184 if (result
== null) result
= new SmartList
<SemElement
>();
189 return result
== null ? Collections
.<SemElement
>emptyList() : Collections
.unmodifiableList(result
);
193 public <T
extends SemElement
> List
<T
> getCachedSemElements(SemKey
<T
> key
, @NotNull PsiElement psi
) {
194 return _getCachedSemElements(key
, psi
, false);
198 private <T
extends SemElement
> List
<T
> _getCachedSemElements(SemKey
<T
> key
, PsiElement psi
, boolean paranoid
) {
199 final ConcurrentMap
<SemKey
, List
<SemElement
>> map
= myCache
.get(psi
);
200 if (map
== null) return null;
202 List
<T
> singleList
= null;
203 LinkedHashSet
<T
> result
= null;
204 final List
<SemKey
> inheritors
= (List
<SemKey
>)myInheritors
.get(key
);
205 //noinspection ForLoopReplaceableByForEach
206 for (int i
= 0; i
< inheritors
.size(); i
++) {
207 List
<T
> cached
= (List
<T
>)map
.get(inheritors
.get(i
));
209 if (cached
== null && paranoid
) {
213 if (cached
!= null && cached
!= Collections
.EMPTY_LIST
) {
214 if (singleList
== null) {
219 if (result
== null) {
220 result
= new LinkedHashSet
<T
>(singleList
);
222 result
.addAll(cached
);
227 if (result
== null) {
228 if (singleList
!= null) {
232 return Collections
.emptyList();
235 return new ArrayList
<T
>(result
);
238 public <T
extends SemElement
> void setCachedSemElement(SemKey
<T
> key
, @NotNull PsiElement psi
, @Nullable T semElement
) {
239 cacheOrGetMap(psi
).put(key
, ContainerUtil
.<SemElement
>createMaybeSingletonList(semElement
));
243 public <T
extends SemElement
> void clearCachedSemElements(@NotNull PsiElement psi
) {
247 private ConcurrentMap
<SemKey
, List
<SemElement
>> cacheOrGetMap(PsiElement psi
) {
248 ConcurrentMap
<SemKey
, List
<SemElement
>> map
= myCache
.get(psi
);
250 map
= ConcurrencyUtil
.cacheOrGet(myCache
, psi
, new ConcurrentHashMap
<SemKey
, List
<SemElement
>>());