add references registered to supers of the class requested
[fedora-idea.git] / lang-impl / src / com / intellij / psi / impl / source / resolve / reference / ReferenceProvidersRegistry.java
blob820bda8b2b1057dd6363ef0817dc3ca8e82b7901
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;
20 import java.util.*;
21 import java.util.concurrent.ConcurrentMap;
23 /**
24 * Created by IntelliJ IDEA.
25 * User: ik
26 * Date: 27.03.2003
27 * Time: 17:13:45
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() {
50 /**
51 * @deprecated {@see com.intellij.psi.PsiReferenceContributor
53 public void registerReferenceProvider(@Nullable ElementFilter elementFilter,
54 @NotNull Class scope,
55 @NotNull PsiReferenceProvider provider,
56 double priority) {
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) {
65 myKnownSupers = null;
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);
78 return;
81 final CaseInsensitiveValuePatternCondition ciCondition =
82 ContainerUtil.findInstance(nameCondition.getNamePattern().getCondition().getConditions(), CaseInsensitiveValuePatternCondition.class);
83 if (ciCondition != null) {
84 registerNamedReferenceProvider(ciCondition.getValues(), new NamedObjectProviderBinding() {
85 @Nullable
86 protected String getName(final PsiElement position) {
87 return nameCondition.getPropertyValue(position);
89 }, scope, false, provider, priority, pattern);
90 return;
95 while (true) {
96 final SimpleProviderBinding providerBinding = myBindingsMap.get(scope);
97 if (providerBinding != null) {
98 providerBinding.registerProvider(provider, pattern, priority);
99 return;
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);
143 @Deprecated
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());
150 return providers;
153 @NotNull
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()) {
205 return result;
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);
218 } else {
219 for (PsiReference ref : refs) {
220 for (PsiReference reference : result) {
221 if (reference.getRangeInElement().contains(ref.getRangeInElement())) {
222 continue next;
226 result = ArrayUtil.mergeArrays(result, refs, PsiReference.class);
229 return result;