blob: bcea41fd6ceb646594e323c43c43f6c7f3de3d46 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.resolve.reference;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.patterns.*;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.PsiReferenceRegistrar;
import com.intellij.psi.PsiReferenceService;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ConcurrentFactoryMap;
import com.intellij.util.containers.FactoryMap;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author Dmitry Avdeev
*/
public class PsiReferenceRegistrarImpl extends PsiReferenceRegistrar {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.resolve.reference.PsiReferenceRegistrarImpl");
private final Map<Class, SimpleProviderBinding<PsiReferenceProvider>> myBindingsMap = new THashMap<Class, SimpleProviderBinding<PsiReferenceProvider>>();
private final Map<Class, NamedObjectProviderBinding<PsiReferenceProvider>> myNamedBindingsMap = new THashMap<Class, NamedObjectProviderBinding<PsiReferenceProvider>>();
private final FactoryMap<Class, Class[]> myKnownSupers = new ConcurrentFactoryMap<Class, Class[]>() {
@Override
protected Class[] create(Class key) {
final Set<Class> result = new LinkedHashSet<Class>();
for (Class candidate : myBindingsMap.keySet()) {
if (candidate.isAssignableFrom(key)) {
result.add(candidate);
}
}
for (Class candidate : myNamedBindingsMap.keySet()) {
if (candidate.isAssignableFrom(key)) {
result.add(candidate);
}
}
if (result.isEmpty()) {
return ArrayUtil.EMPTY_CLASS_ARRAY;
}
return result.toArray(new Class[result.size()]);
}
};
private boolean myInitialized;
public void markInitialized() {
myInitialized = true;
}
@Override
public <T extends PsiElement> void registerReferenceProvider(@NotNull ElementPattern<T> pattern,
@NotNull PsiReferenceProvider provider,
double priority) {
if (myInitialized && !ApplicationManager.getApplication().isUnitTestMode()) {
LOG.error("Reference provider registration is only allowed from PsiReferenceContributor");
}
myKnownSupers.clear(); // we should clear the cache
final Class scope = pattern.getCondition().getInitialCondition().getAcceptedClass();
final List<PatternCondition<? super T>> conditions = pattern.getCondition().getConditions();
for (PatternCondition<? super T> _condition : conditions) {
if (!(_condition instanceof PsiNamePatternCondition)) {
continue;
}
final PsiNamePatternCondition<?> nameCondition = (PsiNamePatternCondition)_condition;
List<PatternCondition<? super String>> conditions1 = nameCondition.getNamePattern().getCondition().getConditions();
for (PatternCondition<? super String> condition1 : conditions1) {
if (condition1 instanceof ValuePatternCondition) {
final Collection<String> strings = ((ValuePatternCondition)condition1).getValues();
registerNamedReferenceProvider(ArrayUtil.toStringArray(strings), nameCondition, scope, true, provider, priority, pattern);
return;
}
if (condition1 instanceof CaseInsensitiveValuePatternCondition) {
final String[] strings = ((CaseInsensitiveValuePatternCondition)condition1).getValues();
registerNamedReferenceProvider(strings, nameCondition, scope, false, provider, priority, pattern);
return;
}
}
break;
}
SimpleProviderBinding<PsiReferenceProvider> providerBinding = myBindingsMap.get(scope);
if (providerBinding == null) {
myBindingsMap.put(scope, providerBinding = new SimpleProviderBinding<PsiReferenceProvider>());
}
providerBinding.registerProvider(provider, pattern, priority);
}
public void unregisterReferenceProvider(@NotNull Class scope, @NotNull PsiReferenceProvider provider) {
ProviderBinding<PsiReferenceProvider> providerBinding = myBindingsMap.get(scope);
providerBinding.unregisterProvider(provider);
}
private void registerNamedReferenceProvider(@NotNull String[] names,
final PsiNamePatternCondition<?> nameCondition,
@NotNull Class scopeClass,
final boolean caseSensitive,
@NotNull PsiReferenceProvider provider,
final double priority,
@NotNull ElementPattern pattern) {
NamedObjectProviderBinding<PsiReferenceProvider> providerBinding = myNamedBindingsMap.get(scopeClass);
if (providerBinding == null) {
myNamedBindingsMap.put(scopeClass, providerBinding = new NamedObjectProviderBinding<PsiReferenceProvider>() {
@Override
protected String getName(final PsiElement position) {
return nameCondition.getPropertyValue(position);
}
});
}
providerBinding.registerProvider(names, pattern, caseSensitive, provider, priority);
}
/**
* @see com.intellij.psi.PsiReferenceContributor
* @deprecated
*/
public void registerReferenceProvider(@NotNull Class scope, @NotNull PsiReferenceProvider provider) {
registerReferenceProvider(PlatformPatterns.psiElement(scope), provider, DEFAULT_PRIORITY);
}
@NotNull
List<ProviderBinding.ProviderInfo<PsiReferenceProvider,ProcessingContext>> getPairsByElement(@NotNull PsiElement element,
@NotNull PsiReferenceService.Hints hints) {
final Class<? extends PsiElement> clazz = element.getClass();
List<ProviderBinding.ProviderInfo<PsiReferenceProvider, ProcessingContext>> ret = null;
for (Class aClass : myKnownSupers.get(clazz)) {
SimpleProviderBinding<PsiReferenceProvider> simpleBinding = myBindingsMap.get(aClass);
NamedObjectProviderBinding<PsiReferenceProvider> namedBinding = myNamedBindingsMap.get(aClass);
if (simpleBinding == null && namedBinding == null) continue;
if (ret == null) ret = new SmartList<ProviderBinding.ProviderInfo<PsiReferenceProvider, ProcessingContext>>();
if (simpleBinding != null) {
simpleBinding.addAcceptableReferenceProviders(element, ret, hints);
}
if (namedBinding != null) {
namedBinding.addAcceptableReferenceProviders(element, ret, hints);
}
}
return ret == null ? Collections.<ProviderBinding.ProviderInfo<PsiReferenceProvider, ProcessingContext>>emptyList() : ret;
}
}