1 package com
.intellij
.psi
.impl
.source
.resolve
.reference
;
3 import com
.intellij
.codeInsight
.completion
.LegacyCompletionContributor
;
4 import com
.intellij
.openapi
.components
.ServiceManager
;
5 import com
.intellij
.openapi
.extensions
.Extensions
;
6 import com
.intellij
.openapi
.project
.Project
;
7 import com
.intellij
.openapi
.util
.Trinity
;
8 import com
.intellij
.patterns
.*;
9 import com
.intellij
.psi
.*;
10 import com
.intellij
.psi
.filters
.ElementFilter
;
11 import com
.intellij
.psi
.filters
.position
.FilterPattern
;
12 import com
.intellij
.util
.*;
13 import com
.intellij
.util
.containers
.ConcurrentWeakHashMap
;
14 import com
.intellij
.util
.containers
.ContainerUtil
;
15 import com
.intellij
.util
.containers
.MultiMap
;
16 import gnu
.trove
.THashSet
;
17 import org
.jetbrains
.annotations
.NotNull
;
18 import org
.jetbrains
.annotations
.Nullable
;
21 import java
.util
.concurrent
.ConcurrentMap
;
24 * Created by IntelliJ IDEA.
28 * To change this template use Options | File Templates.
30 public class ReferenceProvidersRegistry
implements PsiReferenceRegistrar
{
31 private final ConcurrentMap
<Class
,SimpleProviderBinding
> myBindingsMap
= new ConcurrentWeakHashMap
<Class
, SimpleProviderBinding
>();
32 private final ConcurrentMap
<Class
,NamedObjectProviderBinding
> myNamedBindingsMap
= new ConcurrentWeakHashMap
<Class
, NamedObjectProviderBinding
>();
33 private MultiMap
<Class
,Class
> myKnownSupers
;
34 private boolean myInitialized
;
36 private static final Comparator
<Trinity
<PsiReferenceProvider
,ProcessingContext
,Double
>> PRIORITY_COMPARATOR
= new Comparator
<Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
>>() {
37 public int compare(final Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
> o1
,
38 final Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
> o2
) {
39 return o2
.getThird().compareTo(o1
.getThird());
43 public static ReferenceProvidersRegistry
getInstance(@NotNull Project project
) {
44 return ServiceManager
.getService(project
, ReferenceProvidersRegistry
.class);
47 private ReferenceProvidersRegistry() {
51 * @deprecated {@see com.intellij.psi.PsiReferenceContributor
53 public void registerReferenceProvider(@Nullable ElementFilter elementFilter
,
55 @NotNull PsiReferenceProvider provider
,
57 registerReferenceProvider(PlatformPatterns
.psiElement(scope
).and(new FilterPattern(elementFilter
)), provider
, priority
);
61 public void registerReferenceProvider(@NotNull ElementPattern
<?
extends PsiElement
> pattern
, @NotNull PsiReferenceProvider provider
) {
62 registerReferenceProvider(pattern
, provider
, DEFAULT_PRIORITY
);
64 public <T
extends PsiElement
> void registerReferenceProvider(@NotNull ElementPattern
<T
> pattern
, @NotNull PsiReferenceProvider provider
, double priority
) {
66 final Class scope
= pattern
.getCondition().getInitialCondition().getAcceptedClass();
67 final PsiNamePatternCondition
<?
> nameCondition
= ContainerUtil
.findInstance(pattern
.getCondition().getConditions(), PsiNamePatternCondition
.class);
68 if (nameCondition
!= null) {
69 final ValuePatternCondition
<String
> valueCondition
=
70 ContainerUtil
.findInstance(nameCondition
.getNamePattern().getCondition().getConditions(), ValuePatternCondition
.class);
71 if (valueCondition
!= null) {
72 final Collection
<String
> strings
= valueCondition
.getValues();
73 registerNamedReferenceProvider(strings
.toArray(new String
[strings
.size()]), new NamedObjectProviderBinding() {
74 protected String
getName(final PsiElement position
) {
75 return nameCondition
.getPropertyValue(position
);
77 }, scope
, true, provider
, priority
, pattern
);
81 final CaseInsensitiveValuePatternCondition ciCondition
=
82 ContainerUtil
.findInstance(nameCondition
.getNamePattern().getCondition().getConditions(), CaseInsensitiveValuePatternCondition
.class);
83 if (ciCondition
!= null) {
84 registerNamedReferenceProvider(ciCondition
.getValues(), new NamedObjectProviderBinding() {
86 protected String
getName(final PsiElement position
) {
87 return nameCondition
.getPropertyValue(position
);
89 }, scope
, false, provider
, priority
, pattern
);
96 final SimpleProviderBinding providerBinding
= myBindingsMap
.get(scope
);
97 if (providerBinding
!= null) {
98 providerBinding
.registerProvider(provider
, pattern
, priority
);
102 final SimpleProviderBinding binding
= new SimpleProviderBinding();
103 binding
.registerProvider(provider
, pattern
, priority
);
104 if (myBindingsMap
.putIfAbsent(scope
, binding
) == null) break;
109 * @deprecated {@see com.intellij.psi.PsiReferenceContributor
111 public void registerReferenceProvider(@Nullable ElementFilter elementFilter
,
112 @NotNull Class scope
,
113 @NotNull PsiReferenceProvider provider
) {
114 registerReferenceProvider(elementFilter
, scope
, provider
, DEFAULT_PRIORITY
);
117 public void unregisterReferenceProvider(@NotNull Class scope
, @NotNull PsiReferenceProvider provider
) {
118 final ProviderBinding providerBinding
= myBindingsMap
.get(scope
);
119 providerBinding
.unregisterProvider(provider
);
123 private void registerNamedReferenceProvider(final String
[] names
, final NamedObjectProviderBinding binding
,
124 final Class scopeClass
,
125 final boolean caseSensitive
,
126 final PsiReferenceProvider provider
, final double priority
, final ElementPattern pattern
) {
127 NamedObjectProviderBinding providerBinding
= myNamedBindingsMap
.get(scopeClass
);
129 if (providerBinding
== null) {
130 providerBinding
= ConcurrencyUtil
.cacheOrGet(myNamedBindingsMap
, scopeClass
, binding
);
133 providerBinding
.registerProvider(names
, pattern
, caseSensitive
, provider
, priority
);
137 * @deprecated {@see com.intellij.psi.PsiReferenceContributor
139 public void registerReferenceProvider(@NotNull Class scope
, @NotNull PsiReferenceProvider provider
) {
140 registerReferenceProvider(null, scope
, provider
);
144 public List
<PsiReferenceProvider
> getProvidersByElement(@NotNull PsiElement element
, @NotNull Class clazz
) {
145 final List
<Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
>> list
= getPairsByElement(element
, clazz
);
146 final ArrayList
<PsiReferenceProvider
> providers
= new ArrayList
<PsiReferenceProvider
>(list
.size());
147 for (Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
> trinity
: list
) {
148 providers
.add(trinity
.getFirst());
154 private List
<Trinity
<PsiReferenceProvider
,ProcessingContext
,Double
>> getPairsByElement(@NotNull PsiElement element
, @NotNull Class clazz
) {
155 assert ReflectionCache
.isInstance(element
, clazz
);
157 synchronized (myBindingsMap
) {
158 if (!myInitialized
) {
159 for (final PsiReferenceContributor contributor
: Extensions
.getExtensions(PsiReferenceContributor
.EP_NAME
)) {
160 contributor
.registerReferenceProviders(this);
162 myInitialized
= true;
166 MultiMap
<Class
, Class
> knownSupers
= myKnownSupers
;
167 if (knownSupers
== null) {
168 knownSupers
= new MultiMap
<Class
, Class
>();
169 Set
<Class
> allClasses
= new THashSet
<Class
>();
170 allClasses
.addAll(myBindingsMap
.keySet());
171 allClasses
.addAll(myNamedBindingsMap
.keySet());
172 for (final Class ancestor
: allClasses
) {
173 for (final Class descendant
: allClasses
) {
174 if (ancestor
.isAssignableFrom(descendant
)) {
175 knownSupers
.putValue(descendant
, ancestor
);
179 myKnownSupers
= knownSupers
;
182 List
<Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
>> ret
= null;
183 for (final Class aClass
: knownSupers
.get(clazz
)) {
184 final SimpleProviderBinding simpleBinding
= myBindingsMap
.get(aClass
);
185 final NamedObjectProviderBinding namedBinding
= myNamedBindingsMap
.get(aClass
);
186 if (simpleBinding
== null && namedBinding
== null) continue;
188 if (ret
== null) ret
= new SmartList
<Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
>>();
189 if (simpleBinding
!= null) {
190 simpleBinding
.addAcceptableReferenceProviders(element
, ret
);
192 if (namedBinding
!= null) {
193 namedBinding
.addAcceptableReferenceProviders(element
, ret
);
196 return ret
== null ? Collections
.<Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
>>emptyList() : ret
;
199 public static PsiReference
[] getReferencesFromProviders(PsiElement context
, @NotNull Class clazz
){
200 assert context
.isValid() : "Invalid context: " + context
;
202 PsiReference
[] result
= PsiReference
.EMPTY_ARRAY
;
203 final List
<Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
>> providers
= getInstance(context
.getProject()).getPairsByElement(context
, clazz
);
204 if (providers
.isEmpty()) {
207 Collections
.sort(providers
, PRIORITY_COMPARATOR
);
208 if (LegacyCompletionContributor
.DEBUG
) {
209 System
.out
.println("ReferenceProvidersRegistry.getReferencesFromProviders");
210 System
.out
.println("providers = " + providers
);
213 final Double maxPriority
= providers
.get(0).getThird();
214 next
: for (Trinity
<PsiReferenceProvider
, ProcessingContext
, Double
> trinity
: providers
) {
215 final PsiReference
[] refs
= trinity
.getFirst().getReferencesByElement(context
, trinity
.getSecond());
216 if (trinity
.getThird().equals(maxPriority
)) {
217 result
= ArrayUtil
.mergeArrays(result
, refs
, PsiReference
.class);
219 for (PsiReference ref
: refs
) {
220 for (PsiReference reference
: result
) {
221 if (reference
.getRangeInElement().contains(ref
.getRangeInElement())) {
226 result
= ArrayUtil
.mergeArrays(result
, refs
, PsiReference
.class);