Methods chains completion for arrays

This commit is contained in:
Dmitry Batkovich
2014-02-17 14:36:14 +04:00
parent 68622f7073
commit ba7188dc42
23 changed files with 397 additions and 217 deletions
@@ -33,16 +33,16 @@ public enum ClassFilesIndexFeature {
@NotNull
private final String myKey;
@NotNull
private final Collection<? extends MethodsUsageIndexConfigure> myRequiredIndicesConfigures;
private final Collection<? extends ClassFilesIndexConfigure> myRequiredIndicesConfigures;
ClassFilesIndexFeature(@NotNull final String key,
@NotNull final Collection<? extends MethodsUsageIndexConfigure> requiredIndicesConfigures) {
@NotNull final Collection<? extends ClassFilesIndexConfigure> requiredIndicesConfigures) {
myKey = key;
myRequiredIndicesConfigures = requiredIndicesConfigures;
}
ClassFilesIndexFeature(@NotNull final String key, @NotNull final MethodsUsageIndexConfigure requiredConfigure) {
this(key, Collections.<MethodsUsageIndexConfigure>singleton(requiredConfigure));
ClassFilesIndexFeature(@NotNull final String key, @NotNull final ClassFilesIndexConfigure requiredConfigure) {
this(key, Collections.<ClassFilesIndexConfigure>singleton(requiredConfigure));
}
public RegistryValue getRegistryValue() {
@@ -70,7 +70,7 @@ public enum ClassFilesIndexFeature {
}
@NotNull
public Collection<? extends MethodsUsageIndexConfigure> getRequiredIndicesConfigures() {
public Collection<? extends ClassFilesIndexConfigure> getRequiredIndicesConfigures() {
return myRequiredIndicesConfigures;
}
}
@@ -86,7 +86,7 @@ public class ClassFilesIndexFeaturesHolder extends AbstractProjectComponent {
}
for (final ClassFilesIndexFeature feature : ClassFilesIndexFeature.values()) {
if (feature.isEnabled() && !myEnabledFeatures.containsKey(feature)) {
for (final MethodsUsageIndexConfigure configure : feature.getRequiredIndicesConfigures()) {
for (final ClassFilesIndexConfigure configure : feature.getRequiredIndicesConfigures()) {
if (!myEnabledIndexReaders.containsKey(configure)) {
notAvailableConfiguresVisitor.process(configure);
}
@@ -96,7 +96,7 @@ public class ClassFilesIndexFeaturesHolder extends AbstractProjectComponent {
}
private synchronized void disposeFeature(final ClassFilesIndexFeature featureToRemove) {
for (final MethodsUsageIndexConfigure requiredConfigure : featureToRemove.getRequiredIndicesConfigures()) {
for (final ClassFilesIndexConfigure requiredConfigure : featureToRemove.getRequiredIndicesConfigures()) {
boolean needClose = true;
for (final ClassFilesIndexFeature enabledFeature : myEnabledFeatures.keySet()) {
if (!enabledFeature.equals(featureToRemove) && enabledFeature.getRequiredIndicesConfigures().contains(requiredConfigure)) {
@@ -116,10 +116,10 @@ public class ClassFilesIndexFeaturesHolder extends AbstractProjectComponent {
if (myEnabledFeatures.containsKey(feature)) {
throw new IllegalStateException(String.format("feature %s already contains", feature.getKey()));
}
final Map<MethodsUsageIndexConfigure, ClassFilesIndexReaderBase> newIndices =
new HashMap<MethodsUsageIndexConfigure, ClassFilesIndexReaderBase>();
final Map<ClassFilesIndexConfigure, ClassFilesIndexReaderBase> newIndices =
new HashMap<ClassFilesIndexConfigure, ClassFilesIndexReaderBase>();
FeatureState newFeatureState = FeatureState.AVAILABLE;
for (final MethodsUsageIndexConfigure requiredConfigure : feature.getRequiredIndicesConfigures()) {
for (final ClassFilesIndexConfigure requiredConfigure : feature.getRequiredIndicesConfigures()) {
boolean isIndexAlreadyLoaded = false;
for (final ClassFilesIndexFeature enabledFeature : myEnabledFeatures.keySet()) {
if (enabledFeature.getRequiredIndicesConfigures().contains(requiredConfigure)) {
@@ -55,9 +55,6 @@ public abstract class ClassFilesIndexReaderBase<K, V> {
}
}
/**
* All inheritors MUST have constructor with only one parameter - Project
*/
@SuppressWarnings("ConstantConditions")
protected ClassFilesIndexReaderBase(final KeyDescriptor<K> keyDescriptor,
final DataExternalizer<V> valueExternalizer,
@@ -149,5 +146,4 @@ public abstract class ClassFilesIndexReaderBase<K, V> {
return true;
}
}
}
@@ -52,7 +52,7 @@ public class CachedRelevantStaticMethodSearcher {
final ChainCompletionContext completionContext) {
if (resultQualifiedClassName == null ||
ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(resultQualifiedClassName) ||
completionContext.getTargetQName().equals(resultQualifiedClassName)) {
completionContext.getTarget().equals(resultQualifiedClassName)) {
return Collections.emptyList();
}
final TreeSet<UsageIndexValue> indexValues = myIndexReader.getMethods(resultQualifiedClassName);
@@ -68,7 +68,7 @@ public class CachedRelevantStaticMethodSearcher {
else {
final PsiMethod[] methods = completionContext.resolveNotDeprecated(methodInvocation);
method = MethodChainsSearchUtil
.getMethodWithMinNotPrimitiveParameters(methods, Collections.singleton(completionContext.getTargetQName()));
.getMethodWithMinNotPrimitiveParameters(methods, Collections.singleton(completionContext.getTarget().getClassQName()));
myCachedResolveResults.put(methodInvocation, method);
if (method == null) {
return Collections.emptyList();
@@ -16,19 +16,19 @@
package com.intellij.compiler.classFilesIndex.chainsSearch;
import com.intellij.compiler.classFilesIndex.chainsSearch.context.ChainCompletionContext;
import com.intellij.compiler.classFilesIndex.chainsSearch.context.TargetType;
import com.intellij.compiler.classFilesIndex.impl.MethodsUsageIndexReader;
import com.intellij.compiler.classFilesIndex.impl.UsageIndexValue;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.*;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.classFilesIndex.indexer.impl.MethodIncompleteSignature;
import org.jetbrains.jps.incremental.storage.BuildDataManager;
import java.util.*;
@@ -42,21 +42,36 @@ public final class ChainsSearcher {
private static final Logger LOG = Logger.getInstance(ChainsSearcher.class);
private static final double NEXT_METHOD_IN_CHAIN_RATIO = 1.5;
public static List<MethodsChain> search(final MethodsUsageIndexReader indexReader,
final String targetQName,
public static List<MethodsChain> search(final int pathMaximalLength,
final TargetType targetType,
final Set<String> contextQNames,
final int maxResultSize,
final int pathMaximalLength,
final ChainCompletionContext context) {
final SearchInitializer initializer = createInitializer(targetQName, indexReader, context.getExcludedQNames(), context);
return search(indexReader, initializer, contextQNames, pathMaximalLength, maxResultSize, targetQName, context);
final ChainCompletionContext context,
final MethodsUsageIndexReader methodsUsageIndexReader) {
final SearchInitializer initializer =
createInitializer(targetType,
context.getExcludedQNames(),
methodsUsageIndexReader,
context);
if (initializer == null) {
return Collections.emptyList();
}
return search(methodsUsageIndexReader,
initializer,
contextQNames,
pathMaximalLength,
maxResultSize,
targetType.getClassQName(),
context);
}
private static SearchInitializer createInitializer(final String targetQName,
final MethodsUsageIndexReader indexReader,
@Nullable
private static SearchInitializer createInitializer(final TargetType target,
final Set<String> excludedParamsTypesQNames,
final MethodsUsageIndexReader methodsUsageIndexReader,
final ChainCompletionContext context) {
return new SearchInitializer(indexReader.getMethods(targetQName), targetQName, excludedParamsTypesQNames, context);
final TreeSet<UsageIndexValue> methods = methodsUsageIndexReader.getMethods(target.getClassQName());
return new SearchInitializer(methods, target.getClassQName(), excludedParamsTypesQNames, context);
}
@NotNull
@@ -80,22 +95,20 @@ public final class ChainsSearcher {
@Override
public WeightAware<Pair<MethodIncompleteSignature, MethodsChain>> fun(
final WeightAware<MethodIncompleteSignature> methodIncompleteSignatureWeightAware) {
final MethodIncompleteSignature
underlying =
methodIncompleteSignatureWeightAware
.getUnderlying();
final MethodIncompleteSignature underlying = methodIncompleteSignatureWeightAware.getUnderlying();
return new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>(
new Pair<MethodIncompleteSignature, MethodsChain>(
underlying, new MethodsChain(
context.resolveNotDeprecated(
underlying),
methodIncompleteSignatureWeightAware
.getWeight(),
underlying.getOwner())),
methodIncompleteSignatureWeightAware
.getWeight());
underlying,
new MethodsChain(context.resolveNotDeprecated(underlying),
methodIncompleteSignatureWeightAware.getWeight(),
underlying.getOwner()
)
),
methodIncompleteSignatureWeightAware.getWeight()
);
}
}));
}
));
int maxWeight = 0;
for (final MethodsChain methodsChain : knownDistance.values()) {
@@ -118,7 +131,8 @@ public final class ChainsSearcher {
result.add(currentVertex.getUnderlying().getSecond());
continue;
}
final SortedSet<UsageIndexValue> nextMethods = indexReader.getMethods(currentVertexUnderlying.getFirst().getOwner());
final String currentReturnType = currentVertexUnderlying.getFirst().getOwner();
final SortedSet<UsageIndexValue> nextMethods = indexReader.getMethods(currentReturnType);
final MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>> currentSignatures =
new MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>>(maxResultSize);
for (final UsageIndexValue indexValue : nextMethods) {
@@ -133,8 +147,10 @@ public final class ChainsSearcher {
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
final PsiMethod[] psiMethods = context.resolveNotDeprecated(methodInvocation);
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, allExcludedNames)) {
final MethodsChain newBestMethodsChain = currentVertexMethodsChain.addEdge(psiMethods, indexValue.getMethodIncompleteSignature().getOwner(), vertexDistance);
currentSignatures.add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(), vertexDistance));
final MethodsChain newBestMethodsChain =
currentVertexMethodsChain.addEdge(psiMethods, indexValue.getMethodIncompleteSignature().getOwner(), vertexDistance);
currentSignatures
.add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(), vertexDistance));
knownDistance.put(vertex, newBestMethodsChain);
}
}
@@ -302,4 +318,4 @@ public final class ChainsSearcher {
return resultHolder.getRawResult();
}
}
}
}
@@ -60,8 +60,7 @@ public class MethodsChainLookupRangingHelper {
@SuppressWarnings("ConstantConditions")
@Nullable
private static WeightableChainLookupElement chainToWeightableLookupElement(final MethodsChain chain,
final ChainCompletionContext context) {
private static LookupElement chainToWeightableLookupElement(final MethodsChain chain, final ChainCompletionContext context) {
final int chainSize = chain.size();
assert chainSize != 0;
final int lastMethodWeight = chain.getChainWeight();
@@ -76,7 +75,7 @@ public class MethodsChainLookupRangingHelper {
for (final PsiMethod[] psiMethods : chain.getPath()) {
final PsiMethod method =
MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(psiMethods, Collections.singleton(context.getTargetQName()));
MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(psiMethods, Collections.singleton(context.getTarget().getClassQName()));
if (method == null) {
return null;
}
@@ -141,52 +140,49 @@ public class MethodsChainLookupRangingHelper {
for (int i = 0; i < parameters.length; i++) {
final PsiParameter parameter = parameters[i];
final String typeQName = parameter.getType().getCanonicalText();
if (typeQName != null) {
if (JAVA_LANG_STRING.equals(typeQName)) {
final PsiVariable relevantStringVar = context.findRelevantStringInContext(parameter.getName());
if (relevantStringVar == null) {
notMatchedStringVars++;
}
else {
parametersMap.put(i, new VariableSubLookupElement(relevantStringVar));
}
if (JAVA_LANG_STRING.equals(typeQName)) {
final PsiVariable relevantStringVar = context.findRelevantStringInContext(parameter.getName());
if (relevantStringVar == null) {
notMatchedStringVars++;
}
else if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(typeQName)) {
final Collection<PsiVariable> contextVariables = context.getVariables(typeQName);
final PsiVariable contextVariable = ContainerUtil.getFirstItem(contextVariables, null);
if (contextVariable != null) {
if (contextVariables.size() == 1) parametersMap.put(i, new VariableSubLookupElement(contextVariable));
matchedParametersInContext++;
continue;
}
final Collection<ContextRelevantVariableGetter> relevantVariablesGetters = context.getRelevantVariablesGetters(typeQName);
final ContextRelevantVariableGetter contextVariableGetter = ContainerUtil.getFirstItem(relevantVariablesGetters, null);
if (contextVariableGetter != null) {
if (relevantVariablesGetters.size() == 1) parametersMap.put(i, contextVariableGetter.createSubLookupElement());
matchedParametersInContext++;
continue;
}
final Collection<PsiMethod> containingClassMethods = context.getContainingClassMethods(typeQName);
final PsiMethod contextRelevantGetter = ContainerUtil.getFirstItem(containingClassMethods, null);
if (contextRelevantGetter != null) {
if (containingClassMethods.size() == 1) parametersMap.put(i, new GetterLookupSubLookupElement(method.getName()));
matchedParametersInContext++;
continue;
}
//todo
final ContextRelevantStaticMethod contextRelevantStaticMethod =
ContainerUtil.getFirstItem(context.getRelevantStaticMethods(typeQName, weight), null);
if (contextRelevantStaticMethod != null) {
//
// In most cases it is not really relevant
//
//parametersMap.put(i, contextRelevantStaticMethod.createLookupElement());
matchedParametersInContext++;
continue;
}
if (!nullableNotNullManager.isNullable(parameter, true)) {
unreachableParametersCount++;
}
else {
parametersMap.put(i, new VariableSubLookupElement(relevantStringVar));
}
}
else if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(typeQName)) {
final Collection<PsiVariable> contextVariables = context.getVariables(typeQName);
final PsiVariable contextVariable = ContainerUtil.getFirstItem(contextVariables, null);
if (contextVariable != null) {
if (contextVariables.size() == 1) parametersMap.put(i, new VariableSubLookupElement(contextVariable));
matchedParametersInContext++;
continue;
}
final Collection<ContextRelevantVariableGetter> relevantVariablesGetters = context.getRelevantVariablesGetters(typeQName);
final ContextRelevantVariableGetter contextVariableGetter = ContainerUtil.getFirstItem(relevantVariablesGetters, null);
if (contextVariableGetter != null) {
if (relevantVariablesGetters.size() == 1) parametersMap.put(i, contextVariableGetter.createSubLookupElement());
matchedParametersInContext++;
continue;
}
final Collection<PsiMethod> containingClassMethods = context.getContainingClassMethods(typeQName);
final PsiMethod contextRelevantGetter = ContainerUtil.getFirstItem(containingClassMethods, null);
if (contextRelevantGetter != null) {
if (containingClassMethods.size() == 1) parametersMap.put(i, new GetterLookupSubLookupElement(method.getName()));
matchedParametersInContext++;
continue;
}
final ContextRelevantStaticMethod contextRelevantStaticMethod =
ContainerUtil.getFirstItem(context.getRelevantStaticMethods(typeQName, weight), null);
if (contextRelevantStaticMethod != null) {
//
// In most cases it is not really relevant
//
//parametersMap.put(i, contextRelevantStaticMethod.createLookupElement());
matchedParametersInContext++;
continue;
}
if (!nullableNotNullManager.isNullable(parameter, true)) {
unreachableParametersCount++;
}
}
}
@@ -59,7 +59,7 @@ public final class ParametersMatcher {
else {
unMatched++;
}
if (context.getTargetQName().equals(canonicalText) || additionalExcludedNames.contains(canonicalText)) {
if (context.getTarget().getClassQName().equals(canonicalText) || additionalExcludedNames.contains(canonicalText)) {
hasTarget = true;
}
}
@@ -1,18 +1,3 @@
/*
* Copyright 2000-2013 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.compiler.classFilesIndex.chainsSearch.completion;
import com.intellij.codeInsight.completion.*;
@@ -22,12 +7,14 @@ import com.intellij.compiler.classFilesIndex.api.index.ClassFilesIndexFeaturesHo
import com.intellij.compiler.classFilesIndex.chainsSearch.*;
import com.intellij.compiler.classFilesIndex.chainsSearch.context.ChainCompletionContext;
import com.intellij.compiler.classFilesIndex.chainsSearch.context.ContextUtil;
import com.intellij.compiler.classFilesIndex.chainsSearch.context.TargetType;
import com.intellij.compiler.classFilesIndex.impl.MethodsUsageIndexReader;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.*;
@@ -53,8 +40,8 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
@Override
public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) {
if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD
&& ClassFilesIndexFeaturesHolder.getInstance(parameters.getPosition().getProject())
if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD &&
ClassFilesIndexFeaturesHolder.getInstance(parameters.getPosition().getProject())
.enableFeatureIfNeed(ClassFilesIndexFeature.METHOD_CHAINS_COMPLETION)) {
super.fillCompletionVariants(parameters, result);
}
@@ -72,7 +59,6 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
final ChainCompletionContext completionContext = extractContext(parameters);
if (completionContext == null) return;
final String targetClassQName = completionContext.getTargetQName();
final Set<String> contextTypesKeysSet = completionContext.getContextTypes();
final Set<String> contextRelevantTypes = new HashSet<String>(contextTypesKeysSet.size() + 1);
for (final String type : contextTypesKeysSet) {
@@ -80,9 +66,9 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
contextRelevantTypes.add(type);
}
}
contextRelevantTypes.remove(targetClassQName);
final List<LookupElement> foundElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext);
final TargetType target = completionContext.getTarget();
contextRelevantTypes.remove(target.getClassQName());
final List<LookupElement> elementsFoundByMethodsChainsSearch = searchForLookups(target, contextRelevantTypes, completionContext);
if (!IS_UNIT_TEST_MODE) {
result.runRemainingContributors(parameters, new Consumer<CompletionResult>() {
@Override
@@ -90,9 +76,9 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
final LookupElement lookupElement = completionResult.getLookupElement();
final PsiElement lookupElementPsi = lookupElement.getPsiElement();
if (lookupElementPsi != null) {
for (final LookupElement element : foundElements) {
for (final LookupElement element : elementsFoundByMethodsChainsSearch) {
if (lookupElementPsi.isEquivalentTo(element.getPsiElement())) {
foundElements.remove(element);
elementsFoundByMethodsChainsSearch.remove(element);
break;
}
}
@@ -103,45 +89,50 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
} else {
result.stopHere();
}
result.addAllElements(foundElements);
result.addAllElements(elementsFoundByMethodsChainsSearch);
}
});
}
private static List<LookupElement> searchForLookups(final String targetClassQName,
private static List<LookupElement> searchForLookups(final TargetType target,
final Set<String> contextRelevantTypes,
final ChainCompletionContext completionContext) {
final MethodsUsageIndexReader methodsUsageIndexReader = MethodsUsageIndexReader.getInstance(completionContext.getProject());
final Project project = completionContext.getProject();
final MethodsUsageIndexReader methodsUsageIndexReader = MethodsUsageIndexReader.getInstance(project);
final List<MethodsChain> searchResult =
searchChains(targetClassQName, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE, completionContext, methodsUsageIndexReader);
searchChains(target, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE, completionContext, methodsUsageIndexReader);
if (searchResult.size() < MAX_SEARCH_RESULT_SIZE) {
final PsiClass aClass = JavaPsiFacade.getInstance(completionContext.getProject())
.findClass(targetClassQName, GlobalSearchScope.allScope(completionContext.getProject()));
if (aClass != null) {
DirectClassInheritorsSearch.search(aClass).forEach(new Processor<PsiClass>() {
if (!target.isArray()) {
final List<MethodsChain> inheritorFilteredSearchResult = new SmartList<MethodsChain>();
final Processor<TargetType> consumer = new Processor<TargetType>() {
@Override
public boolean process(final TargetType targetType) {
for (final MethodsChain chain : searchChains(targetType, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE,
completionContext, methodsUsageIndexReader)) {
boolean insert = true;
for (final MethodsChain baseChain : searchResult) {
final MethodsChain.CompareResult r = MethodsChain.compare(baseChain, chain, completionContext.getPsiManager());
if (r != MethodsChain.CompareResult.NOT_EQUAL) {
insert = false;
break;
}
}
if (insert) {
inheritorFilteredSearchResult.add(chain);
}
}
searchResult.addAll(inheritorFilteredSearchResult);
return searchResult.size() < MAX_SEARCH_RESULT_SIZE;
}
};
DirectClassInheritorsSearch.search(((PsiClassType)target.getPsiType()).resolve()).forEach(new Processor<PsiClass>() {
@Override
public boolean process(final PsiClass psiClass) {
final String inheritorQName = psiClass.getQualifiedName();
if (!StringUtil.isEmpty(inheritorQName)) {
final List<MethodsChain> inheritorFilteredSearchResult = new SmartList<MethodsChain>();
//noinspection ConstantConditions
for (final MethodsChain chain : searchChains(inheritorQName, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE,
completionContext, methodsUsageIndexReader)) {
boolean insert = true;
for (final MethodsChain baseChain : searchResult) {
final MethodsChain.CompareResult r = MethodsChain.compare(baseChain, chain, completionContext.getPsiManager());
if (r != MethodsChain.CompareResult.NOT_EQUAL) {
insert = false;
break;
}
}
if (insert) {
inheritorFilteredSearchResult.add(chain);
}
}
searchResult.addAll(inheritorFilteredSearchResult);
if (inheritorQName == null) {
return true;
}
return true;
return consumer.process(new TargetType(inheritorQName, false, new PsiImmediateClassType(psiClass, PsiSubstitutor.EMPTY)));
}
});
}
@@ -221,12 +212,12 @@ public class MethodsChainsCompletionContributor extends CompletionContributor {
return filteredResult;
}
private static List<MethodsChain> searchChains(final String targetQName,
private static List<MethodsChain> searchChains(final TargetType target,
final Set<String> contextVarsQNames,
final int maxResultSize,
final int maxChainSize,
final ChainCompletionContext context,
final MethodsUsageIndexReader methodsUsageIndexReader) {
return ChainsSearcher.search(methodsUsageIndexReader, targetQName, contextVarsQNames, maxResultSize, maxChainSize, context);
return ChainsSearcher.search(maxChainSize, target, contextVarsQNames, maxResultSize, context, methodsUsageIndexReader);
}
}
}
@@ -19,10 +19,7 @@ import com.intellij.compiler.classFilesIndex.chainsSearch.CachedRelevantStaticMe
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiVariable;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.containers.MultiMap;
@@ -44,7 +41,7 @@ public class ChainCompletionContext {
}
};
private final PsiMethod myContextMethod;
private final String myTargetQName;
private final TargetType myTarget;
private final Set<String> myContainingClassQNames;
private final MultiMap<String, PsiVariable> myContextVars;
private final MultiMap<String, PsiMethod> myContainingClassGetters;
@@ -72,7 +69,7 @@ public class ChainCompletionContext {
}
ChainCompletionContext(final PsiMethod contextMethod,
final String targetQName,
final TargetType target,
final Set<String> containingClassQNames,
final MultiMap<String, PsiVariable> contextVars,
final MultiMap<String, PsiMethod> containingClassGetters,
@@ -82,7 +79,7 @@ public class ChainCompletionContext {
final Project project,
final GlobalSearchScope resolveScope) {
myContextMethod = contextMethod;
myTargetQName = targetQName;
myTarget = target;
myContainingClassQNames = containingClassQNames;
myContextVars = contextVars;
myContainingClassGetters = containingClassGetters;
@@ -104,8 +101,8 @@ public class ChainCompletionContext {
return myContextMethodName.getValue();
}
public String getTargetQName() {
return myTargetQName;
public TargetType getTarget() {
return myTarget;
}
@Nullable
@@ -38,20 +38,18 @@ public final class ContextUtil {
if (variableType == null || containingElement == null) {
return null;
}
if (variableType instanceof PsiClassType) {
final PsiClass aClass = ((PsiClassType)variableType).resolve();
if (aClass != null) {
if (aClass.hasTypeParameters()) {
return null;
}
}
else {
return null;
}
}
final String targetQName = variableType.getCanonicalText();
if (targetQName == null || targetQName.endsWith("[]")) {
final TargetType target;
if (variableType instanceof PsiClassType) {
target = TargetType.create((PsiClassType)variableType);
}
else if (variableType instanceof PsiArrayType) {
target = TargetType.create((PsiArrayType)variableType);
}
else {
return null;
}
if (target == null) {
return null;
}
@@ -121,7 +119,7 @@ public final class ContextUtil {
}
}
return create(method, targetQName, contextVars, contextMethods, containingClassQNames, containingElement.getProject(),
return create(method, target, contextVars, contextMethods, containingClassQNames, containingElement.getProject(),
containingElement.getResolveScope(), excludedQNames);
}
@@ -177,7 +175,7 @@ public final class ContextUtil {
@Nullable
private static ChainCompletionContext create(final PsiMethod contextMethod,
final String targetQName,
final TargetType target,
final List<PsiVariable> contextVars,
final List<PsiMethod> contextMethods,
final Set<String> containingClassQNames,
@@ -190,12 +188,10 @@ public final class ContextUtil {
final Map<String, PsiVariable> stringVars = new HashMap<String, PsiVariable>();
for (final PsiMethod method : contextMethods) {
PsiType returnType = method.getReturnType();
final PsiType returnType = method.getReturnType();
if (returnType != null) {
final String returnTypeQName = returnType.getCanonicalText();
if (returnTypeQName != null) {
containingClassGetters.putValue(returnTypeQName, method);
}
containingClassGetters.putValue(returnTypeQName, method);
}
}
@@ -214,13 +210,14 @@ public final class ContextUtil {
final PsiClass aClass = ((PsiClassType)type).resolve();
if (aClass != null) {
final String classQName = type.getCanonicalText();
if (!targetQName.equals(classQName)) {
if (!target.getClassQName().equals(classQName)) {
classQNames.add(classQName);
classQNames.addAll(resolveSupersNamesRecursively(aClass));
for (final PsiMethod method : aClass.getAllMethods()) {
if (method.getParameterList().getParametersCount() == 0 && method.getName().startsWith("get")) {
final String getterReturnTypeQName = method.getReturnType().getCanonicalText();
if (getterReturnTypeQName != null) {
final PsiType returnType = method.getReturnType();
if (returnType != null) {
final String getterReturnTypeQName = returnType.getCanonicalText();
contextVarsGetters.putValue(getterReturnTypeQName, new ContextRelevantVariableGetter(var, method));
}
}
@@ -230,15 +227,13 @@ public final class ContextUtil {
}
else {
final String classQName = type.getCanonicalText();
if (classQName != null) {
classQNames.add(classQName);
}
classQNames.add(classQName);
}
for (final String qName : classQNames) {
classQNameToVariable.putValue(qName, var);
}
}
return new ChainCompletionContext(contextMethod, targetQName, containingClassQNames, classQNameToVariable, containingClassGetters,
return new ChainCompletionContext(contextMethod, target, containingClassQNames, classQNameToVariable, containingClassGetters,
contextVarsGetters, stringVars, excludedQNames, project, resolveScope);
}
@@ -57,8 +57,22 @@ final class MethodIncompleteSignatureResolver {
for (final PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC) == signature.isStatic()) {
final PsiType returnType = method.getReturnType();
if (returnType != null && returnType.equalsToText(signature.getReturnType())) {
filtered.add(method);
if (returnType != null) {
if (returnType instanceof PsiClassType) {
final PsiClass resolved = ((PsiClassType)returnType).resolve();
if (resolved == null) {
continue;
}
final String qualifiedName = resolved.getQualifiedName();
if (qualifiedName == null) {
continue;
}
if (qualifiedName.equals(signature.getReturnType())) {
filtered.add(method);
}
} else if (returnType.equalsToText(signature.getReturnType())) {
filtered.add(method);
}
}
}
}
@@ -0,0 +1,84 @@
/*
* 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.compiler.classFilesIndex.chainsSearch.context;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class TargetType {
private final String myClassQName;
private final boolean myArray;
private final PsiType myPsiType;
public TargetType(final String classQName,
final boolean isArray,
final PsiType targetType) {
myClassQName = classQName;
myArray = isArray;
myPsiType = targetType;
}
public String getClassQName() {
return myClassQName;
}
public boolean isArray() {
return myArray;
}
public PsiType getPsiType() {
return myPsiType;
}
@Nullable
public static TargetType create(final PsiArrayType arrayType) {
PsiType currentComponentType = arrayType.getComponentType();
while (currentComponentType instanceof PsiArrayType) {
currentComponentType = ((PsiArrayType)currentComponentType).getComponentType();
}
if (!(currentComponentType instanceof PsiClassType)) {
return null;
}
final String targetQName = arrayType.getCanonicalText();
return new TargetType(targetQName, true, arrayType);
}
@Nullable
public static TargetType create(final PsiClassType classType) {
final PsiClassType.ClassResolveResult resolvedGenerics = classType.resolveGenerics();
final PsiClass resolvedClass = resolvedGenerics.getElement();
if (resolvedClass == null) {
return null;
}
final String classQName = resolvedClass.getQualifiedName();
if (classQName == null) {
return null;
}
if (resolvedClass.hasTypeParameters()) {
return null;
}
return new TargetType(classQName, false, classType);
}
}
@@ -78,5 +78,4 @@ public class MethodsUsageIndexReader extends ClassFilesIndexReaderBase<String, T
});
return values;
}
}
@@ -0,0 +1,32 @@
/*
* 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.
*/
import java.jang.String;
class PsiMethod {
}
interface PsiClass {
PsiMethod[] getMethods();
}
public class TestCompletion {
PsiClass c;
public void method() {
PsiMethod[] m = <caret>
}
}
@@ -0,0 +1,35 @@
/*
* 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.
*/
public class TestIndex {
public void statMethod(PsiClass c) {
c.getMethods();
c.getMethods();
c.getMethods();
c.getMethods();
c.getMethods();
c.getMethods();
c.getMethods();
c.getMethods();
}
}
class PsiMethod {
}
interface PsiClass {
PsiMethod[] getMethods();
}
@@ -19,15 +19,16 @@ import com.intellij.openapi.compiler.CompilerMessage;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.testFramework.CompilerTester;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TObjectIntHashMap;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* @author Dmitry Batkovich
@@ -48,21 +49,16 @@ public abstract class AbstractCompilerAwareTest extends JavaCodeInsightFixtureTe
}
protected final void compileAndIndexData(final String... fileNames) {
final VirtualFile[] filesToCompile =
ContainerUtil.map2Array(ContainerUtil.list(fileNames), new VirtualFile[fileNames.length], new Function<String, VirtualFile>() {
@Override
public VirtualFile fun(final String fileName) {
try {
return myFixture.addFileToProject(fileName, FileUtil.loadFile(new File(getTestDataPath() + getName() + "/" + fileName)))
.getVirtualFile();
}
catch (final IOException e) {
throw new RuntimeException(e);
}
}
});
try {
for (String fileName : fileNames) {
myFixture.addFileToProject(fileName, FileUtil.loadFile(new File(getTestDataPath() + getName() + "/" + fileName))).getVirtualFile();
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
for (final CompilerMessage compilerMessage : myCompilerTester.rebuild()) {
assertNotSame(CompilerMessageCategory.ERROR, compilerMessage.getCategory());
assertNotSame(compilerMessage.getMessage(), CompilerMessageCategory.ERROR, compilerMessage.getCategory());
}
}
}
@@ -251,8 +251,7 @@ public class MethodChainsCompletionTest extends AbstractCompilerAwareTest {
private LookupElement[] runCompletion() {
myFixture.configureByFiles(getTestCompletionFilePath());
final LookupElement[] lookupElements =
myFixture.complete(MethodsChainsCompletionContributor.COMPLETION_TYPE,
MethodsChainsCompletionContributor.INVOCATIONS_THRESHOLD);
myFixture.complete(CompletionType.BASIC, MethodsChainsCompletionContributor.INVOCATIONS_THRESHOLD);
return lookupElements == null ? LookupElement.EMPTY_ARRAY : lookupElements;
}
@@ -46,10 +46,12 @@ public class AsmUtil {
private static final Set<String> ASM_PRIMITIVE_TYPES = ContainerUtil
.newHashSet("C", "D", "F", "I", "J", "S", "Z", "B", "V", "Ljava/lang/Object;", "Ljava/lang/String;", "Ljava/lang/Class;");
public static boolean isPrimitiveOrArray(final String asmType) {
if (asmType.startsWith("[")) {
return true;
public static boolean isPrimitiveOrArrayOfPrimitives(final String asmType) {
for (int i = 0; i < asmType.length(); i++) {
if (asmType.charAt(i) != '[') {
return ASM_PRIMITIVE_TYPES.contains(asmType.substring(i));
}
}
return ASM_PRIMITIVE_TYPES.contains(asmType);
throw new AssertionError("Illegal string: " + asmType);
}
}
@@ -15,6 +15,8 @@
*/
package org.jetbrains.jps.classFilesIndex.indexer.api;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.Processor;
import com.intellij.util.containers.SLRUCache;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
@@ -27,6 +29,7 @@ import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.locks.Lock;
@@ -15,6 +15,8 @@
*/
package org.jetbrains.jps.classFilesIndex.indexer.api;
import com.intellij.openapi.diagnostic.Log;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.asm4.ClassReader;
@@ -30,6 +32,8 @@ import java.util.Set;
* @author Dmitry Batkovich
*/
public class ClassFilesIndexWriter<K, V> {
private static final Logger LOG = Logger.getInstance(ClassFilesIndexWriter.class);
private final ClassFileIndexer<K, V> myIndexer;
private final boolean myEmpty;
protected final ClassFilesIndexStorage<K, V> myIndex;
@@ -43,6 +47,7 @@ public class ClassFilesIndexWriter<K, V> {
}
ClassFilesIndexStorage<K, V> index = null;
IOException exception = null;
LOG.debug("start open... " + indexer.getIndexCanonicalName());
for (int attempt = 0; attempt < 2; attempt++) {
try {
index = new ClassFilesIndexStorage<K, V>(storageDir, myIndexer.getKeyDescriptor(), myIndexer.getDataExternalizer());
@@ -53,6 +58,7 @@ public class ClassFilesIndexWriter<K, V> {
PersistentHashMap.deleteFilesStartingWith(ClassFilesIndexStorage.getIndexFile(storageDir));
}
}
LOG.debug("opened " + indexer.getIndexCanonicalName());
if (index == null) {
throw new RuntimeException(exception);
}
@@ -36,6 +36,8 @@ import org.jetbrains.jps.service.JpsServiceManager;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Dmitry Batkovich
@@ -46,6 +48,8 @@ public class ClassFilesIndicesBuilder extends BaseInstrumentingBuilder {
private static final String PROGRESS_MESSAGE = "Indexing class-files...";
public static final String PROPERTY_NAME = "intellij.compiler.output.index";
private final AtomicLong myMs = new AtomicLong(0);
private final AtomicInteger myFilesCount = new AtomicInteger(0);
private final Collection<ClassFilesIndexWriter> myIndexWriters = new ArrayList<ClassFilesIndexWriter>();
@Override
@@ -89,11 +93,13 @@ public class ClassFilesIndicesBuilder extends BaseInstrumentingBuilder {
if (!isEnabled()) {
return;
}
final long ms = System.currentTimeMillis();
for (final ClassFilesIndexWriter index : myIndexWriters) {
index.close(context);
}
myIndexWriters.clear();
LOG.info("class files indexing finished");
myMs.addAndGet(System.currentTimeMillis() - ms);
LOG.info("class files indexing finished for " + myFilesCount.get() + " files in " + myMs.get() + "ms");
}
@Nullable
@@ -103,9 +109,12 @@ public class ClassFilesIndicesBuilder extends BaseInstrumentingBuilder {
final ClassReader reader,
final ClassWriter writer,
final InstrumentationClassFinder finder) {
final long ms = System.currentTimeMillis();
for (final ClassFilesIndexWriter index : myIndexWriters) {
index.update(compiled.getOutputFile().getPath(), reader);
}
myMs.addAndGet(System.currentTimeMillis() - ms);
myFilesCount.incrementAndGet();
return null;
}
@@ -17,6 +17,7 @@ package org.jetbrains.jps.classFilesIndex.indexer.impl;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.classFilesIndex.AsmUtil;
@@ -79,6 +80,16 @@ public class MethodIncompleteSignature {
return myStatic;
}
@Override
public String toString() {
return "MethodIncompleteSignature{" +
"myOwner='" + myOwner + '\'' +
", myReturnType='" + myReturnType + '\'' +
", myName='" + myName + '\'' +
", myStatic=" + myStatic +
'}';
}
public final static Comparator<MethodIncompleteSignature> COMPARATOR = new Comparator<MethodIncompleteSignature>() {
@Override
public int compare(final MethodIncompleteSignature o1, final MethodIncompleteSignature o2) {
@@ -15,18 +15,15 @@
*/
package org.jetbrains.jps.classFilesIndex.indexer.impl;
import com.intellij.util.containers.FactoryMap;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.asm4.*;
import org.jetbrains.jps.classFilesIndex.AsmUtil;
import org.jetbrains.jps.classFilesIndex.TObjectIntHashMapExternalizer;
import org.jetbrains.jps.classFilesIndex.indexer.api.ClassFileIndexer;
import org.jetbrains.jps.classFilesIndex.indexer.api.ClassFilesIndicesBuilder;
import java.util.HashMap;
import java.util.Map;
@@ -44,12 +41,14 @@ public class MethodsUsageIndexer extends ClassFileIndexer<String, TObjectIntHash
@NotNull
@Override
public Map<String, TObjectIntHashMap<MethodIncompleteSignature>> map(final ClassReader inputData) {
final Map<String, TObjectIntHashMap<MethodIncompleteSignature>> map = new HashMap<String, TObjectIntHashMap<MethodIncompleteSignature>>();
final Map<String, TObjectIntHashMap<MethodIncompleteSignature>> map =
new HashMap<String, TObjectIntHashMap<MethodIncompleteSignature>>();
final MethodVisitor methodVisitor = new MethodVisitor(Opcodes.ASM4) {
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
final Type returnType = Type.getReturnType(desc);
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(name) || AsmUtil.isPrimitiveOrArray(returnType.getDescriptor())) {
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(name) ||
AsmUtil.isPrimitiveOrArrayOfPrimitives(returnType.getDescriptor())) {
return;
}
final boolean isStatic = opcode == Opcodes.INVOKESTATIC;
@@ -68,7 +67,7 @@ public class MethodsUsageIndexer extends ClassFileIndexer<String, TObjectIntHash
final String[] exceptions) {
return methodVisitor;
}
}, ClassReader.EXPAND_FRAMES);
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return map;
}