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
;
37 import java
.util
.concurrent
.ConcurrentMap
;
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
) {
59 project
.getMessageBus().connect().subscribe(ProjectTopics
.MODIFICATION_TRACKER
, new PsiModificationTracker
.Listener() {
60 public void modificationCountChanged() {
61 if (!isInsideAtomicChange()) {
66 ((PsiManagerEx
)psiManager
).registerRunnableToRunOnChange(new Runnable() {
68 if (!isInsideAtomicChange()) {
75 public void clearCache() {
80 public void performAtomicChange(@NotNull Runnable change
) {
81 ApplicationManager
.getApplication().assertWriteAccessAllowed();
83 final boolean oldValue
= myBulkChange
;
89 myBulkChange
= oldValue
;
97 public boolean isInsideAtomicChange() {
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
);
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;
151 public <T
extends SemElement
> List
<T
> getSemElements(SemKey
<T
> key
, @NotNull PsiElement psi
) {
154 List
<T
> cached
= _getCachedSemElements(key
, psi
, true);
155 if (cached
!= null) {
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
);
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
>();
184 return result
== null ? Collections
.<SemElement
>emptyList() : Collections
.unmodifiableList(result
);
188 public <T
extends SemElement
> List
<T
> getCachedSemElements(SemKey
<T
> key
, @NotNull PsiElement psi
) {
189 return _getCachedSemElements(key
, psi
, false);
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
) {
208 if (cached
!= null && cached
!= Collections
.EMPTY_LIST
) {
209 if (singleList
== null) {
214 if (result
== null) {
215 result
= new LinkedHashSet
<T
>(singleList
);
217 result
.addAll(cached
);
222 if (result
== null) {
223 if (singleList
!= null) {
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
));
238 public <T
extends SemElement
> void clearCachedSemElements(@NotNull PsiElement psi
) {
242 private ConcurrentMap
<SemKey
, List
<SemElement
>> cacheOrGetMap(PsiElement psi
) {
243 ConcurrentMap
<SemKey
, List
<SemElement
>> map
= myCache
.get(psi
);
245 map
= ConcurrencyUtil
.cacheOrGet(myCache
, psi
, new ConcurrentHashMap
<SemKey
, List
<SemElement
>>());