mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
method chains completion
This commit is contained in:
@@ -34,6 +34,8 @@
|
||||
<orderEntry type="module" module-name="jps-model-impl" />
|
||||
<orderEntry type="module" module-name="java-analysis-impl" exported="" />
|
||||
<orderEntry type="module" module-name="external-system-api" />
|
||||
<orderEntry type="library" name="asm4" level="project" />
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
</component>
|
||||
<component name="copyright">
|
||||
<Base>
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package com.intellij.codeInsight.completion.methodChains;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtilRt;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class ChainCompletionStringUtil {
|
||||
private ChainCompletionStringUtil() {}
|
||||
|
||||
public static boolean isPrimitiveOrArray(final @Nullable String typeQName) {
|
||||
return typeQName != null && (typeQName.endsWith("[]") || PRIMITIVES_NAMES.contains(typeQName));
|
||||
}
|
||||
|
||||
/**
|
||||
* CAUTION: isPrimitiveOrArrayOfPrimitives("java.lang.String") == true,
|
||||
* isPrimitiveOrArrayOfPrimitives("java.lang.Object") == true
|
||||
* isPrimitiveOrArrayOfPrimitives("java.lang.Class") == true
|
||||
*/
|
||||
public static boolean isPrimitiveOrArrayOfPrimitives(final String typeQName) {
|
||||
if (typeQName == null) {
|
||||
return false;
|
||||
}
|
||||
return PRIMITIVES_NAMES.contains(deleteArraySigns(typeQName));
|
||||
}
|
||||
|
||||
public static boolean isShortNamePrimitiveOrArrayOfPrimitives(final @Nullable String shortName) {
|
||||
if (shortName == null) {
|
||||
return false;
|
||||
}
|
||||
return PRIMITIVES_SHORT_NAMES.contains(deleteArraySigns(shortName));
|
||||
}
|
||||
|
||||
private static String deleteArraySigns(final @NotNull String typeName) {
|
||||
String nameWithoutArraySign = typeName;
|
||||
while (nameWithoutArraySign.endsWith("[]")) {
|
||||
nameWithoutArraySign = nameWithoutArraySign.substring(0, nameWithoutArraySign.length() - 2);
|
||||
}
|
||||
return nameWithoutArraySign;
|
||||
}
|
||||
|
||||
private static final Set<String> PRIMITIVES_NAMES = new HashSet<String>();
|
||||
|
||||
static {
|
||||
fillPrimitivesNames(PsiType.BOOLEAN);
|
||||
fillPrimitivesNames(PsiType.INT);
|
||||
fillPrimitivesNames(PsiType.LONG);
|
||||
fillPrimitivesNames(PsiType.DOUBLE);
|
||||
fillPrimitivesNames(PsiType.FLOAT);
|
||||
fillPrimitivesNames(PsiType.SHORT);
|
||||
fillPrimitivesNames(PsiType.CHAR);
|
||||
fillPrimitivesNames(PsiType.BYTE);
|
||||
fillPrimitivesNames(PsiType.VOID);
|
||||
PRIMITIVES_NAMES.add(CommonClassNames.JAVA_LANG_STRING);
|
||||
PRIMITIVES_NAMES.add(CommonClassNames.JAVA_LANG_OBJECT);
|
||||
PRIMITIVES_NAMES.add(CommonClassNames.JAVA_LANG_CLASS);
|
||||
}
|
||||
|
||||
private static void fillPrimitivesNames(final PsiPrimitiveType type) {
|
||||
PRIMITIVES_NAMES.add(type.getBoxedTypeName());
|
||||
PRIMITIVES_NAMES.add(type.getCanonicalText());
|
||||
}
|
||||
|
||||
private static final Set<String> PRIMITIVES_SHORT_NAMES = new HashSet<String>();
|
||||
|
||||
static {
|
||||
fillPrimitivesShortNames(PsiType.BOOLEAN);
|
||||
fillPrimitivesShortNames(PsiType.INT);
|
||||
fillPrimitivesShortNames(PsiType.LONG);
|
||||
fillPrimitivesShortNames(PsiType.DOUBLE);
|
||||
fillPrimitivesShortNames(PsiType.FLOAT);
|
||||
fillPrimitivesShortNames(PsiType.SHORT);
|
||||
fillPrimitivesShortNames(PsiType.CHAR);
|
||||
fillPrimitivesShortNames(PsiType.BYTE);
|
||||
fillPrimitivesShortNames(PsiType.VOID);
|
||||
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(CommonClassNames.JAVA_LANG_STRING));
|
||||
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(CommonClassNames.JAVA_LANG_OBJECT));
|
||||
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(CommonClassNames.JAVA_LANG_CLASS));
|
||||
}
|
||||
|
||||
private static void fillPrimitivesShortNames(final PsiPrimitiveType type) {
|
||||
PRIMITIVES_SHORT_NAMES.add(StringUtilRt.getShortName(type.getBoxedTypeName()));
|
||||
PRIMITIVES_SHORT_NAMES.add(type.getCanonicalText());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.intellij.codeInsight.completion.methodChains;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class Constants {
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
|
||||
/**
|
||||
* magic numbers
|
||||
*/
|
||||
public static final int SINGLETON_MAGIC_RATIO = 100;
|
||||
|
||||
public static final int SINGLETON_MAGIC_RATIO2 = 5;
|
||||
|
||||
public static final int CHAIN_SEARCH_MAGIC_RATIO = 12;
|
||||
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionInitializationContext;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl;
|
||||
|
||||
import static com.intellij.patterns.PsiJavaPatterns.psiElement;
|
||||
import static com.intellij.patterns.StandardPatterns.or;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public final class CompletionContributorPatternUtil {
|
||||
|
||||
private CompletionContributorPatternUtil() {}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static ElementPattern<PsiElement> patternForVariableAssignment() {
|
||||
final ElementPattern<PsiElement> patternForParent = or(psiElement().withText(CompletionInitializationContext.DUMMY_IDENTIFIER_TRIMMED)
|
||||
.afterSiblingSkipping(psiElement(PsiWhiteSpace.class),
|
||||
psiElement(PsiJavaToken.class).withText("=")));
|
||||
|
||||
return psiElement().withParent(patternForParent).withSuperParent(2, or(psiElement(PsiAssignmentExpression.class),
|
||||
psiElement(PsiLocalVariable.class)
|
||||
.inside(PsiDeclarationStatement.class)))
|
||||
.inside(PsiMethod.class);
|
||||
}
|
||||
|
||||
public static ElementPattern<PsiElement> patternForMethodParameter() {
|
||||
return psiElement().withSuperParent(3, PsiMethodCallExpressionImpl.class);
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextUtil;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.ChainsSearcher;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.MethodChainsSearchService;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.MethodsChain;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.MethodsChainLookupRangingHelper;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.patterns.ElementPattern;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.patterns.PsiJavaPatterns.or;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodsChainsCompletionContributor extends CompletionContributor {
|
||||
public static final int INVOCATIONS_THRESHOLD = 3;
|
||||
|
||||
private final static int MAX_SEARCH_RESULT_SIZE = 20;
|
||||
private final static int MAX_CHAIN_SIZE = 4;
|
||||
private final static int FILTER_RATIO = 10;
|
||||
|
||||
@Override
|
||||
public void fillCompletionVariants(final CompletionParameters parameters, final CompletionResultSet result) {
|
||||
if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD && CompilerOutputIndexer.getInstance(parameters.getPosition().getProject()).isEnabled()) {
|
||||
super.fillCompletionVariants(parameters, result);
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
result.stopHere();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public MethodsChainsCompletionContributor() {
|
||||
final ElementPattern<PsiElement> pattern =
|
||||
or(CompletionContributorPatternUtil.patternForMethodParameter(), CompletionContributorPatternUtil.patternForVariableAssignment());
|
||||
extend(CompletionType.BASIC, pattern, new CompletionProvider<CompletionParameters>() {
|
||||
@Override
|
||||
protected void addCompletions(final @NotNull CompletionParameters parameters,
|
||||
final ProcessingContext context,
|
||||
final @NotNull CompletionResultSet result) {
|
||||
|
||||
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) {
|
||||
if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(type)) {
|
||||
contextRelevantTypes.add(type);
|
||||
}
|
||||
}
|
||||
contextRelevantTypes.remove(targetClassQName);
|
||||
|
||||
final List<LookupElement> foundedElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext);
|
||||
result.addAllElements(foundedElements);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<LookupElement> searchForLookups(final String targetClassQName,
|
||||
final Set<String> contextRelevantTypes,
|
||||
final ChainCompletionContext completionContext) {
|
||||
final MethodChainsSearchService searchService = new MethodChainsSearchService(completionContext.getProject());
|
||||
final List<MethodsChain> searchResult =
|
||||
searchChains(targetClassQName, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE, completionContext, searchService);
|
||||
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>() {
|
||||
@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, searchService)) {
|
||||
boolean insert = true;
|
||||
for (final MethodsChain baseChain : searchResult) {
|
||||
if (baseChain.weakContains(chain)) {
|
||||
insert = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insert) {
|
||||
inheritorFilteredSearchResult.add(chain);
|
||||
}
|
||||
}
|
||||
searchResult.addAll(inheritorFilteredSearchResult);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return MethodsChainLookupRangingHelper.chainsToWeightableLookupElements(filterTailAndGetSumLastMethodOccurrence(searchResult),
|
||||
completionContext);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private static ChainCompletionContext extractContext(final CompletionParameters parameters) {
|
||||
final PsiElement parent = PsiTreeUtil
|
||||
.getParentOfType(parameters.getPosition(), PsiAssignmentExpression.class, PsiLocalVariable.class, PsiMethodCallExpression.class);
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent instanceof PsiAssignmentExpression) {
|
||||
return tryExtractContextFromAssignment((PsiAssignmentExpression)parent);
|
||||
}
|
||||
if (parent instanceof PsiLocalVariable) {
|
||||
final PsiLocalVariable localVariable = (PsiLocalVariable)parent;
|
||||
return ContextUtil.createContext(localVariable.getType(), localVariable.getName(),
|
||||
PsiTreeUtil.getParentOfType(parent, PsiDeclarationStatement.class));
|
||||
}
|
||||
final PsiMethod method = ((PsiMethodCallExpression)parent).resolveMethod();
|
||||
if (method == null) return null;
|
||||
final PsiExpression expression = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiExpression.class);
|
||||
final PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(parameters.getPosition(), PsiExpressionList.class);
|
||||
if (expressionList == null) return null;
|
||||
final int exprPosition = Arrays.asList(expressionList.getExpressions()).indexOf(expression);
|
||||
final PsiParameter[] methodParameters = method.getParameterList().getParameters();
|
||||
if (exprPosition < methodParameters.length) {
|
||||
final PsiParameter methodParameter = methodParameters[exprPosition];
|
||||
return ContextUtil
|
||||
.createContext(methodParameter.getType(), null, PsiTreeUtil.getParentOfType(expression, PsiDeclarationStatement.class));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ChainCompletionContext tryExtractContextFromAssignment(final PsiAssignmentExpression assignmentExpression) {
|
||||
final PsiType type = assignmentExpression.getLExpression().getType();
|
||||
final PsiIdentifier identifier = PsiTreeUtil.getChildOfType(assignmentExpression.getLExpression(), PsiIdentifier.class);
|
||||
if (identifier == null) return null;
|
||||
final String identifierText = identifier.getText();
|
||||
return ContextUtil.createContext(type, identifierText, assignmentExpression);
|
||||
}
|
||||
|
||||
private static List<MethodsChain> filterTailAndGetSumLastMethodOccurrence(final List<MethodsChain> chains) {
|
||||
int maxWeight = 0;
|
||||
for (final MethodsChain chain : chains) {
|
||||
final int chainWeight = chain.getChainWeight();
|
||||
if (chainWeight > maxWeight) {
|
||||
maxWeight = chainWeight;
|
||||
}
|
||||
}
|
||||
|
||||
final List<MethodsChain> filteredResult = new ArrayList<MethodsChain>();
|
||||
for (final MethodsChain chain : chains) {
|
||||
final int chainWeight = chain.getChainWeight();
|
||||
if (chainWeight * FILTER_RATIO >= maxWeight) {
|
||||
filteredResult.add(chain);
|
||||
}
|
||||
}
|
||||
return filteredResult;
|
||||
}
|
||||
|
||||
private static List<MethodsChain> searchChains(final String targetQName,
|
||||
final Set<String> contextVarsQNames,
|
||||
final int maxResultSize,
|
||||
final int maxChainSize,
|
||||
final ChainCompletionContext context,
|
||||
final MethodChainsSearchService searchService) {
|
||||
return ChainsSearcher.search(searchService, targetQName, contextVarsQNames, maxResultSize, maxChainSize,
|
||||
createNotDeprecatedMethodsResolver(JavaPsiFacade.getInstance(context.getProject()),
|
||||
context.getResolveScope()),
|
||||
context.getExcludedQNames(), context.getContextMethodName());
|
||||
}
|
||||
|
||||
private static FactoryMap<MethodIncompleteSignature, PsiMethod[]> createNotDeprecatedMethodsResolver(final JavaPsiFacade javaPsiFacade,
|
||||
final GlobalSearchScope scope) {
|
||||
return new FactoryMap<MethodIncompleteSignature, PsiMethod[]>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiMethod[] create(final MethodIncompleteSignature signature) {
|
||||
return signature.resolveNotDeprecated(javaPsiFacade, scope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.codeInsight.completion.methodChains.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionLocation;
|
||||
import com.intellij.codeInsight.completion.CompletionWeigher;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.WeightableChainLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.ChainRelevance;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodsChainsWeigher extends CompletionWeigher {
|
||||
@Override
|
||||
public Comparable weigh(@NotNull final LookupElement element, @NotNull final CompletionLocation location) {
|
||||
if (element instanceof WeightableChainLookupElement) {
|
||||
return ((WeightableChainLookupElement)element).getChainRelevance();
|
||||
}
|
||||
return ChainRelevance.LOWEST;
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.context;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.search.CachedRelevantStaticMethodSearcher;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NotNullLazyValue;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ChainCompletionContext {
|
||||
private final NotNullLazyValue<String> myContextMethodName = new NotNullLazyValue<String>() {
|
||||
@NotNull
|
||||
@Override
|
||||
protected String compute() {
|
||||
return myContextMethod.getName();
|
||||
}
|
||||
};
|
||||
private final PsiMethod myContextMethod;
|
||||
private final String myTargetQName;
|
||||
private final Set<String> myContainingClassQNames;
|
||||
private final MultiMap<String, PsiVariable> myContextVars;
|
||||
private final MultiMap<String, PsiMethod> myContainingClassGetters;
|
||||
private final MultiMap<String, ContextRelevantVariableGetter> myContextVarsGetters;
|
||||
private final Map<String, PsiVariable> myStringVars;
|
||||
private final CachedRelevantStaticMethodSearcher myStaticMethodSearcher;
|
||||
private final Set<String> myExcludedQNames;
|
||||
private final GlobalSearchScope myResolveScope;
|
||||
private final Project myProject;
|
||||
|
||||
private final NotNullLazyValue<Set<String>> contextTypesQNames = new NotNullLazyValue<Set<String>>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
@Override
|
||||
protected Set<String> compute() {
|
||||
return unionToHashSet(myContainingClassQNames, myContextVars.keySet(), myContainingClassGetters.keySet(),
|
||||
myContextVarsGetters.keySet());
|
||||
}
|
||||
};
|
||||
|
||||
public Set<String> getExcludedQNames() {
|
||||
return myExcludedQNames;
|
||||
}
|
||||
|
||||
ChainCompletionContext(final PsiMethod contextMethod,
|
||||
final String targetQName,
|
||||
final Set<String> containingClassQNames,
|
||||
final MultiMap<String, PsiVariable> contextVars,
|
||||
final MultiMap<String, PsiMethod> containingClassGetters,
|
||||
final MultiMap<String, ContextRelevantVariableGetter> contextVarsGetters,
|
||||
final Map<String, PsiVariable> stringVars,
|
||||
final Set<String> excludedQNames,
|
||||
final Project project,
|
||||
final GlobalSearchScope resolveScope) {
|
||||
myContextMethod = contextMethod;
|
||||
myTargetQName = targetQName;
|
||||
myContainingClassQNames = containingClassQNames;
|
||||
myContextVars = contextVars;
|
||||
myContainingClassGetters = containingClassGetters;
|
||||
myContextVarsGetters = contextVarsGetters;
|
||||
myStringVars = stringVars;
|
||||
myExcludedQNames = excludedQNames;
|
||||
myResolveScope = resolveScope;
|
||||
myProject = project;
|
||||
myStaticMethodSearcher = new CachedRelevantStaticMethodSearcher(project, resolveScope);
|
||||
}
|
||||
|
||||
public PsiMethod getContextMethod() {
|
||||
return myContextMethod;
|
||||
}
|
||||
|
||||
public String getContextMethodName() {
|
||||
return myContextMethodName.getValue();
|
||||
}
|
||||
|
||||
public String getTargetQName() {
|
||||
return myTargetQName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiVariable findRelevantStringInContext(@Nullable final String stringParamName) {
|
||||
if (stringParamName == null) {
|
||||
return null;
|
||||
}
|
||||
for (final Map.Entry<String, PsiVariable> e : myStringVars.entrySet()) {
|
||||
if (ChainCompletionContextStringUtil.isSimilar(e.getKey(), stringParamName)) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getContainingClassQNames() {
|
||||
return myContainingClassQNames;
|
||||
}
|
||||
|
||||
public Collection<PsiVariable> getVariables(final String typeQName) {
|
||||
return myContextVars.get(typeQName);
|
||||
}
|
||||
|
||||
public Collection<PsiMethod> getContainingClassMethods(final String typeQName) {
|
||||
return myContainingClassGetters.get(typeQName);
|
||||
}
|
||||
|
||||
public Collection<ContextRelevantStaticMethod> getRelevantStaticMethods(final String typeQName, final int weight) {
|
||||
return myStaticMethodSearcher.getRelevantStaticMethods(typeQName, weight, this);
|
||||
}
|
||||
|
||||
public Collection<ContextRelevantVariableGetter> getRelevantVariablesGetters(final String typeQName) {
|
||||
return myContextVarsGetters.get(typeQName);
|
||||
}
|
||||
|
||||
public Collection<?> getContextRefElements(final String typeQName) {
|
||||
final Collection<PsiVariable> variables = getVariables(typeQName);
|
||||
final Collection<PsiMethod> containingClassMethods = getContainingClassMethods(typeQName);
|
||||
final Collection<UserDataHolder> refElements = new ArrayList<UserDataHolder>(variables.size() + containingClassMethods.size());
|
||||
refElements.addAll(variables);
|
||||
refElements.addAll(containingClassMethods);
|
||||
for (final ContextRelevantVariableGetter contextRelevantVariableGetter : getRelevantVariablesGetters(typeQName)) {
|
||||
refElements.add(contextRelevantVariableGetter.createLookupElement());
|
||||
}
|
||||
return refElements;
|
||||
}
|
||||
|
||||
public boolean contains(@Nullable final String typeQualifierName) {
|
||||
return typeQualifierName != null && contextTypesQNames.getValue().contains(typeQualifierName);
|
||||
}
|
||||
|
||||
public Set<String> getContextTypes() {
|
||||
return contextTypesQNames.getValue();
|
||||
}
|
||||
|
||||
public GlobalSearchScope getResolveScope() {
|
||||
return myResolveScope;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
private static <T> HashSet<T> unionToHashSet(final Collection<T>... collections) {
|
||||
final HashSet<T> res = new HashSet<T>();
|
||||
for (final Collection<T> set : collections) {
|
||||
res.addAll(set);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.context;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
class ChainCompletionContextStringUtil {
|
||||
|
||||
private ChainCompletionContextStringUtil(){}
|
||||
|
||||
private final static int COMMON_PART_MIN_LENGTH = 3;
|
||||
|
||||
public static boolean isSimilar(@NotNull final String varName,
|
||||
@NotNull final String parameterName) {
|
||||
final String sanitizedParamName = sanitizedToLowerCase(parameterName);
|
||||
if (StringUtil.commonPrefix(varName, sanitizedParamName).length() >= COMMON_PART_MIN_LENGTH) {
|
||||
return true;
|
||||
}
|
||||
final String suffix = StringUtil.commonSuffix(varName, sanitizedParamName);
|
||||
return suffix.length() >= COMMON_PART_MIN_LENGTH;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String sanitizedToLowerCase(@NotNull final String name) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < name.length(); i++) {
|
||||
final char ch = name.charAt(i);
|
||||
if (Character.isLetter(ch)) {
|
||||
result.append(Character.toLowerCase(ch));
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.context;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.StaticMethodSubLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.VariableSubLookupElement;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TIntObjectProcedure;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ContextRelevantStaticMethod {
|
||||
private final PsiMethod psiMethod;
|
||||
@Nullable
|
||||
private final TIntObjectHashMap<SubLookupElement> parameters;
|
||||
|
||||
public ContextRelevantStaticMethod(final PsiMethod psiMethod, @Nullable final TIntObjectHashMap<PsiVariable> parameters) {
|
||||
this.psiMethod = psiMethod;
|
||||
if (parameters == null) {
|
||||
this.parameters = null;
|
||||
} else {
|
||||
this.parameters = new TIntObjectHashMap<SubLookupElement>(parameters.size());
|
||||
parameters.forEachEntry(new TIntObjectProcedure<PsiVariable>() {
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Override
|
||||
public boolean execute(final int pos, final PsiVariable var) {
|
||||
ContextRelevantStaticMethod.this.parameters.put(pos, new VariableSubLookupElement(var));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private SubLookupElement cachedLookupElement;
|
||||
|
||||
public SubLookupElement createLookupElement() {
|
||||
if (cachedLookupElement == null) {
|
||||
cachedLookupElement = new StaticMethodSubLookupElement(psiMethod, parameters);
|
||||
}
|
||||
return cachedLookupElement;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.context;
|
||||
|
||||
import com.intellij.codeInsight.completion.JavaChainLookupElement;
|
||||
import com.intellij.codeInsight.completion.JavaMethodCallElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.GetterLookupSubLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.VariableLookupItem;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class ContextRelevantVariableGetter {
|
||||
private final PsiVariable myVariable;
|
||||
private final PsiMethod myMethod;
|
||||
|
||||
public ContextRelevantVariableGetter(final PsiVariable variable, final PsiMethod method) {
|
||||
myVariable = variable;
|
||||
myMethod = method;
|
||||
}
|
||||
|
||||
public SubLookupElement createSubLookupElement() {
|
||||
return new GetterLookupSubLookupElement(myVariable.getName(), myMethod.getName());
|
||||
}
|
||||
|
||||
public LookupElement createLookupElement() {
|
||||
return new JavaChainLookupElement(new VariableLookupItem(myVariable), new JavaMethodCallElement(myMethod));
|
||||
}
|
||||
}
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.context;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ContextUtil {
|
||||
@Nullable
|
||||
public static ChainCompletionContext createContext(final @Nullable PsiType variableType,
|
||||
final @Nullable String variableName,
|
||||
final @Nullable PsiElement containingElement) {
|
||||
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("[]")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiMethod method = PsiTreeUtil.getParentOfType(containingElement, PsiMethod.class);
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiClass aClass = method.getContainingClass();
|
||||
if (aClass == null) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> containingClassQNames = resolveSupersNamesRecursively(aClass);
|
||||
|
||||
final List<PsiVariable> contextVars = new SmartList<PsiVariable>();
|
||||
for (final PsiField field : aClass.getFields()) {
|
||||
final PsiClass containingClass = field.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
if ((field.hasModifierProperty(PsiModifier.PUBLIC) ||
|
||||
field.hasModifierProperty(PsiModifier.PROTECTED) ||
|
||||
((field.hasModifierProperty(PsiModifier.PRIVATE) || field.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) &&
|
||||
aClass.isEquivalentTo(containingClass))) && !field.getName().equals(variableName)) {
|
||||
contextVars.add(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.addAll(contextVars, method.getParameterList().getParameters());
|
||||
|
||||
final PsiCodeBlock methodBody = method.getBody();
|
||||
assert methodBody != null;
|
||||
boolean processMethodTail = false;
|
||||
final List<PsiElement> afterElements = new ArrayList<PsiElement>();
|
||||
for (final PsiElement element : methodBody.getChildren()) {
|
||||
if (element.isEquivalentTo(containingElement)) {
|
||||
if (variableType instanceof PsiClassType) {
|
||||
processMethodTail = true;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiDeclarationStatement) {
|
||||
if (processMethodTail) {
|
||||
afterElements.add(element);
|
||||
}
|
||||
else {
|
||||
for (final PsiElement declaredElement : ((PsiDeclarationStatement)element).getDeclaredElements()) {
|
||||
if (declaredElement instanceof PsiLocalVariable &&
|
||||
(variableName == null || !variableName.equals(((PsiLocalVariable)declaredElement).getName()))) {
|
||||
contextVars.add((PsiVariable)declaredElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Set<String> excludedQNames = processMethodTail
|
||||
? generateExcludedQNames(afterElements, ((PsiClassType)variableType).resolve(), variableName,
|
||||
contextVars)
|
||||
: Collections.<String>emptySet();
|
||||
|
||||
final List<PsiMethod> contextMethods = new ArrayList<PsiMethod>();
|
||||
for (final PsiMethod psiMethod : aClass.getMethods()) {
|
||||
if ((psiMethod.hasModifierProperty(PsiModifier.PROTECTED) || psiMethod.hasModifierProperty(PsiModifier.PRIVATE)) &&
|
||||
psiMethod.getParameterList().getParametersCount() == 0) {
|
||||
contextMethods.add(psiMethod);
|
||||
}
|
||||
}
|
||||
|
||||
return create(method, targetQName, contextVars, contextMethods, containingClassQNames, containingElement.getProject(),
|
||||
containingElement.getResolveScope(), excludedQNames);
|
||||
}
|
||||
|
||||
private static Set<String> generateExcludedQNames(final List<PsiElement> tailElements,
|
||||
final @Nullable PsiClass psiClass,
|
||||
final @Nullable String varName,
|
||||
final List<PsiVariable> contextVars) {
|
||||
if (psiClass == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
final String classQName = psiClass.getQualifiedName();
|
||||
if (classQName == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
final Set<String> excludedQNames = new HashSet<String>();
|
||||
if (!tailElements.isEmpty()) {
|
||||
final Set<String> contextVarTypes = new HashSet<String>();
|
||||
final Map<String, PsiVariable> contextVarNamesToVar = new HashMap<String, PsiVariable>();
|
||||
for (final PsiVariable var : contextVars) {
|
||||
contextVarTypes.add(var.getType().getCanonicalText());
|
||||
contextVarNamesToVar.put(var.getName(), var);
|
||||
}
|
||||
for (final PsiElement element : tailElements) {
|
||||
final Collection<PsiMethodCallExpression> methodCallExpressions =
|
||||
PsiTreeUtil.findChildrenOfType(element, PsiMethodCallExpression.class);
|
||||
for (final PsiMethodCallExpression methodCallExpression : methodCallExpressions) {
|
||||
final PsiExpressionList args = methodCallExpression.getArgumentList();
|
||||
final PsiMethod resolvedMethod = methodCallExpression.resolveMethod();
|
||||
if (resolvedMethod != null) {
|
||||
final PsiType returnType = resolvedMethod.getReturnType();
|
||||
if (returnType != null) {
|
||||
final String returnTypeAsString = returnType.getCanonicalText();
|
||||
for (final PsiExpression expression : args.getExpressions()) {
|
||||
final String qVarName = expression.getText();
|
||||
if (qVarName != null) {
|
||||
if (contextVarNamesToVar.containsKey(qVarName) || qVarName.equals(varName)) {
|
||||
excludedQNames.add(returnTypeAsString);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!contextVarTypes.contains(returnTypeAsString)) {
|
||||
excludedQNames.add(returnTypeAsString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return excludedQNames;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ChainCompletionContext create(final PsiMethod contextMethod,
|
||||
final String targetQName,
|
||||
final List<PsiVariable> contextVars,
|
||||
final List<PsiMethod> contextMethods,
|
||||
final Set<String> containingClassQNames,
|
||||
final Project project,
|
||||
final GlobalSearchScope resolveScope,
|
||||
final Set<String> excludedQNames) {
|
||||
final MultiMap<String, PsiVariable> classQNameToVariable = new MultiMap<String, PsiVariable>();
|
||||
final MultiMap<String, PsiMethod> containingClassGetters = new MultiMap<String, PsiMethod>();
|
||||
final MultiMap<String, ContextRelevantVariableGetter> contextVarsGetters = new MultiMap<String, ContextRelevantVariableGetter>();
|
||||
final Map<String, PsiVariable> stringVars = new HashMap<String, PsiVariable>();
|
||||
|
||||
for (final PsiMethod method : contextMethods) {
|
||||
final String returnTypeQName = method.getReturnType().getCanonicalText();
|
||||
if (returnTypeQName != null) {
|
||||
containingClassGetters.putValue(returnTypeQName, method);
|
||||
}
|
||||
}
|
||||
|
||||
for (final PsiVariable var : contextVars) {
|
||||
final PsiType type = var.getType();
|
||||
final Set<String> classQNames = new HashSet<String>();
|
||||
if (type instanceof PsiClassType) {
|
||||
if (JAVA_LANG_STRING_SHORT_NAME.equals(((PsiClassType)type).getClassName())) {
|
||||
final String varName = var.getName();
|
||||
if (varName != null) {
|
||||
stringVars.put(ChainCompletionContextStringUtil.sanitizedToLowerCase(varName), var);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiClass aClass = ((PsiClassType)type).resolve();
|
||||
if (aClass != null) {
|
||||
final String classQName = type.getCanonicalText();
|
||||
if (!targetQName.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) {
|
||||
contextVarsGetters.putValue(getterReturnTypeQName, new ContextRelevantVariableGetter(var, method));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String classQName = type.getCanonicalText();
|
||||
if (classQName != null) {
|
||||
classQNames.add(classQName);
|
||||
}
|
||||
}
|
||||
for (final String qName : classQNames) {
|
||||
classQNameToVariable.putValue(qName, var);
|
||||
}
|
||||
}
|
||||
return new ChainCompletionContext(contextMethod, targetQName, containingClassQNames, classQNameToVariable, containingClassGetters,
|
||||
contextVarsGetters, stringVars, excludedQNames, project, resolveScope);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<String> resolveSupersNamesRecursively(@Nullable final PsiClass psiClass) {
|
||||
final Set<String> result = new HashSet<String>();
|
||||
if (psiClass != null) {
|
||||
for (final PsiClass superClass : psiClass.getSupers()) {
|
||||
final String qualifiedName = superClass.getQualifiedName();
|
||||
if (!CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName)) {
|
||||
if (qualifiedName != null) {
|
||||
result.add(qualifiedName);
|
||||
}
|
||||
result.addAll(resolveSupersNamesRecursively(superClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private final static String JAVA_LANG_STRING_SHORT_NAME = StringUtil.getShortName(CommonClassNames.JAVA_LANG_STRING);
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class ChainCompletionLookupElementUtil {
|
||||
private ChainCompletionLookupElementUtil() {
|
||||
}
|
||||
|
||||
public static LookupElement createLookupElement(final PsiMethod method,
|
||||
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements) {
|
||||
if (method.isConstructor()) {
|
||||
//noinspection ConstantConditions
|
||||
return LookupElementBuilder.create(String.format("%s %s", PsiKeyword.NEW, method.getContainingClass().getName()));
|
||||
} else if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
return new ChainCompletionMethodCallLookupElement(method, replaceElements, false, true);
|
||||
} else {
|
||||
return new ChainCompletionMethodCallLookupElement(method, replaceElements);
|
||||
}
|
||||
}
|
||||
|
||||
public static String fillMethodParameters(final PsiMethod method, @Nullable final TIntObjectHashMap<SubLookupElement> replaceElements) {
|
||||
final TIntObjectHashMap<SubLookupElement> notNullReplaceElements = replaceElements == null ?
|
||||
new TIntObjectHashMap<SubLookupElement>(0) :
|
||||
replaceElements;
|
||||
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
final PsiParameter parameter = parameters[i];
|
||||
final SubLookupElement replaceElement = notNullReplaceElements.get(i);
|
||||
if (replaceElement != null) {
|
||||
sb.append(replaceElement.getInsertString());
|
||||
} else {
|
||||
sb.append(parameter.getName());
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.completion.JavaMethodCallElement;
|
||||
import com.intellij.codeInsight.completion.StaticallyImportable;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
|
||||
import com.intellij.codeInsight.lookup.AutoCompletionPolicy;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TObjectProcedure;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ChainCompletionMethodCallLookupElement extends JavaMethodCallElement implements StaticallyImportable {
|
||||
public static final String PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION = "methods.chain.completion.autoCompletion";
|
||||
|
||||
private final PsiMethod myMethod;
|
||||
@Nullable
|
||||
private final TIntObjectHashMap<SubLookupElement> myReplaceElements;
|
||||
private final boolean myMergedOverloads;
|
||||
|
||||
public ChainCompletionMethodCallLookupElement(final PsiMethod method,
|
||||
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements,
|
||||
final boolean shouldImportStatic,
|
||||
final boolean mergedOverloads) {
|
||||
super(method, shouldImportStatic, mergedOverloads);
|
||||
myMethod = method;
|
||||
myReplaceElements = replaceElements;
|
||||
myMergedOverloads = mergedOverloads;
|
||||
configureAutoCompletionPolicy();
|
||||
}
|
||||
|
||||
public ChainCompletionMethodCallLookupElement(final PsiMethod method,
|
||||
final @Nullable TIntObjectHashMap<SubLookupElement> replaceElements) {
|
||||
super(method);
|
||||
myMethod = method;
|
||||
myReplaceElements = replaceElements;
|
||||
myMergedOverloads = true;
|
||||
configureAutoCompletionPolicy();
|
||||
}
|
||||
|
||||
private void configureAutoCompletionPolicy() {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
if (PropertiesComponent.getInstance(myMethod.getProject()).getBoolean(PROP_METHODS_CHAIN_COMPLETION_AUTO_COMPLETION, false)) {
|
||||
setAutoCompletionPolicy(AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(final InsertionContext context) {
|
||||
super.handleInsert(context);
|
||||
if (!myMergedOverloads || isUniqueMethod(myMethod)) {
|
||||
context.commitDocument();
|
||||
context.getDocument()
|
||||
.insertString(context.getTailOffset() - 1, ChainCompletionLookupElementUtil.fillMethodParameters(myMethod, myReplaceElements));
|
||||
final PsiFile file = context.getFile();
|
||||
assert file instanceof PsiJavaFile;
|
||||
final PsiJavaFile javaFile = (PsiJavaFile)file;
|
||||
if (myReplaceElements != null) {
|
||||
myReplaceElements.forEachValue(new TObjectProcedure<SubLookupElement>() {
|
||||
@Override
|
||||
public boolean execute(final SubLookupElement subLookupElement) {
|
||||
subLookupElement.doImport(javaFile);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
context.commitDocument();
|
||||
context.getEditor().getCaretModel().moveToOffset(context.getTailOffset());
|
||||
context.commitDocument();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean isUniqueMethod(@NotNull final PsiMethod method) {
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
return containingClass == null || containingClass.findMethodsByName(method.getName(), true).length == 1;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
|
||||
|
||||
import com.intellij.codeInsight.completion.InsertionContext;
|
||||
import com.intellij.codeInsight.lookup.LookupElementPresentation;
|
||||
import com.intellij.codeInsight.lookup.LookupItem;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.SuggestedNameInfo;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class ChainCompletionNewVariableLookupElement extends LookupItem<PsiClass> {
|
||||
|
||||
private final PsiClass psiClass;
|
||||
private final String newVarName;
|
||||
|
||||
public ChainCompletionNewVariableLookupElement(final PsiClass psiClass, final String newVarName) {
|
||||
super(psiClass, newVarName);
|
||||
this.newVarName = newVarName;
|
||||
this.psiClass = psiClass;
|
||||
}
|
||||
|
||||
public static ChainCompletionNewVariableLookupElement create(final PsiClass psiClass) {
|
||||
final Project project = psiClass.getProject();
|
||||
final SuggestedNameInfo suggestedNameInfo = JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, JavaPsiFacade .getElementFactory( project).createType(psiClass));
|
||||
return new ChainCompletionNewVariableLookupElement(psiClass, chooseLongest(suggestedNameInfo.names));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInsert(final InsertionContext context) {
|
||||
final PsiFile file = context.getFile();
|
||||
((PsiJavaFile) file).importClass(psiClass);
|
||||
final PsiStatement statement = PsiTreeUtil.getParentOfType(file.findElementAt(context.getEditor().getCaretModel().getOffset()), PsiStatement.class);
|
||||
final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(statement, PsiCodeBlock.class);
|
||||
assert codeBlock != null;
|
||||
final Project project = context.getProject();
|
||||
new WriteCommandAction.Simple(project, file) {
|
||||
@Override
|
||||
protected void run() throws Throwable {
|
||||
codeBlock.addBefore(
|
||||
JavaPsiFacade.getElementFactory(
|
||||
project).
|
||||
createStatementFromText(String.format("%s %s = null;", psiClass.getName(), newVarName), null), statement);
|
||||
}
|
||||
}.execute();
|
||||
PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(context.getDocument());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getLookupString() {
|
||||
return newVarName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderElement(final LookupElementPresentation presentation) {
|
||||
super.renderElement(presentation);
|
||||
presentation.setItemText(newVarName);
|
||||
}
|
||||
|
||||
private static String chooseLongest(final String[] names) {
|
||||
String longestWord = names[0];
|
||||
int maxLength = longestWord.length();
|
||||
for (int i = 1; i < names.length; i++) {
|
||||
final int length = names[i].length();
|
||||
if (length > maxLength) {
|
||||
maxLength = length;
|
||||
longestWord = names[i];
|
||||
}
|
||||
}
|
||||
return longestWord;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.search.ChainRelevance;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementDecorator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class WeightableChainLookupElement extends LookupElementDecorator<LookupElement> {
|
||||
private final ChainRelevance myChainRelevance;
|
||||
|
||||
public WeightableChainLookupElement(final @NotNull LookupElement delegate, final ChainRelevance relevance) {
|
||||
super(delegate);
|
||||
myChainRelevance = relevance;
|
||||
}
|
||||
|
||||
public ChainRelevance getChainRelevance() {
|
||||
return myChainRelevance;
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
|
||||
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class GetterLookupSubLookupElement implements SubLookupElement {
|
||||
private final String myVariableName;
|
||||
private final String myMethodName;
|
||||
|
||||
public GetterLookupSubLookupElement(final String methodName) {
|
||||
this(null, methodName);
|
||||
}
|
||||
|
||||
public GetterLookupSubLookupElement(@Nullable final String variableName, final String methodName) {
|
||||
myVariableName = variableName;
|
||||
myMethodName = methodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doImport(final PsiJavaFile javaFile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInsertString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (myVariableName != null) {
|
||||
sb.append(myVariableName).append(".");
|
||||
}
|
||||
sb.append(myMethodName).append("()");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.ChainCompletionLookupElementUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import gnu.trove.TObjectProcedure;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class StaticMethodSubLookupElement implements SubLookupElement {
|
||||
|
||||
private final PsiMethod myMethod;
|
||||
private final TIntObjectHashMap<SubLookupElement> myReplaceElements;
|
||||
|
||||
public StaticMethodSubLookupElement(final PsiMethod method, @Nullable final TIntObjectHashMap<SubLookupElement> replaceElements) {
|
||||
assert method.hasModifierProperty(PsiModifier.STATIC);
|
||||
myReplaceElements = replaceElements;
|
||||
myMethod = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doImport(final PsiJavaFile javaFile) {
|
||||
final PsiClass containingClass = myMethod.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
if (javaFile.findImportReferenceTo(containingClass) == null) {
|
||||
javaFile.importClass(containingClass);
|
||||
}
|
||||
}
|
||||
if (myReplaceElements != null) {
|
||||
myReplaceElements.forEachValue(new TObjectProcedure<SubLookupElement>() {
|
||||
@Override
|
||||
public boolean execute(final SubLookupElement subLookupElement) {
|
||||
subLookupElement.doImport(javaFile);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInsertString() {
|
||||
//noinspection ConstantConditions
|
||||
return String.format("%s.%s(%s)", myMethod.getContainingClass().getName(), myMethod.getName(),
|
||||
ChainCompletionLookupElementUtil.fillMethodParameters(myMethod, myReplaceElements));
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
|
||||
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public interface SubLookupElement {
|
||||
|
||||
void doImport(final PsiJavaFile javaFile);
|
||||
|
||||
String getInsertString();
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.completion.lookup.sub;
|
||||
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class VariableSubLookupElement implements SubLookupElement {
|
||||
|
||||
private final String myVarName;
|
||||
|
||||
public VariableSubLookupElement(final PsiVariable variable) {
|
||||
myVarName = variable.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doImport(final PsiJavaFile javaFile) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInsertString() {
|
||||
return myVarName;
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class CachedNotDeprecatedMethodsResolver {
|
||||
private final Map<MethodIncompleteSignature, PsiMethod[]> myResolveLocalCache = new HashMap<MethodIncompleteSignature, PsiMethod[]>();
|
||||
private final JavaPsiFacade myJavaPsiFacade;
|
||||
private final GlobalSearchScope myScope;
|
||||
|
||||
public CachedNotDeprecatedMethodsResolver(final Project project, final GlobalSearchScope scope) {
|
||||
myScope = scope;
|
||||
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod[] resolveNotDeprecated(@NotNull final MethodIncompleteSignature methodInvocation) {
|
||||
final PsiMethod[] cached = myResolveLocalCache.get(methodInvocation);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
final PsiMethod[] methods = methodInvocation.resolveNotDeprecated(myJavaPsiFacade, myScope);
|
||||
myResolveLocalCache.put(methodInvocation, methods);
|
||||
return methods;
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextRelevantStaticMethod;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodsUsageIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class CachedRelevantStaticMethodSearcher {
|
||||
private final HashMap<MethodIncompleteSignature, PsiMethod> myCachedResolveResults = new HashMap<MethodIncompleteSignature, PsiMethod>();
|
||||
private final MethodsUsageIndex myIndex;
|
||||
private final JavaPsiFacade myJavaPsiFacade;
|
||||
private final GlobalSearchScope myAllScope;
|
||||
private final GlobalSearchScope myResolveScope;
|
||||
|
||||
public CachedRelevantStaticMethodSearcher(final Project project, final GlobalSearchScope resolveScope) {
|
||||
myIndex = MethodsUsageIndex.getInstance(project);
|
||||
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
|
||||
myAllScope = GlobalSearchScope.allScope(project);
|
||||
myResolveScope = resolveScope;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ContextRelevantStaticMethod> getRelevantStaticMethods(final String resultQualifiedClassName,
|
||||
final int minOccurrence,
|
||||
final ChainCompletionContext completionContext) {
|
||||
if (resultQualifiedClassName == null ||
|
||||
ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(resultQualifiedClassName) ||
|
||||
completionContext.getTargetQName().equals(resultQualifiedClassName)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final TreeSet<UsageIndexValue> indexValues = myIndex.getValues(resultQualifiedClassName);
|
||||
if (indexValues != null) {
|
||||
int occurrences = 0;
|
||||
final List<ContextRelevantStaticMethod> relevantMethods = new ArrayList<ContextRelevantStaticMethod>();
|
||||
for (final UsageIndexValue indexValue : extractStaticMethods(indexValues)) {
|
||||
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
|
||||
final PsiMethod method;
|
||||
if (myCachedResolveResults.containsKey(methodInvocation)) {
|
||||
method = myCachedResolveResults.get(methodInvocation);
|
||||
}
|
||||
else {
|
||||
final PsiMethod[] methods = methodInvocation.resolveNotDeprecated(myJavaPsiFacade, myAllScope);
|
||||
method = MethodChainsSearchUtil
|
||||
.getMethodWithMinNotPrimitiveParameters(methods, Collections.singleton(completionContext.getTargetQName()));
|
||||
myCachedResolveResults.put(methodInvocation, method);
|
||||
if (method == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
if (method.hasModifierProperty(PsiModifier.PUBLIC)) {
|
||||
if (isMethodValid(method, completionContext, resultQualifiedClassName)) {
|
||||
occurrences += indexValue.getOccurrences();
|
||||
if (myResolveScope.contains(method.getContainingFile().getVirtualFile())) {
|
||||
relevantMethods.add(new ContextRelevantStaticMethod(method, null));
|
||||
}
|
||||
if (occurrences >= minOccurrence) {
|
||||
return relevantMethods;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static List<UsageIndexValue> extractStaticMethods(final TreeSet<UsageIndexValue> indexValues) {
|
||||
final List<UsageIndexValue> relevantStaticMethods = new SmartList<UsageIndexValue>();
|
||||
for (final UsageIndexValue indexValue : indexValues) {
|
||||
if (indexValue.getMethodIncompleteSignature().isStatic()) {
|
||||
relevantStaticMethods.add(indexValue);
|
||||
}
|
||||
}
|
||||
return relevantStaticMethods;
|
||||
}
|
||||
|
||||
private static boolean isMethodValid(final @Nullable PsiMethod method,
|
||||
final ChainCompletionContext completionContext,
|
||||
final String targetTypeShortName) {
|
||||
if (method == null) return false;
|
||||
for (final PsiParameter parameter : method.getParameterList().getParameters()) {
|
||||
final PsiType type = parameter.getType();
|
||||
final String shortClassName = typeAsString(type);
|
||||
if (targetTypeShortName.equals(shortClassName)) return false;
|
||||
if (!ChainCompletionStringUtil.isShortNamePrimitiveOrArrayOfPrimitives(shortClassName) &&
|
||||
!completionContext.contains(type.getCanonicalText())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static String typeAsString(final PsiType type) {
|
||||
if (type instanceof PsiClassType)
|
||||
return ((PsiClassType) type).getClassName();
|
||||
else if (type instanceof PsiPrimitiveType)
|
||||
return type.getCanonicalText();
|
||||
else if (type instanceof PsiArrayType) {
|
||||
final String componentTypeAsString = typeAsString(((PsiArrayType) type).getComponentType());
|
||||
if (componentTypeAsString == null) return null;
|
||||
return String.format("%s[]", componentTypeAsString);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class ChainRelevance implements Comparable<ChainRelevance> {
|
||||
public static final ChainRelevance LOWEST = new ChainRelevance(Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, false, false);
|
||||
|
||||
private final int myChainSize;
|
||||
private final int myLastMethodOccurrences;
|
||||
private final int myUnreachableParametersCount;
|
||||
private final int myNotMatchedStringVars;
|
||||
private final boolean myHasCallingVariableInContext;
|
||||
private final boolean myFirstMethodStatic;
|
||||
|
||||
public ChainRelevance(final int chainSize,
|
||||
final int lastMethodOccurrences,
|
||||
final int unreachableParametersCount,
|
||||
final int notMatchedStringVars,
|
||||
final boolean hasCallingVariableInContext,
|
||||
final boolean firstMethodStatic) {
|
||||
myChainSize = chainSize;
|
||||
myLastMethodOccurrences = lastMethodOccurrences;
|
||||
myUnreachableParametersCount = unreachableParametersCount;
|
||||
myNotMatchedStringVars = notMatchedStringVars;
|
||||
myHasCallingVariableInContext = hasCallingVariableInContext;
|
||||
myFirstMethodStatic = firstMethodStatic;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public boolean hasCallingVariableInContext() {
|
||||
return myHasCallingVariableInContext;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public int getChainSize() {
|
||||
return myChainSize;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public int getLastMethodOccurrences() {
|
||||
return myLastMethodOccurrences;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public int getUnreachableParametersCount() {
|
||||
return myUnreachableParametersCount;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public int getNotMatchedStringVars() {
|
||||
return myNotMatchedStringVars;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public boolean isFirstMethodStatic() {
|
||||
return myFirstMethodStatic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull final ChainRelevance that) {
|
||||
if (myFirstMethodStatic && !that.myFirstMethodStatic) {
|
||||
return -1;
|
||||
}
|
||||
if (that.myFirstMethodStatic && !myFirstMethodStatic) {
|
||||
return 1;
|
||||
}
|
||||
if (myHasCallingVariableInContext && !that.myHasCallingVariableInContext) {
|
||||
return 1;
|
||||
}
|
||||
if (that.myHasCallingVariableInContext && !myHasCallingVariableInContext) {
|
||||
return -1;
|
||||
}
|
||||
int sub = myLastMethodOccurrences - that.myLastMethodOccurrences;
|
||||
if (sub != 0) return sub;
|
||||
sub = myUnreachableParametersCount - that.myUnreachableParametersCount;
|
||||
if (sub != 0) return -sub;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.Constants;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ChainsSearcher {
|
||||
|
||||
public static List<MethodsChain> search(final MethodChainsSearchService searchService,
|
||||
final String targetQName,
|
||||
final Set<String> contextQNames,
|
||||
final int maxResultSize,
|
||||
final int pathMaximalLength,
|
||||
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
|
||||
final String contextMethodName) {
|
||||
return search(searchService, targetQName, contextQNames, maxResultSize, pathMaximalLength, resolver,
|
||||
Collections.<String>singleton(targetQName), contextMethodName);
|
||||
}
|
||||
|
||||
public static List<MethodsChain> search(final MethodChainsSearchService searchService,
|
||||
final String targetQName,
|
||||
final Set<String> contextQNames,
|
||||
final int maxResultSize,
|
||||
final int pathMaximalLength,
|
||||
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
|
||||
final Set<String> excludedParamsTypesQNames,
|
||||
final String contextMethodName) {
|
||||
final SearchInitializer initializer = createInitializer(targetQName, resolver, searchService, excludedParamsTypesQNames);
|
||||
final ArrayList<MethodsChain> methodsChains = new ArrayList<MethodsChain>(maxResultSize);
|
||||
final MethodsChain firstBestMethodsChain =
|
||||
search(searchService, initializer, contextQNames, Collections.<String>emptySet(), pathMaximalLength, resolver, targetQName,
|
||||
excludedParamsTypesQNames, contextMethodName);
|
||||
if (firstBestMethodsChain != null) {
|
||||
methodsChains.add(firstBestMethodsChain);
|
||||
Set<Set<String>> excludedCombinations = MethodsChain.edgeCombinations(Collections.<Set<String>>emptySet(), firstBestMethodsChain);
|
||||
while (methodsChains.size() <= maxResultSize) {
|
||||
final Set<Set<String>> localExcludedCombinations = excludedCombinations;
|
||||
boolean allLocalsIsNull = true;
|
||||
final int beforeStepChainsCount = methodsChains.size();
|
||||
for (final Set<String> excludedEdges : localExcludedCombinations) {
|
||||
final MethodsChain local =
|
||||
search(searchService, initializer, contextQNames, excludedEdges, pathMaximalLength, resolver, targetQName,
|
||||
excludedParamsTypesQNames, contextMethodName);
|
||||
if (local != null) {
|
||||
allLocalsIsNull = false;
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
boolean add = true;
|
||||
for (int i = 0; i < methodsChains.size(); i++) {
|
||||
final MethodsChain chain = methodsChains.get(i);
|
||||
final MethodsChain.CompareResult compareResult = MethodsChain.compare(local, chain);
|
||||
if (compareResult == MethodsChain.CompareResult.EQUAL || compareResult == MethodsChain.CompareResult.RIGHT_CONTAINS_LEFT) {
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
else if (compareResult == MethodsChain.CompareResult.LEFT_CONTAINS_RIGHT) {
|
||||
methodsChains.set(i, local);
|
||||
add = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
methodsChains.add(local);
|
||||
if (methodsChains.size() >= maxResultSize) {
|
||||
return methodsChains;
|
||||
}
|
||||
excludedCombinations = MethodsChain.edgeCombinations(excludedCombinations, local);
|
||||
}
|
||||
}
|
||||
if (allLocalsIsNull || beforeStepChainsCount == methodsChains.size()) {
|
||||
return methodsChains;
|
||||
}
|
||||
}
|
||||
}
|
||||
return methodsChains;
|
||||
}
|
||||
|
||||
private static SearchInitializer createInitializer(final String targetQName,
|
||||
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> context,
|
||||
final MethodChainsSearchService searchService,
|
||||
final Set<String> excludedParamsTypesQNames) {
|
||||
return new SearchInitializer(searchService.getMethods(targetQName), context, targetQName, excludedParamsTypesQNames);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MethodsChain search(final MethodChainsSearchService searchService,
|
||||
final SearchInitializer initializer,
|
||||
final Set<String> toSet,
|
||||
final Set<String> excludedEdgeNames,
|
||||
final int pathMaximalLength,
|
||||
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
|
||||
final String targetQName,
|
||||
final Set<String> excludedParamsTypesQNames,
|
||||
final String contextMethodName) {
|
||||
final Set<String> allExcludedNames = MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName);
|
||||
ProgressManager.checkCanceled();
|
||||
final SearchInitializer.InitResult initResult = initializer.init(excludedEdgeNames, toSet, searchService, contextMethodName);
|
||||
final Map<MethodIncompleteSignature, MethodsChain> knownDistance = initResult.getChains();
|
||||
final PriorityQueue<WeightAware<MethodIncompleteSignature>> q =
|
||||
new PriorityQueue<WeightAware<MethodIncompleteSignature>>(initResult.getVertexes());
|
||||
MethodsChain result = initResult.getCurrentBestTargetChain();
|
||||
|
||||
int maxWeight = 0;
|
||||
for (final MethodsChain methodsChain : knownDistance.values()) {
|
||||
if (methodsChain.getChainWeight() > maxWeight) {
|
||||
maxWeight = methodsChain.getChainWeight();
|
||||
}
|
||||
}
|
||||
|
||||
final WeightAware<MethodIncompleteSignature> maxVertex = q.peek();
|
||||
final int maxDistance;
|
||||
if (maxVertex != null) {
|
||||
maxDistance = maxVertex.getWeight();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (!q.isEmpty()) {
|
||||
final WeightAware<MethodIncompleteSignature> currentVertex = q.poll();
|
||||
final int currentVertexDistance = currentVertex.getWeight();
|
||||
if (currentVertexDistance * Constants.CHAIN_SEARCH_MAGIC_RATIO < maxDistance) {
|
||||
return result;
|
||||
}
|
||||
final MethodIncompleteSignature currentVertexUnderlying = currentVertex.getUnderlying();
|
||||
final MethodsChain currentVertexMethodsChain = knownDistance.get(currentVertexUnderlying);
|
||||
if (currentVertexDistance != currentVertexMethodsChain.getChainWeight()) {
|
||||
continue;
|
||||
}
|
||||
final SortedSet<UsageIndexValue> bigrams = searchService.getBigram(currentVertexUnderlying);
|
||||
int bigramsSumWeight = 0;
|
||||
int maxUpdatedWeight = 0;
|
||||
for (final UsageIndexValue indexValue : bigrams) {
|
||||
final MethodIncompleteSignature vertex = indexValue.getMethodIncompleteSignature();
|
||||
final int occurrences = indexValue.getOccurrences();
|
||||
bigramsSumWeight += occurrences;
|
||||
final boolean canBeResult = vertex.isStatic() || toSet.contains(vertex.getOwner());
|
||||
if (!vertex.getOwner().equals(targetQName) || canBeResult) {
|
||||
final int vertexDistance = Math.min(currentVertexDistance, occurrences);
|
||||
final MethodsChain knownVertexMethodsChain = knownDistance.get(vertex);
|
||||
if ((knownVertexMethodsChain == null || knownVertexMethodsChain.getChainWeight() < vertexDistance) &&
|
||||
(result == null || result.getChainWeight() < vertexDistance)) {
|
||||
if (occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO >= currentVertexMethodsChain.getChainWeight()) {
|
||||
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
|
||||
final PsiMethod[] psiMethods = resolver.get(methodInvocation);
|
||||
|
||||
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, allExcludedNames)) {
|
||||
final MethodsChain newBestMethodsChain = currentVertexMethodsChain.addEdge(psiMethods);
|
||||
if (canBeResult) {
|
||||
result = newBestMethodsChain;
|
||||
}
|
||||
else if (newBestMethodsChain.size() < pathMaximalLength - 1) {
|
||||
maxUpdatedWeight = Math.max(maxUpdatedWeight, newBestMethodsChain.getChainWeight());
|
||||
q.add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(),
|
||||
newBestMethodsChain.getChainWeight()));
|
||||
}
|
||||
knownDistance.put(vertex, newBestMethodsChain);
|
||||
}
|
||||
}
|
||||
else if (!allExcludedNames.contains(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName()) &&
|
||||
searchService.isSingleton(currentVertexMethodsChain.getFirstQualifierClass(), contextMethodName) &&
|
||||
(searchService.isRelevantMethodForNotOverriden(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
|
||||
currentVertexMethodsChain.getOneOfFirst().getName()) ||
|
||||
searchService.isRelevantMethodForField(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
|
||||
currentVertexMethodsChain.getOneOfFirst().getName()))) {
|
||||
result = currentVertexMethodsChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//if ((result == null || maxUpdatedWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO2 <= bigramsSumWeight)
|
||||
// && bigramsSumWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO >= currentVertexMethodsChain.getChainWeight()) {
|
||||
// return currentVertexMethodsChain;
|
||||
//}
|
||||
|
||||
if ((currentVertexMethodsChain.isStaticChain() ||
|
||||
!allExcludedNames.contains(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName())) &&
|
||||
bigramsSumWeight * Constants.CHAIN_SEARCH_MAGIC_RATIO <= currentVertexDistance &&
|
||||
(result == null || result.getChainWeight() < currentVertexDistance) &&
|
||||
(currentVertexMethodsChain.isStaticChain() ||
|
||||
searchService.isSingleton(currentVertexMethodsChain.getFirstQualifierClass(), contextMethodName) &&
|
||||
(searchService.isRelevantMethodForNotOverriden(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
|
||||
currentVertexMethodsChain.getOneOfFirst().getName()) ||
|
||||
searchService.isRelevantMethodForField(currentVertexMethodsChain.getFirstQualifierClass().getQualifiedName(),
|
||||
currentVertexMethodsChain.getOneOfFirst().getName())))) {
|
||||
result = currentVertexMethodsChain;
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null && result.getChainWeight() * Constants.CHAIN_SEARCH_MAGIC_RATIO >= maxWeight) {
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodsUsageIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
|
||||
import com.intellij.compilerOutputIndex.impl.bigram.BigramMethodsUsageIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodNameAndQualifier;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.service.OverridenMethodsService;
|
||||
import com.intellij.codeInsight.completion.methodChains.search.service.SingletonService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class MethodChainsSearchService {
|
||||
private final static SortedSet EMPTY_SORTED_SET = new TreeSet();
|
||||
|
||||
private final MethodsUsageIndex myMethodsUsageIndex;
|
||||
private final BigramMethodsUsageIndex myBigramMethodsUsageIndex;
|
||||
private final SingletonService mySingletonService;
|
||||
private final OverridenMethodsService myOverridenMethodsService;
|
||||
private final Project myProject;
|
||||
private final Map<String, Boolean> mySingletonLocalCache;
|
||||
|
||||
public MethodChainsSearchService(final Project project) {
|
||||
myOverridenMethodsService = new OverridenMethodsService(project);
|
||||
myMethodsUsageIndex = MethodsUsageIndex.getInstance(project);
|
||||
myBigramMethodsUsageIndex = BigramMethodsUsageIndex.getInstance(project);
|
||||
mySingletonService = new SingletonService(project);
|
||||
myProject = project;
|
||||
|
||||
mySingletonLocalCache = new HashMap<String, Boolean>();
|
||||
mySingletonLocalCache.put(null, false);
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public SortedSet<UsageIndexValue> getBigram(final MethodIncompleteSignature methodIncompleteSignature) {
|
||||
final TreeSet<UsageIndexValue> value = myBigramMethodsUsageIndex.getValues(methodIncompleteSignature);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return EMPTY_SORTED_SET;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public SortedSet<UsageIndexValue> getMethods(final String targetQName) {
|
||||
final TreeSet<UsageIndexValue> value = myMethodsUsageIndex.getValues(targetQName);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return EMPTY_SORTED_SET;
|
||||
}
|
||||
|
||||
public boolean isSingleton(@NotNull final PsiClass psiClass, final String contextMethodName) {
|
||||
return isSingleton(psiClass.getQualifiedName(), contextMethodName);
|
||||
}
|
||||
|
||||
public boolean isSingleton(@Nullable final String typeQName, final String methodName) {
|
||||
Boolean isSingleton = mySingletonLocalCache.get(typeQName);
|
||||
if (isSingleton == null) {
|
||||
isSingleton = mySingletonService.isSingleton(typeQName, methodName);
|
||||
mySingletonLocalCache.put(typeQName, isSingleton);
|
||||
}
|
||||
return isSingleton;
|
||||
}
|
||||
|
||||
public boolean isRelevantMethodForField(@NotNull final String className, @NotNull final String methodName) {
|
||||
final Pair<Integer, Integer> occurrences =
|
||||
myOverridenMethodsService.getMethodUsageInFieldContext(new MethodNameAndQualifier(methodName, className));
|
||||
return occurrences.getFirst() > occurrences.getSecond();
|
||||
}
|
||||
|
||||
public boolean isRelevantMethodForNotOverriden(@NotNull final String className, @NotNull final String methodName) {
|
||||
final Pair<Integer, Integer> occurrences =
|
||||
myOverridenMethodsService.getMethodsUsageInOverridenContext(new MethodNameAndQualifier(methodName, className));
|
||||
return occurrences.getFirst() < occurrences.getSecond();
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
import com.intellij.psi.PsiParameterList;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class MethodChainsSearchUtil {
|
||||
private MethodChainsSearchUtil() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiMethod getMethodWithMinNotPrimitiveParameters(final @NotNull PsiMethod[] methods,
|
||||
final Set<String> excludedParamsQNames) {
|
||||
PsiMethod minMethod = null;
|
||||
int minParametersCount = Integer.MAX_VALUE;
|
||||
for (final PsiMethod method : methods) {
|
||||
final PsiParameterList parameterList = method.getParameterList();
|
||||
boolean doContinue = false;
|
||||
int parametersCount = parameterList.getParametersCount();
|
||||
for (final PsiParameter p : parameterList.getParameters()) {
|
||||
if (!(p.getType() instanceof PsiPrimitiveType)) {
|
||||
if (excludedParamsQNames.contains(p.getType().getCanonicalText())) {
|
||||
doContinue = true;
|
||||
break;
|
||||
}
|
||||
parametersCount++;
|
||||
}
|
||||
}
|
||||
if (doContinue) {
|
||||
continue;
|
||||
}
|
||||
if (parametersCount < minParametersCount) {
|
||||
if (parametersCount == 0) {
|
||||
return method;
|
||||
}
|
||||
minParametersCount = parametersCount;
|
||||
minMethod = method;
|
||||
}
|
||||
}
|
||||
return minMethod;
|
||||
}
|
||||
|
||||
public static boolean checkParametersForTypesQNames(final PsiMethod[] psiMethods, final Set<String> excludedTypesQNames) {
|
||||
if (psiMethods.length == 0) {
|
||||
return true;
|
||||
}
|
||||
for (final PsiMethod method : psiMethods) {
|
||||
boolean hasTargetInParams = false;
|
||||
for (final PsiParameter param : method.getParameterList().getParameters()) {
|
||||
final String paramType = param.getType().getCanonicalText();
|
||||
if (excludedTypesQNames.contains(paramType)) {
|
||||
hasTargetInParams = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasTargetInParams) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <T> HashSet<T> unionToHashSet(final Collection<T> collection, final T... items) {
|
||||
final HashSet<T> result = new HashSet<T>();
|
||||
result.addAll(collection);
|
||||
Collections.addAll(result, items);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.util.containers.ContainerUtil.reverse;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodsChain {
|
||||
private final List<PsiMethod[]> myRevertedPath;
|
||||
private final int myWeight;
|
||||
|
||||
public MethodsChain(final PsiMethod[] methods, final int weight) {
|
||||
this(ContainerUtil.<PsiMethod[]>newArrayList(methods), weight);
|
||||
}
|
||||
|
||||
public MethodsChain(final List<PsiMethod[]> revertedPath, final int weight) {
|
||||
myRevertedPath = revertedPath;
|
||||
myWeight = weight;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myRevertedPath.size();
|
||||
}
|
||||
|
||||
public boolean isStaticChain() {
|
||||
return myRevertedPath.get(myRevertedPath.size() - 1)[0].hasModifierProperty(PsiModifier.STATIC);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiClass getFirstQualifierClass() {
|
||||
return myRevertedPath.isEmpty() ? null : myRevertedPath.get(myRevertedPath.size() - 1)[0].getContainingClass();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiMethod getOneOfFirst() {
|
||||
return (myRevertedPath.isEmpty() || myRevertedPath.get(0).length == 0) ? null : myRevertedPath.get(myRevertedPath.size() - 1)[0];
|
||||
}
|
||||
|
||||
public List<PsiMethod[]> getPath() {
|
||||
return reverse(myRevertedPath);
|
||||
}
|
||||
|
||||
public int getChainWeight() {
|
||||
return myWeight;
|
||||
}
|
||||
|
||||
public MethodsChain addEdge(final PsiMethod[] psiMethods) {
|
||||
final List<PsiMethod[]> newRevertedPath = new ArrayList<PsiMethod[]>(myRevertedPath.size() + 1);
|
||||
newRevertedPath.addAll(myRevertedPath);
|
||||
newRevertedPath.add(psiMethods);
|
||||
return new MethodsChain(newRevertedPath, myWeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* checking only method names
|
||||
*/
|
||||
public boolean weakContains(final MethodsChain otherChain) {
|
||||
if (otherChain.myRevertedPath.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
if (myRevertedPath.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
final Iterator<PsiMethod[]> otherChainIterator = otherChain.myRevertedPath.iterator();
|
||||
String otherChainCurrentName = otherChainIterator.next()[0].getName();
|
||||
boolean checkingStarted = false;
|
||||
for (final PsiMethod[] methods : myRevertedPath) {
|
||||
final String thisCurrentName = methods[0].getName();
|
||||
if (!checkingStarted && thisCurrentName.equals(otherChainCurrentName)) {
|
||||
checkingStarted = true;
|
||||
}
|
||||
if (checkingStarted) {
|
||||
if (otherChainIterator.hasNext()) {
|
||||
otherChainCurrentName = otherChainIterator.next()[0].getName();
|
||||
if (!otherChainCurrentName.equals(thisCurrentName)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !otherChainIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtil.join(myRevertedPath, "<-");
|
||||
}
|
||||
|
||||
public static Set<Set<String>> edgeCombinations(final Set<Set<String>> oldCombinations,
|
||||
final MethodsChain methodsChain) {
|
||||
if (oldCombinations.isEmpty()) {
|
||||
final Set<Set<String>> result = new HashSet<Set<String>>(methodsChain.myRevertedPath.size());
|
||||
for (final PsiMethod[] e : methodsChain.myRevertedPath) {
|
||||
final Set<String> set = new HashSet<String>();
|
||||
set.add(e[0].getName());
|
||||
result.add(set);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
final Set<Set<String>> newTail = new HashSet<Set<String>>(oldCombinations.size() * methodsChain.size());
|
||||
for (final PsiMethod[] e : methodsChain.myRevertedPath) {
|
||||
final String methodName = e[0].getName();
|
||||
for (final Set<String> tailSet : oldCombinations) {
|
||||
final Set<String> newSet = new HashSet<String>(tailSet);
|
||||
newSet.add(methodName);
|
||||
if (!oldCombinations.contains(newSet)) {
|
||||
newTail.add(newSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newTail;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static CompareResult compare(final MethodsChain left, final MethodsChain right) {
|
||||
if (left.size() == 0) {
|
||||
return CompareResult.RIGHT_CONTAINS_LEFT;
|
||||
}
|
||||
if (right.size() == 0) {
|
||||
return CompareResult.LEFT_CONTAINS_RIGHT;
|
||||
}
|
||||
final Iterator<PsiMethod[]> leftIterator = left.myRevertedPath.iterator();
|
||||
final Iterator<PsiMethod[]> rightIterator = right.myRevertedPath.iterator();
|
||||
|
||||
final PsiManager psiManager = PsiManager.getInstance(left.getFirstQualifierClass().getProject());
|
||||
while (leftIterator.hasNext() && rightIterator.hasNext()) {
|
||||
final PsiMethod thisNext = leftIterator.next()[0];
|
||||
final PsiMethod thatNext = rightIterator.next()[0];
|
||||
if (((thisNext.isConstructor() != thatNext.isConstructor()))
|
||||
|| !thisNext.getName().equals(thatNext.getName())
|
||||
|| !psiManager.areElementsEquivalent(thisNext.getContainingClass(), thatNext.getContainingClass())) {
|
||||
return CompareResult.NOT_EQUAL;
|
||||
}
|
||||
}
|
||||
if (leftIterator.hasNext() && !rightIterator.hasNext()) {
|
||||
return CompareResult.LEFT_CONTAINS_RIGHT;
|
||||
}
|
||||
if (!leftIterator.hasNext() && rightIterator.hasNext()) {
|
||||
return CompareResult.RIGHT_CONTAINS_LEFT;
|
||||
}
|
||||
return CompareResult.EQUAL;
|
||||
}
|
||||
|
||||
public enum CompareResult {
|
||||
LEFT_CONTAINS_RIGHT,
|
||||
RIGHT_CONTAINS_LEFT,
|
||||
EQUAL,
|
||||
NOT_EQUAL
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.completion.JavaChainLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextRelevantStaticMethod;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.context.ContextRelevantVariableGetter;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.ChainCompletionNewVariableLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.WeightableChainLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.GetterLookupSubLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.SubLookupElement;
|
||||
import com.intellij.codeInsight.completion.methodChains.completion.lookup.sub.VariableSubLookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.VariableLookupItem;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.codeInsight.completion.methodChains.completion.lookup.ChainCompletionLookupElementUtil.createLookupElement;
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class MethodsChainLookupRangingHelper {
|
||||
|
||||
public static List<LookupElement> chainsToWeightableLookupElements(final List<MethodsChain> chains,
|
||||
final ChainCompletionContext context) {
|
||||
final List<LookupElement> lookupElements = new ArrayList<LookupElement>(chains.size());
|
||||
for (final MethodsChain chain : chains) {
|
||||
final LookupElement lookupElement = chainToWeightableLookupElement(chain, context);
|
||||
if (lookupElement != null) {
|
||||
lookupElements.add(lookupElement);
|
||||
}
|
||||
}
|
||||
return lookupElements;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Nullable
|
||||
private static WeightableChainLookupElement chainToWeightableLookupElement(final MethodsChain chain,
|
||||
final ChainCompletionContext context) {
|
||||
final int chainSize = chain.size();
|
||||
assert chainSize != 0;
|
||||
final int lastMethodWeight = chain.getChainWeight();
|
||||
int unreachableParametersCount = 0;
|
||||
int notMatchedStringVars = 0;
|
||||
Boolean isFirstMethodStatic = null;
|
||||
Boolean hasCallingVariableInContext = null;
|
||||
LookupElement chainLookupElement = null;
|
||||
|
||||
final NullableNotNullManager nullableNotNullManager = NullableNotNullManager.getInstance(context.getProject());
|
||||
|
||||
for (final PsiMethod[] psiMethods : chain.getPath()) {
|
||||
final PsiMethod method =
|
||||
MethodChainsSearchUtil.getMethodWithMinNotPrimitiveParameters(psiMethods, Collections.singleton(context.getTargetQName()));
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
if (isFirstMethodStatic == null) {
|
||||
isFirstMethodStatic = psiMethods[0].hasModifierProperty(PsiModifier.STATIC);
|
||||
}
|
||||
final MethodProcResult procResult =
|
||||
processMethod(method, context, lastMethodWeight, chainLookupElement == null, nullableNotNullManager);
|
||||
if (procResult == null) {
|
||||
return null;
|
||||
}
|
||||
if (hasCallingVariableInContext == null) {
|
||||
hasCallingVariableInContext = procResult.hasCallingVariableInContext();
|
||||
}
|
||||
unreachableParametersCount += procResult.getUnreachableParametersCount();
|
||||
notMatchedStringVars += procResult.getNotMatchedStringVars();
|
||||
chainLookupElement = chainLookupElement == null
|
||||
? procResult.getLookupElement()
|
||||
: new JavaChainLookupElement(chainLookupElement, procResult.getLookupElement());
|
||||
}
|
||||
|
||||
final ChainRelevance relevance = new ChainRelevance(chainSize,
|
||||
lastMethodWeight,
|
||||
unreachableParametersCount,
|
||||
notMatchedStringVars,
|
||||
hasCallingVariableInContext,
|
||||
isFirstMethodStatic);
|
||||
|
||||
return new WeightableChainLookupElement(chainLookupElement, relevance);
|
||||
}
|
||||
|
||||
|
||||
private static MethodProcResult processMethod(@NotNull final PsiMethod method,
|
||||
final ChainCompletionContext context,
|
||||
final int weight,
|
||||
final boolean isHeadMethod,
|
||||
final NullableNotNullManager nullableNotNullManager) {
|
||||
int unreachableParametersCount = 0;
|
||||
int notMatchedStringVars = 0;
|
||||
boolean hasCallingVariableInContext = false;
|
||||
final PsiParameterList parameterList = method.getParameterList();
|
||||
final TIntObjectHashMap<SubLookupElement> parametersMap = new TIntObjectHashMap<SubLookupElement>(parameterList.getParametersCount());
|
||||
final PsiParameter[] parameters = parameterList.getParameters();
|
||||
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));
|
||||
}
|
||||
}
|
||||
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));
|
||||
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());
|
||||
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()));
|
||||
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());
|
||||
continue;
|
||||
}
|
||||
if (!nullableNotNullManager.isNullable(parameter, true)) {
|
||||
unreachableParametersCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final LookupElement lookupElement;
|
||||
if (isHeadMethod) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
lookupElement = createLookupElement(method, parametersMap);
|
||||
}
|
||||
else if (method.isConstructor()) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
final String classQName = containingClass.getQualifiedName();
|
||||
if (classQName == null) return null;
|
||||
final Object e = ContainerUtil.getFirstItem(context.getContextRefElements(classQName), null);
|
||||
if (e != null) {
|
||||
final LookupElement firstChainElement;
|
||||
if (e instanceof PsiVariable) {
|
||||
hasCallingVariableInContext = true;
|
||||
firstChainElement = new VariableLookupItem((PsiVariable)e);
|
||||
}
|
||||
else if (e instanceof PsiMethod) {
|
||||
firstChainElement = createLookupElement((PsiMethod)e, null);
|
||||
}
|
||||
else if (e instanceof LookupElement) {
|
||||
firstChainElement = (LookupElement)e;
|
||||
}
|
||||
else {
|
||||
throw new AssertionError();
|
||||
}
|
||||
lookupElement = new JavaChainLookupElement(firstChainElement, createLookupElement(method, parametersMap));
|
||||
}
|
||||
else lookupElement = context.getContainingClassQNames().contains(classQName)
|
||||
? createLookupElement(method, parametersMap)
|
||||
: new JavaChainLookupElement(ChainCompletionNewVariableLookupElement.create(containingClass),
|
||||
createLookupElement(method, parametersMap));
|
||||
}
|
||||
}
|
||||
else {
|
||||
lookupElement = createLookupElement(method, parametersMap);
|
||||
}
|
||||
return new MethodProcResult(lookupElement, unreachableParametersCount, notMatchedStringVars, hasCallingVariableInContext);
|
||||
}
|
||||
|
||||
private static class MethodProcResult {
|
||||
private final LookupElement myMethodLookup;
|
||||
private final int myUnreachableParametersCount;
|
||||
private final int myNotMatchedStringVars;
|
||||
private final boolean myHasCallingVariableInContext;
|
||||
|
||||
private MethodProcResult(final LookupElement methodLookup,
|
||||
final int unreachableParametersCount,
|
||||
final int notMatchedStringVars,
|
||||
final boolean hasCallingVariableInContext) {
|
||||
myMethodLookup = methodLookup;
|
||||
myUnreachableParametersCount = unreachableParametersCount;
|
||||
myNotMatchedStringVars = notMatchedStringVars;
|
||||
myHasCallingVariableInContext = hasCallingVariableInContext;
|
||||
}
|
||||
|
||||
private boolean hasCallingVariableInContext() {
|
||||
return myHasCallingVariableInContext;
|
||||
}
|
||||
|
||||
private LookupElement getLookupElement() {
|
||||
return myMethodLookup;
|
||||
}
|
||||
|
||||
private int getUnreachableParametersCount() {
|
||||
return myUnreachableParametersCount;
|
||||
}
|
||||
|
||||
private int getNotMatchedStringVars() {
|
||||
return myNotMatchedStringVars;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.Constants;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.UsageIndexValue;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class SearchInitializer {
|
||||
private final List<WeightAware<MethodIncompleteSignature>> myVertexes;
|
||||
private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains;
|
||||
private final Map<MethodIncompleteSignature, Integer> myOccurrencesMap;
|
||||
private final FactoryMap<MethodIncompleteSignature, PsiMethod[]> myResolver;
|
||||
|
||||
public SearchInitializer(final SortedSet<UsageIndexValue> indexValues,
|
||||
final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver,
|
||||
final String targetQName,
|
||||
final Set<String> excludedParamsTypesQNames) {
|
||||
myResolver = resolver;
|
||||
final int size = indexValues.size();
|
||||
myVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
|
||||
myChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
|
||||
myOccurrencesMap = new HashMap<MethodIncompleteSignature, Integer>(size);
|
||||
add(indexValues, MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName));
|
||||
}
|
||||
|
||||
private void add(final Collection<UsageIndexValue> indexValues, final Set<String> excludedParamsTypesQNames) {
|
||||
int bestOccurrences = -1;
|
||||
for (final UsageIndexValue indexValue : indexValues) {
|
||||
if (add(indexValue, excludedParamsTypesQNames)) {
|
||||
final int occurrences = indexValue.getOccurrences();
|
||||
if (bestOccurrences == -1) {
|
||||
bestOccurrences = occurrences;
|
||||
} else if (bestOccurrences > occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean add(final UsageIndexValue indexValue, final Set<String> excludedParamsTypesQNames) {
|
||||
final MethodIncompleteSignature methodInvocation = indexValue.getMethodIncompleteSignature();
|
||||
final PsiMethod[] psiMethods = myResolver.get(methodInvocation);
|
||||
if (psiMethods.length != 0 && MethodChainsSearchUtil.checkParametersForTypesQNames(psiMethods, excludedParamsTypesQNames)) {
|
||||
final int occurrences = indexValue.getOccurrences();
|
||||
final MethodsChain methodsChain = new MethodsChain(psiMethods, occurrences);
|
||||
myChains.put(methodInvocation, methodsChain);
|
||||
myVertexes.add(new WeightAware<MethodIncompleteSignature>(methodInvocation, occurrences));
|
||||
myOccurrencesMap.put(methodInvocation, occurrences);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public InitResult init(final Set<String> excludedEdgeNames,
|
||||
final Set<String> contextQNames,
|
||||
final MethodChainsSearchService searchService,
|
||||
final String contextMethodName) {
|
||||
final int size = myVertexes.size();
|
||||
int bestOccurrences = 0;
|
||||
MethodsChain bestTargetMethodChain = null;
|
||||
final List<WeightAware<MethodIncompleteSignature>> initedVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size);
|
||||
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> initedChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size);
|
||||
final Iterator<Map.Entry<MethodIncompleteSignature, MethodsChain>> chainsIterator = myChains.entrySet().iterator();
|
||||
for (final WeightAware<MethodIncompleteSignature> vertex : myVertexes) {
|
||||
final Map.Entry<MethodIncompleteSignature, MethodsChain> chainEntry = chainsIterator.next();
|
||||
final MethodIncompleteSignature method = vertex.getUnderlying();
|
||||
if (!excludedEdgeNames.contains(method.getName())) {
|
||||
initedVertexes.add(vertex);
|
||||
final MethodsChain methodsChain = chainEntry.getValue();
|
||||
initedChains.put(chainEntry.getKey(), methodsChain);
|
||||
if (contextQNames.contains(method.getOwner())) {
|
||||
final Integer occurrences = myOccurrencesMap.get(method);
|
||||
if (occurrences > bestOccurrences) {
|
||||
final PsiMethod oneOfFirst = methodsChain.getOneOfFirst();
|
||||
if (oneOfFirst != null && oneOfFirst.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
bestTargetMethodChain = methodsChain;
|
||||
bestOccurrences = occurrences;
|
||||
continue;
|
||||
}
|
||||
final PsiClass firstQualifierClass = methodsChain.getFirstQualifierClass();
|
||||
if (firstQualifierClass != null && (searchService.isSingleton(firstQualifierClass, contextMethodName)
|
||||
|| contextQNames.contains(firstQualifierClass.getQualifiedName()))) {
|
||||
bestTargetMethodChain = methodsChain;
|
||||
bestOccurrences = occurrences;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new InitResult(initedVertexes, initedChains, bestTargetMethodChain);
|
||||
}
|
||||
|
||||
public static class InitResult {
|
||||
private final List<WeightAware<MethodIncompleteSignature>> myVertexes;
|
||||
private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains;
|
||||
private final MethodsChain myCurrentBestTargetChain;
|
||||
|
||||
private InitResult(final List<WeightAware<MethodIncompleteSignature>> vertexes,
|
||||
final LinkedHashMap<MethodIncompleteSignature, MethodsChain> chains,
|
||||
final @Nullable MethodsChain currentBestTargetChain) {
|
||||
this.myVertexes = vertexes;
|
||||
this.myChains = chains;
|
||||
this.myCurrentBestTargetChain = currentBestTargetChain;
|
||||
}
|
||||
|
||||
public List<WeightAware<MethodIncompleteSignature>> getVertexes() {
|
||||
return myVertexes;
|
||||
}
|
||||
|
||||
public LinkedHashMap<MethodIncompleteSignature, MethodsChain> getChains() {
|
||||
return myChains;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodsChain getCurrentBestTargetChain() {
|
||||
return myCurrentBestTargetChain;
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class WeightAware<V> implements Comparable<WeightAware<V>> {
|
||||
private final V myUnderlying;
|
||||
private final int myWeight;
|
||||
|
||||
public WeightAware(final V underlying, final int weight) {
|
||||
myUnderlying = underlying;
|
||||
myWeight = weight;
|
||||
}
|
||||
|
||||
public V getUnderlying() {
|
||||
return myUnderlying;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return myWeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull final WeightAware<V> that) {
|
||||
return -getWeight() + that.getWeight();
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search.service;
|
||||
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.callingLocation.CallingLocation;
|
||||
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodCallingLocationIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.callingLocation.MethodNameAndQualifier;
|
||||
import com.intellij.compilerOutputIndex.impl.callingLocation.VariableType;
|
||||
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickInheritanceIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickMethodsIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.quickInheritance.QuickOverrideUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class OverridenMethodsService {
|
||||
|
||||
private final QuickMethodsIndex myQuickMethodsIndex;
|
||||
private final QuickInheritanceIndex myQuickInheritanceIndex;
|
||||
private final MethodCallingLocationIndex myMethodCallingLocationIndex;
|
||||
|
||||
public OverridenMethodsService(final Project project) {
|
||||
myQuickInheritanceIndex = QuickInheritanceIndex.getInstance(project);
|
||||
myQuickMethodsIndex = QuickMethodsIndex.getInstance(project);
|
||||
myMethodCallingLocationIndex = MethodCallingLocationIndex.getInstance(project);
|
||||
}
|
||||
|
||||
public boolean isMethodOverriden(final String classQName, final String methodName) {
|
||||
return QuickOverrideUtil.isMethodOverriden(classQName, methodName, myQuickInheritanceIndex, myQuickMethodsIndex);
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getMethodsUsageInOverridenContext(final MethodNameAndQualifier method) {
|
||||
final Multiset<MethodIncompleteSignature> locationsAsParam = myMethodCallingLocationIndex.getLocationsAsParam(method);
|
||||
int overridenOccurrences = 0;
|
||||
int nonOverridenOccurrences = 0;
|
||||
for (final Multiset.Entry<MethodIncompleteSignature> e : locationsAsParam.entrySet()) {
|
||||
final MethodIncompleteSignature sign = e.getElement();
|
||||
final boolean methodOverriden = isMethodOverriden(sign.getOwner(), sign.getName());
|
||||
if (methodOverriden) {
|
||||
overridenOccurrences++;
|
||||
}
|
||||
else {
|
||||
nonOverridenOccurrences++;
|
||||
}
|
||||
}
|
||||
|
||||
return Pair.create(overridenOccurrences, nonOverridenOccurrences);
|
||||
}
|
||||
|
||||
public Pair<Integer, Integer> getMethodUsageInFieldContext(final MethodNameAndQualifier method) {
|
||||
int asField = 0;
|
||||
int notField = 0;
|
||||
for (final CallingLocation callingLocation : myMethodCallingLocationIndex.getAllLocations(method)) {
|
||||
if (callingLocation.getVariableType().equals(VariableType.FIELD)) {
|
||||
asField++;
|
||||
}
|
||||
else {
|
||||
notField++;
|
||||
}
|
||||
}
|
||||
return Pair.create(asField, notField);
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package com.intellij.codeInsight.completion.methodChains.search.service;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.Constants;
|
||||
import com.intellij.compilerOutputIndex.impl.singleton.MethodShortSignatureWithWeight;
|
||||
import com.intellij.compilerOutputIndex.impl.singleton.ParamsInMethodOccurrencesIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.singleton.TwinVariablesIndex;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.psi.JavaPsiFacade;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class SingletonService {
|
||||
private final TwinVariablesIndex myTwinVariablesIndex;
|
||||
private final ParamsInMethodOccurrencesIndex myParamsInMethodOccurrencesIndex;
|
||||
private final GlobalSearchScope myAllScope;
|
||||
private final JavaPsiFacade myJavaPsiFacade;
|
||||
|
||||
private final Map<String, Boolean> myLocalCache;
|
||||
|
||||
public SingletonService(final Project project) {
|
||||
myTwinVariablesIndex = TwinVariablesIndex.getInstance(project);
|
||||
myParamsInMethodOccurrencesIndex = ParamsInMethodOccurrencesIndex.getInstance(project);
|
||||
myAllScope = GlobalSearchScope.allScope(project);
|
||||
myJavaPsiFacade = JavaPsiFacade.getInstance(project);
|
||||
|
||||
myLocalCache = new HashMap<String, Boolean>();
|
||||
myLocalCache.put(null, false);
|
||||
}
|
||||
|
||||
public boolean isSingleton(@Nullable final String typeQName, final @NotNull String contextMethodName) {
|
||||
final Boolean isSingletonCached = myLocalCache.get(typeQName);
|
||||
if (isSingletonCached == null) {
|
||||
assert typeQName != null;
|
||||
final PsiClass aClass = myJavaPsiFacade.findClass(typeQName, myAllScope);
|
||||
if (aClass == null) {
|
||||
myLocalCache.put(typeQName, false);
|
||||
return false;
|
||||
}
|
||||
for (final PsiClass psiClass : aClass.getInterfaces()) {
|
||||
final String qualifiedName = psiClass.getQualifiedName();
|
||||
if (CommonClassNames.JAVA_LANG_OBJECT.equals(qualifiedName) || !isSingleton(qualifiedName, contextMethodName)) {
|
||||
myLocalCache.put(typeQName, false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
final boolean isSingleton = hasTwinsFeature(typeQName) && isSuitableTypeFor(typeQName, contextMethodName);
|
||||
myLocalCache.put(typeQName, isSingleton);
|
||||
return isSingleton;
|
||||
}
|
||||
return isSingletonCached;
|
||||
}
|
||||
|
||||
public boolean hasTwinsFeature(final String typeQName) {
|
||||
final List<Integer> twinInfo = myTwinVariablesIndex.getTwinInfo(typeQName);
|
||||
if (twinInfo.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
int ones = 0;
|
||||
for (final int i : twinInfo) {
|
||||
if (i == 1) {
|
||||
ones++;
|
||||
}
|
||||
}
|
||||
return (twinInfo.size() - ones) * Constants.SINGLETON_MAGIC_RATIO < twinInfo.size();
|
||||
}
|
||||
|
||||
private boolean isSuitableTypeFor(final String typeName, final String methodName) {
|
||||
final Pair<List<MethodShortSignatureWithWeight>, Integer> parameterOccurrences =
|
||||
myParamsInMethodOccurrencesIndex.getParameterOccurrences(typeName);
|
||||
if (parameterOccurrences.getSecond() == 0) {
|
||||
return true;
|
||||
}
|
||||
final List<MethodShortSignatureWithWeight> contextMethods = parameterOccurrences.getFirst();
|
||||
final MethodShortSignatureWithWeight last = contextMethods.get(contextMethods.size() - 1);
|
||||
return last.getMethodShortSignature().getName().equals(methodName) || last.getWeight() * Constants.SINGLETON_MAGIC_RATIO2 <= parameterOccurrences.getSecond();
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package com.intellij.compilerOutputIndex.api.descriptor;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class ArrayListKeyDescriptor<E> implements KeyDescriptor<List<E>> {
|
||||
|
||||
private final DataExternalizer<E> myDataExternalizer;
|
||||
|
||||
public ArrayListKeyDescriptor(final DataExternalizer<E> dataExternalizer) {
|
||||
myDataExternalizer = dataExternalizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(final DataOutput out, final List<E> list) throws IOException {
|
||||
out.writeInt(list.size());
|
||||
for (final E element : list) {
|
||||
myDataExternalizer.save(out, element);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<E> read(final DataInput in) throws IOException {
|
||||
final int size = in.readInt();
|
||||
final ArrayList<E> list = new ArrayList<E>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
list.add(myDataExternalizer.read(in));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(final List<E> value) {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(final List<E> val1, final List<E> val2) {
|
||||
return val1.equals(val2);
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.intellij.compilerOutputIndex.api.descriptor;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class HashMapKeyDescriptor<K, V> implements KeyDescriptor<Map<K, V>> {
|
||||
|
||||
private final DataExternalizer<K> myKeyDataExternalizer;
|
||||
private final DataExternalizer<V> myValueDataExternalizer;
|
||||
|
||||
public HashMapKeyDescriptor(final DataExternalizer<K> keyDataExternalizer, final DataExternalizer<V> valueDataExternalizer) {
|
||||
myKeyDataExternalizer = keyDataExternalizer;
|
||||
myValueDataExternalizer = valueDataExternalizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(final DataOutput out, final Map<K, V> map) throws IOException {
|
||||
final int size = map.size();
|
||||
out.writeInt(size);
|
||||
for (final Map.Entry<K, V> e : map.entrySet()) {
|
||||
myKeyDataExternalizer.save(out, e.getKey());
|
||||
myValueDataExternalizer.save(out, e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> read(final DataInput in) throws IOException {
|
||||
final int size = in.readInt();
|
||||
final HashMap<K, V> map = new HashMap<K, V>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
map.put(myKeyDataExternalizer.read(in), myValueDataExternalizer.read(in));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(final Map<K, V> map) {
|
||||
return map.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(final Map<K, V> val1, final Map<K, V> val2) {
|
||||
return val1.equals(val2);
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
package com.intellij.compilerOutputIndex.api.descriptor;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class HashSetKeyDescriptor<K> implements KeyDescriptor<Set<K>> {
|
||||
|
||||
private final DataExternalizer<K> keyDataExternalizer;
|
||||
|
||||
public HashSetKeyDescriptor(final DataExternalizer<K> keyDataExternalizer) {
|
||||
this.keyDataExternalizer = keyDataExternalizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(final DataOutput out, final Set<K> set) throws IOException {
|
||||
out.writeInt(set.size());
|
||||
for (final K k : set) {
|
||||
keyDataExternalizer.save(out, k);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashSet<K> read(final DataInput in) throws IOException {
|
||||
final int size = in.readInt();
|
||||
final HashSet<K> set = new HashSet<K>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
set.add(keyDataExternalizer.read(in));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
public static <K> HashSetKeyDescriptor<K> of(final DataExternalizer<K> keyDataExternalizer) {
|
||||
return new HashSetKeyDescriptor<K>(keyDataExternalizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(final Set<K> value) {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(final Set<K> val1, final Set<K> val2) {
|
||||
return val1.equals(val2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package com.intellij.compilerOutputIndex.api.fs;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.Type;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public final class AsmUtil implements Opcodes {
|
||||
|
||||
private AsmUtil() {}
|
||||
|
||||
public static boolean isStaticMethodDeclaration(final int access) {
|
||||
return (access & Opcodes.ACC_STATIC) != 0;
|
||||
}
|
||||
|
||||
public static boolean isStaticMethodInvocation(final int opcode) {
|
||||
return opcode == Opcodes.INVOKESTATIC;
|
||||
}
|
||||
|
||||
public static String getQualifiedClassName(final String name) {
|
||||
return asJavaInnerClassQName(Type.getObjectType(name).getClassName());
|
||||
}
|
||||
|
||||
public static String getReturnType(final String desc) {
|
||||
return asJavaInnerClassQName(Type.getReturnType(desc).getClassName());
|
||||
}
|
||||
|
||||
public static String[] getQualifiedClassNames(final String[] classNames, final String... yetAnotherClassNames) {
|
||||
final List<String> qualifiedClassNames = new ArrayList<String>(classNames.length + yetAnotherClassNames.length);
|
||||
for (final String className : classNames) {
|
||||
qualifiedClassNames.add(getQualifiedClassName(className));
|
||||
}
|
||||
for (final String className : yetAnotherClassNames) {
|
||||
if (className != null) {
|
||||
qualifiedClassNames.add(getQualifiedClassName(className));
|
||||
}
|
||||
}
|
||||
return ArrayUtil.toStringArray(qualifiedClassNames);
|
||||
}
|
||||
|
||||
public static String[] getParamsTypes(final String desc) {
|
||||
final Type[] types = Type.getArgumentTypes(desc);
|
||||
final String[] typesAsString = new String[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
typesAsString[i] = types[i].getClassName();
|
||||
}
|
||||
return typesAsString;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getSignature(final PsiMethod psiMethod) {
|
||||
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (final PsiParameter p : parameters) {
|
||||
final String desc = getDescriptor(p);
|
||||
if (desc == null) {
|
||||
return null;
|
||||
}
|
||||
sb.append(desc);
|
||||
}
|
||||
sb.append(")");
|
||||
final String desc = getDescriptor(psiMethod.getReturnType());
|
||||
if (desc == null) {
|
||||
return null;
|
||||
}
|
||||
sb.append(desc);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDescriptor(final PsiParameter parameter) {
|
||||
return getDescriptor(parameter.getType());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDescriptor(@Nullable final PsiType type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
if (type instanceof PsiPrimitiveType) {
|
||||
final PsiPrimitiveType primitiveType = (PsiPrimitiveType) type;
|
||||
if (PsiType.INT.equals(primitiveType)) {
|
||||
return "I";
|
||||
} else if (primitiveType.equals(PsiType.VOID)) {
|
||||
return "V";
|
||||
} else if (primitiveType.equals(PsiType.BOOLEAN)) {
|
||||
return "Z";
|
||||
} else if (primitiveType.equals(PsiType.BYTE)) {
|
||||
return "B";
|
||||
} else if (primitiveType.equals(PsiType.CHAR)) {
|
||||
return "C";
|
||||
} else if (primitiveType.equals(PsiType.SHORT)) {
|
||||
return "S";
|
||||
} else if (primitiveType.equals(PsiType.DOUBLE)) {
|
||||
return "D";
|
||||
} else if (primitiveType.equals(PsiType.FLOAT)) {
|
||||
return "F";
|
||||
} else /* if (primitiveType.equals(PsiType.LONG)) */ {
|
||||
return "J";
|
||||
}
|
||||
} else if (type instanceof PsiArrayType) {
|
||||
return "[" + getDescriptor(((PsiArrayType) type).getComponentType());
|
||||
} else {
|
||||
final PsiClassType classType = (PsiClassType) type;
|
||||
final PsiClass aClass = classType.resolve();
|
||||
if (aClass == null) {
|
||||
return null;
|
||||
}
|
||||
final String qName = aClass.getQualifiedName();
|
||||
if (qName == null) {
|
||||
return null;
|
||||
}
|
||||
return "L" + StringUtil.replace(qName, ".", "/") + ";";
|
||||
}
|
||||
}
|
||||
|
||||
private static String asJavaInnerClassQName(final String byteCodeClassQName) {
|
||||
return StringUtil.replaceChar(byteCodeClassQName, '$', '.');
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package com.intellij.compilerOutputIndex.api.fs;
|
||||
|
||||
import com.intellij.openapi.compiler.CompilerPaths;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public final class CompilerOutputFilesUtil {
|
||||
|
||||
private CompilerOutputFilesUtil() {}
|
||||
|
||||
public final static String CLASS_FILES_SUFFIX = ".class";
|
||||
|
||||
public static void iterateProjectClassFiles(@NotNull final Project project, @NotNull final Consumer<File> fileConsumer) {
|
||||
for (final Module module : ModuleManager.getInstance(project).getModules()) {
|
||||
iterateModuleClassFiles(module, fileConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void iterateModuleClassFiles(@NotNull final Module module, @NotNull final Consumer<File> fileConsumer) {
|
||||
final VirtualFile moduleOutputDirectory = CompilerPaths.getModuleOutputDirectory(module, false);
|
||||
if (moduleOutputDirectory == null) {
|
||||
return;
|
||||
}
|
||||
final String canonicalPath = moduleOutputDirectory.getCanonicalPath();
|
||||
if (canonicalPath == null) {
|
||||
return;
|
||||
}
|
||||
final File root = new File(canonicalPath);
|
||||
iterateClassFilesOverRoot(root, fileConsumer);
|
||||
}
|
||||
|
||||
public static void iterateClassFilesOverRoot(@NotNull final File file, final Consumer<File> fileConsumer) {
|
||||
iterateClassFilesOverRoot(file, fileConsumer, new HashSet<File>());
|
||||
}
|
||||
|
||||
private static void iterateClassFilesOverRoot(@NotNull final File file, final Consumer<File> fileConsumer, final Set<File> visited) {
|
||||
if (file.isDirectory()) {
|
||||
final File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (final File childFile : files) {
|
||||
if (visited.add(childFile)) {
|
||||
iterateClassFilesOverRoot(childFile.getAbsoluteFile(), fileConsumer, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (file.getName().endsWith(CLASS_FILES_SUFFIX)) {
|
||||
fileConsumer.consume(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.intellij.compilerOutputIndex.api.fs;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.Consumer;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public interface FileVisitorService {
|
||||
|
||||
interface Visitor {
|
||||
void visit(File file);
|
||||
}
|
||||
|
||||
void visit(final Consumer<File> visitor);
|
||||
|
||||
class ProjectClassFiles implements FileVisitorService {
|
||||
private final Project myProject;
|
||||
|
||||
public ProjectClassFiles(final Project project) {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(final Consumer<File> visitor) {
|
||||
CompilerOutputFilesUtil.iterateProjectClassFiles(myProject, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
class DirectoryClassFiles implements FileVisitorService {
|
||||
private final File myDir;
|
||||
|
||||
public DirectoryClassFiles(final File dir) {
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
myDir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(final Consumer<File> visitor) {
|
||||
//noinspection ConstantConditions
|
||||
for (final File file : myDir.listFiles()) {
|
||||
if (file.getName().endsWith(CompilerOutputFilesUtil.CLASS_FILES_SUFFIX)) {
|
||||
visitor.consume(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package com.intellij.compilerOutputIndex.api.indexer;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Factory;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.indexing.*;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import com.intellij.util.io.PersistentHashMap;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.intellij.util.indexing.IndexInfrastructure.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public abstract class CompilerOutputBaseIndex<K, V> {
|
||||
public final static ExtensionPointName<CompilerOutputBaseIndex> EXTENSION_POINT_NAME =
|
||||
ExtensionPointName.create("com.intellij.java.compilerOutputIndex");
|
||||
|
||||
private final static Logger LOG = Logger.getInstance(CompilerOutputBaseIndex.class);
|
||||
private final KeyDescriptor<K> myKeyDescriptor;
|
||||
private final DataExternalizer<V> myValueExternalizer;
|
||||
|
||||
protected volatile MapReduceIndex<K, V, ClassReader> myIndex;
|
||||
|
||||
private volatile Project myProject;
|
||||
|
||||
public CompilerOutputBaseIndex(final KeyDescriptor<K> keyDescriptor, final DataExternalizer<V> valueExternalizer) {
|
||||
myKeyDescriptor = keyDescriptor;
|
||||
myValueExternalizer = valueExternalizer;
|
||||
}
|
||||
|
||||
public final boolean init(final Project project) {
|
||||
myProject = project;
|
||||
final MapReduceIndex<K, V, ClassReader> index;
|
||||
final Ref<Boolean> rewriteIndex = new Ref<Boolean>(false);
|
||||
try {
|
||||
final ID<K, V> indexId = getIndexId();
|
||||
if (!IndexInfrastructure.getIndexRootDir(indexId).exists()) {
|
||||
rewriteIndex.set(true);
|
||||
}
|
||||
final File storageFile = IndexInfrastructure.getStorageFile(indexId);
|
||||
final MapIndexStorage<K, V> indexStorage = new MapIndexStorage<K, V>(storageFile, myKeyDescriptor, myValueExternalizer, 1024);
|
||||
index = new MapReduceIndex<K, V, ClassReader>(indexId, getIndexer(), indexStorage);
|
||||
index.setInputIdToDataKeysIndex(new Factory<PersistentHashMap<Integer, Collection<K>>>() {
|
||||
@Override
|
||||
public PersistentHashMap<Integer, Collection<K>> create() {
|
||||
Exception failCause = null;
|
||||
for (int attempts = 0; attempts < 2; attempts++) {
|
||||
try {
|
||||
return FileBasedIndexImpl.createIdToDataKeysIndex(indexId, myKeyDescriptor, new MemoryIndexStorage<K, V>(indexStorage));
|
||||
}
|
||||
catch (IOException e) {
|
||||
failCause = e;
|
||||
FileUtil.delete(IndexInfrastructure.getInputIndexStorageFile(getIndexId()));
|
||||
rewriteIndex.set(true);
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("couldn't create index", failCause);
|
||||
}
|
||||
});
|
||||
final File versionFile = getVersionFile(indexId);
|
||||
if (versionFile.exists()) {
|
||||
if (versionDiffers(versionFile, getVersion())) {
|
||||
rewriteVersion(versionFile, getVersion());
|
||||
rewriteIndex.set(true);
|
||||
try {
|
||||
LOG.info("clearing index for updating index version");
|
||||
index.clear();
|
||||
}
|
||||
catch (StorageException e) {
|
||||
LOG.error("couldn't clear index for reinitializing");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (versionFile.createNewFile()) {
|
||||
rewriteVersion(versionFile, getVersion());
|
||||
rewriteIndex.set(true);
|
||||
}
|
||||
else {
|
||||
LOG.error(String.format("problems while access to index version file to index %s ", indexId));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error("couldn't initialize index");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
myIndex = index;
|
||||
return rewriteIndex.get();
|
||||
}
|
||||
|
||||
protected abstract ID<K, V> getIndexId();
|
||||
|
||||
protected abstract int getVersion();
|
||||
|
||||
protected abstract DataIndexer<K, V, ClassReader> getIndexer();
|
||||
|
||||
public final void projectClosed() {
|
||||
if (myIndex != null) {
|
||||
try {
|
||||
myIndex.flush();
|
||||
}
|
||||
catch (StorageException ignored) {
|
||||
}
|
||||
myIndex.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void update(final int id, final ClassReader classReader) {
|
||||
try {
|
||||
myIndex.update(id, classReader);
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
myIndex.clear();
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected final ID<K, V> generateIndexId(final String indexName) {
|
||||
return CompilerOutputIndexUtil.generateIndexId(indexName, myProject);
|
||||
}
|
||||
|
||||
protected final ID<K, V> generateIndexId(final Class aClass) {
|
||||
final String className = StringUtil.getShortName(aClass);
|
||||
return generateIndexId(StringUtil.trimEnd(className, "Index"));
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.intellij.compilerOutputIndex.api.indexer;
|
||||
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.indexing.ID;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class CompilerOutputIndexUtil {
|
||||
private CompilerOutputIndexUtil() {}
|
||||
|
||||
public static <K, V> ID<K, V> generateIndexId(final String indexName, final Project project) {
|
||||
return ID.create(String.format("compilerOutputIndex.%s.%d", indexName, Math.abs(project.getBasePath().hashCode())));
|
||||
}
|
||||
|
||||
public static boolean isSetterOrConstructorMethodName(final String methodName) {
|
||||
return MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(methodName) || methodName.startsWith("set");
|
||||
|
||||
}
|
||||
}
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
package com.intellij.compilerOutputIndex.api.indexer;
|
||||
|
||||
import com.intellij.compilerOutputIndex.api.fs.CompilerOutputFilesUtil;
|
||||
import com.intellij.compilerOutputIndex.api.fs.FileVisitorService;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.compiler.CompileContext;
|
||||
import com.intellij.openapi.compiler.CompileTask;
|
||||
import com.intellij.openapi.compiler.CompilerManager;
|
||||
import com.intellij.openapi.components.AbstractProjectComponent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.registry.RegistryValue;
|
||||
import com.intellij.openapi.util.registry.RegistryValueListener;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.IndexInfrastructure;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.util.io.PersistentEnumeratorDelegate;
|
||||
import com.intellij.util.io.PersistentHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class CompilerOutputIndexer extends AbstractProjectComponent {
|
||||
private final static Logger LOG = Logger.getInstance(CompilerOutputIndexer.class);
|
||||
|
||||
public final static String REGISTRY_KEY = "completion.enable.relevant.method.chain.suggestions";
|
||||
public final static String TITLE = "Compiler output indexer in progress...";
|
||||
|
||||
private volatile CompilerOutputBaseIndex[] myIndexes;
|
||||
private volatile Map<String, CompilerOutputBaseIndex> myIndexTypeQNameToIndex;
|
||||
private volatile PersistentHashMap<String, Long> myFileTimestampsIndex;
|
||||
private volatile PersistentEnumeratorDelegate<String> myFileEnumerator;
|
||||
private volatile boolean myInitialized = false;
|
||||
|
||||
private final Lock myLock = new ReentrantLock();
|
||||
private final AtomicBoolean myInProgress = new AtomicBoolean(false);
|
||||
private AtomicBoolean myEnabled = new AtomicBoolean(false);
|
||||
|
||||
public static CompilerOutputIndexer getInstance(final Project project) {
|
||||
return project.getComponent(CompilerOutputIndexer.class);
|
||||
}
|
||||
|
||||
protected CompilerOutputIndexer(final Project project) {
|
||||
super(project);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return myEnabled.get();
|
||||
}
|
||||
|
||||
private ID<String, Long> getFileTimestampsIndexId() {
|
||||
return CompilerOutputIndexUtil.generateIndexId("ProjectCompilerOutputClassFilesTimestamps", myProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void projectOpened() {
|
||||
Registry.get(REGISTRY_KEY).addListener(new RegistryValueListener.Adapter() {
|
||||
@Override
|
||||
public void afterValueChanged(final RegistryValue value) {
|
||||
myEnabled.set(value.asBoolean());
|
||||
if (myEnabled.get()) {
|
||||
doEnable();
|
||||
}
|
||||
}
|
||||
}, ApplicationManager.getApplication());
|
||||
|
||||
myEnabled = new AtomicBoolean(Registry.is(REGISTRY_KEY) || ApplicationManager.getApplication().isUnitTestMode());
|
||||
if (myEnabled.get()) {
|
||||
doEnable();
|
||||
}
|
||||
}
|
||||
|
||||
private void doEnable() {
|
||||
if (!myInitialized) {
|
||||
myIndexes = Extensions.getExtensions(CompilerOutputBaseIndex.EXTENSION_POINT_NAME, myProject);
|
||||
myIndexTypeQNameToIndex = new HashMap<String, CompilerOutputBaseIndex>();
|
||||
boolean needReindex = false;
|
||||
for (final CompilerOutputBaseIndex index : myIndexes) {
|
||||
if (index.init(myProject)) {
|
||||
needReindex = true;
|
||||
}
|
||||
myIndexTypeQNameToIndex.put(index.getClass().getCanonicalName(), index);
|
||||
}
|
||||
initTimestampIndex(needReindex);
|
||||
try {
|
||||
myFileEnumerator = new PersistentEnumeratorDelegate<String>(
|
||||
IndexInfrastructure.getStorageFile(CompilerOutputIndexUtil.generateIndexId("compilerOutputIndexFileId.enum", myProject)),
|
||||
new EnumeratorStringDescriptor(), 2048);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
CompilerManager.getInstance(myProject).addAfterTask(new CompileTask() {
|
||||
@Override
|
||||
public boolean execute(final CompileContext context) {
|
||||
if (myEnabled.get() && myInProgress.compareAndSet(false, true)) {
|
||||
myLock.lock();
|
||||
try {
|
||||
context.getProgressIndicator().setText("Compiler output indexing in progress");
|
||||
for (final Module module : context.getCompileScope().getAffectedModules()) {
|
||||
CompilerOutputFilesUtil.iterateModuleClassFiles(module, new Consumer<File>() {
|
||||
@Override
|
||||
public void consume(final File file) {
|
||||
try {
|
||||
doIndexing(file, context.getProgressIndicator());
|
||||
}
|
||||
catch (ProcessCanceledException e0) {
|
||||
throw e0;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
finally {
|
||||
myLock.unlock();
|
||||
myInProgress.set(false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (needReindex) {
|
||||
reindexAllProjectInBackground();
|
||||
}
|
||||
myInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void initTimestampIndex(final boolean needReindex) {
|
||||
if (needReindex) {
|
||||
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
|
||||
}
|
||||
for (int attempts = 0; attempts < 2; attempts++) {
|
||||
try {
|
||||
myFileTimestampsIndex = new PersistentHashMap<String, Long>(IndexInfrastructure.getStorageFile(getFileTimestampsIndexId()),
|
||||
new EnumeratorStringDescriptor(), new DataExternalizer<Long>() {
|
||||
@Override
|
||||
public void save(final DataOutput out, final Long value) throws IOException {
|
||||
out.writeLong(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long read(final DataInput in) throws IOException {
|
||||
return in.readLong();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
|
||||
}
|
||||
if (myFileTimestampsIndex != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Timestamps index not initialized");
|
||||
}
|
||||
|
||||
|
||||
public void reindex(final FileVisitorService visitorService, @NotNull final ProgressIndicator indicator) {
|
||||
myLock.lock();
|
||||
try {
|
||||
indicator.setText(TITLE);
|
||||
visitorService.visit(new Consumer<File>() {
|
||||
@Override
|
||||
public void consume(final File file) {
|
||||
try {
|
||||
doIndexing(file, indicator);
|
||||
}
|
||||
catch (ProcessCanceledException e0) {
|
||||
throw e0;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
myLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void reindexAllProjectInBackground() {
|
||||
if (myInProgress.compareAndSet(false, true)) {
|
||||
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, TITLE) {
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
myIndexTypeQNameToIndex.clear();
|
||||
myInProgress.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
myInProgress.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(@NotNull final ProgressIndicator indicator) {
|
||||
reindexAllProject(indicator);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void reindexAllProject(@NotNull final ProgressIndicator indicator) {
|
||||
reindex(new FileVisitorService.ProjectClassFiles(myProject), indicator);
|
||||
}
|
||||
|
||||
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
|
||||
private void doIndexing(@NotNull final File file, @NotNull final ProgressIndicator indicator) {
|
||||
final String filePath;
|
||||
try {
|
||||
filePath = file.getCanonicalPath();
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
return;
|
||||
}
|
||||
indicator.setText2(filePath);
|
||||
final Long timestamp = getTimestamp(filePath);
|
||||
ProgressManager.checkCanceled();
|
||||
final long currentTimeStamp = file.lastModified();
|
||||
if (timestamp == null || timestamp != currentTimeStamp) {
|
||||
putTimestamp(filePath, currentTimeStamp);
|
||||
final ClassReader reader;
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
reader = new ClassReader(is);
|
||||
}
|
||||
catch (IOException e) {
|
||||
removeTimestamp(filePath);
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
final int id = myFileEnumerator.enumerate(filePath);
|
||||
for (final CompilerOutputBaseIndex index : myIndexes) {
|
||||
index.update(id, reader);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
LOG.error(String.format("can't index file: %s", file.getAbsolutePath()), e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(String.format("can't index file: %s", file.getAbsolutePath()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
myFileTimestampsIndex.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
initTimestampIndex(true);
|
||||
for (final CompilerOutputBaseIndex index : myIndexes) {
|
||||
index.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTimestamp(final String fileId) {
|
||||
try {
|
||||
myFileTimestampsIndex.remove(fileId);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Long getTimestamp(final String fileName) {
|
||||
try {
|
||||
return myFileTimestampsIndex.get(fileName);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
private void putTimestamp(final String fileName, final long timestamp) {
|
||||
try {
|
||||
myFileTimestampsIndex.put(fileName, timestamp);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void projectClosed() {
|
||||
if (myInitialized) {
|
||||
for (final CompilerOutputBaseIndex index : myIndexes) {
|
||||
index.projectClosed();
|
||||
}
|
||||
try {
|
||||
myFileTimestampsIndex.close();
|
||||
myFileEnumerator.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public void removeIndexes() {
|
||||
for (final CompilerOutputBaseIndex index : myIndexes) {
|
||||
FileUtil.delete(IndexInfrastructure.getIndexRootDir(index.getIndexId()));
|
||||
}
|
||||
FileUtil.delete(IndexInfrastructure.getIndexRootDir(getFileTimestampsIndexId()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends CompilerOutputBaseIndex> T getIndex(final Class<T> tClass) {
|
||||
final CompilerOutputBaseIndex index = myIndexTypeQNameToIndex.get(tClass.getCanonicalName());
|
||||
if (index == null) {
|
||||
throw new RuntimeException(String.format("index class with name %s not found", tClass.getName()));
|
||||
}
|
||||
return (T)index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.intellij.compilerOutputIndex.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
import org.jetbrains.asm4.ClassVisitor;
|
||||
import org.jetbrains.asm4.MethodVisitor;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class ClassFileData {
|
||||
private final List<MethodData> myMethodDatas;
|
||||
|
||||
public ClassFileData(final ClassReader classReader) {
|
||||
this(classReader, true);
|
||||
}
|
||||
|
||||
public ClassFileData(final ClassReader classReader, final boolean checkForPrimitiveReturn) {
|
||||
myMethodDatas = new ArrayList<MethodData>();
|
||||
classReader.accept(new ClassVisitor(Opcodes.ASM4) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String desc,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
final MethodDataAccumulator methodDataAccumulator = new MethodDataAccumulator(checkForPrimitiveReturn);
|
||||
myMethodDatas.add(methodDataAccumulator.getMethodData());
|
||||
return methodDataAccumulator;
|
||||
}
|
||||
}, Opcodes.ASM4);
|
||||
}
|
||||
|
||||
public List<MethodData> getMethodDatas() {
|
||||
return myMethodDatas;
|
||||
}
|
||||
|
||||
public static class MethodData {
|
||||
private final List<MethodInsnSignature> myMethodInsnSignatures = new ArrayList<MethodInsnSignature>();
|
||||
|
||||
private void addSign(final MethodInsnSignature signature) {
|
||||
myMethodInsnSignatures.add(signature);
|
||||
}
|
||||
|
||||
public List<MethodInsnSignature> getMethodInsnSignatures() {
|
||||
return myMethodInsnSignatures;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MethodDataAccumulator extends MethodVisitor {
|
||||
private final MethodData myMethodData = new MethodData();
|
||||
private final boolean myCheckForPrimitiveReturn;
|
||||
|
||||
public MethodDataAccumulator(final boolean checkForPrimitiveReturn) {
|
||||
super(Opcodes.ASM4);
|
||||
myCheckForPrimitiveReturn = checkForPrimitiveReturn;
|
||||
}
|
||||
|
||||
private MethodData getMethodData() {
|
||||
return myMethodData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
|
||||
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(name)) {
|
||||
return;
|
||||
}
|
||||
final String ownerClassName = AsmUtil.getQualifiedClassName(owner);
|
||||
if (ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(ownerClassName)) {
|
||||
return;
|
||||
}
|
||||
if (myCheckForPrimitiveReturn) {
|
||||
final String returnType = AsmUtil.getReturnType(desc);
|
||||
if (ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(returnType)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
myMethodData.addSign(new MethodInsnSignature(opcode, owner, name, desc));
|
||||
}
|
||||
}
|
||||
|
||||
public static class MethodInsnSignature {
|
||||
private final int myOpcode;
|
||||
private final String myOwner;
|
||||
private final String myName;
|
||||
private final String myDesc;
|
||||
|
||||
private MethodInsnSignature(final int opcode, final String owner, final String name, final String desc) {
|
||||
myOpcode = opcode;
|
||||
myOwner = owner;
|
||||
myName = name;
|
||||
myDesc = desc;
|
||||
}
|
||||
|
||||
public int getOpcode() {
|
||||
return myOpcode;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return myOwner;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return myDesc;
|
||||
}
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.intellij.compilerOutputIndex.impl;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.indexing.ValueContainer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public abstract class CompilerOutputBaseGramsIndex<K> extends CompilerOutputBaseIndex<K, Multiset<MethodIncompleteSignature>> {
|
||||
|
||||
protected CompilerOutputBaseGramsIndex(final KeyDescriptor<K> keyDescriptor) {
|
||||
super(keyDescriptor, new GuavaHashMultiSetExternalizer<MethodIncompleteSignature>(MethodIncompleteSignature.createKeyDescriptor()));
|
||||
}
|
||||
|
||||
public TreeSet<UsageIndexValue> getValues(final K key) {
|
||||
try {
|
||||
final ValueContainer<Multiset<MethodIncompleteSignature>> valueContainer = myIndex.getData(key);
|
||||
final Multiset<MethodIncompleteSignature> rawValues = HashMultiset.create();
|
||||
valueContainer.forEach(new ValueContainer.ContainerAction<Multiset<MethodIncompleteSignature>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final Multiset<MethodIncompleteSignature> values) {
|
||||
for (final Multiset.Entry<MethodIncompleteSignature> entry : values.entrySet()) {
|
||||
rawValues.add(entry.getElement(), entry.getCount());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return rawValuesToValues(rawValues);
|
||||
} catch (StorageException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private static TreeSet<UsageIndexValue> rawValuesToValues(final Multiset<MethodIncompleteSignature> rawValues) {
|
||||
final TreeSet<UsageIndexValue> values = new TreeSet<UsageIndexValue>();
|
||||
for (final Multiset.Entry<MethodIncompleteSignature> entry : rawValues.entrySet()) {
|
||||
values.add(new UsageIndexValue(entry.getElement(), entry.getCount()));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.compilerOutputIndex.impl;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class GuavaHashMultiSetExternalizer<K> implements DataExternalizer<Multiset<K>> {
|
||||
private final DataExternalizer<K> myKeyDataExternalizer;
|
||||
|
||||
public GuavaHashMultiSetExternalizer(final DataExternalizer<K> keyDataExternalizer) {
|
||||
myKeyDataExternalizer = keyDataExternalizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(final DataOutput out, final Multiset<K> multiset) throws IOException {
|
||||
final Set<Multiset.Entry<K>> entries = multiset.entrySet();
|
||||
out.writeInt(entries.size());
|
||||
for (final Multiset.Entry<K> entry : entries) {
|
||||
myKeyDataExternalizer.save(out, entry.getElement());
|
||||
out.writeInt(entry.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multiset<K> read(final DataInput in) throws IOException {
|
||||
final int size = in.readInt();
|
||||
final Multiset<K> multiset = HashMultiset.create(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
multiset.add(myKeyDataExternalizer.read(in), in.readInt());
|
||||
}
|
||||
return multiset;
|
||||
}
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package com.intellij.compilerOutputIndex.impl;
|
||||
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class MethodIncompleteSignature {
|
||||
|
||||
public static final String CONSTRUCTOR_METHOD_NAME = "<init>";
|
||||
|
||||
@NotNull
|
||||
private final String myOwner;
|
||||
@NotNull
|
||||
private final String myReturnType;
|
||||
@NotNull
|
||||
private final String myName;
|
||||
private final boolean myStatic;
|
||||
|
||||
public MethodIncompleteSignature(@NotNull final String owner,
|
||||
@NotNull final String returnType,
|
||||
@NotNull final String name,
|
||||
final boolean aStatic) {
|
||||
myOwner = owner;
|
||||
myReturnType = returnType;
|
||||
myName = name;
|
||||
myStatic = aStatic;
|
||||
}
|
||||
|
||||
public static MethodIncompleteSignature constructor(@NotNull final String className) {
|
||||
return new MethodIncompleteSignature(className, className, CONSTRUCTOR_METHOD_NAME, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getOwner() {
|
||||
return myOwner;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getReturnType() {
|
||||
return myReturnType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public boolean isStatic() {
|
||||
return myStatic;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod[] resolveNotDeprecated(final JavaPsiFacade javaPsiFacade, final GlobalSearchScope scope) {
|
||||
return notDeprecated(resolve(javaPsiFacade, scope));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiMethod[] resolve(final JavaPsiFacade javaPsiFacade, final GlobalSearchScope scope) {
|
||||
if (CONSTRUCTOR_METHOD_NAME.equals(getName())) {
|
||||
return PsiMethod.EMPTY_ARRAY;
|
||||
}
|
||||
final PsiClass aClass = javaPsiFacade.findClass(getOwner(), scope);
|
||||
if (aClass == null) {
|
||||
return PsiMethod.EMPTY_ARRAY;
|
||||
}
|
||||
final PsiMethod[] methods = aClass.findMethodsByName(getName(), true);
|
||||
final List<PsiMethod> filtered = new ArrayList<PsiMethod>(methods.length);
|
||||
for (final PsiMethod method : methods) {
|
||||
if (method.hasModifierProperty(PsiModifier.STATIC) == isStatic()) {
|
||||
final PsiType returnType = method.getReturnType();
|
||||
if (returnType != null && returnType.equalsToText(getReturnType())) {
|
||||
filtered.add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered.toArray(new PsiMethod[filtered.size()]);
|
||||
}
|
||||
|
||||
public static KeyDescriptor<MethodIncompleteSignature> createKeyDescriptor() {
|
||||
final EnumeratorStringDescriptor stringDescriptor = new EnumeratorStringDescriptor();
|
||||
return new KeyDescriptor<MethodIncompleteSignature>() {
|
||||
@Override
|
||||
public void save(final DataOutput out, final MethodIncompleteSignature value) throws IOException {
|
||||
stringDescriptor.save(out, value.getOwner());
|
||||
stringDescriptor.save(out, value.getReturnType());
|
||||
stringDescriptor.save(out, value.getName());
|
||||
out.writeBoolean(value.isStatic());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodIncompleteSignature read(final DataInput in) throws IOException {
|
||||
return new MethodIncompleteSignature(stringDescriptor.read(in), stringDescriptor.read(in), stringDescriptor.read(in),
|
||||
in.readBoolean());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(final MethodIncompleteSignature value) {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(final MethodIncompleteSignature val1, final MethodIncompleteSignature val2) {
|
||||
return val1.equals(val2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static PsiMethod[] notDeprecated(@NotNull final PsiMethod[] methods) {
|
||||
final List<PsiMethod> filtered = ContainerUtil.filter(methods, NOT_DEPRECATED_CONDITION);
|
||||
return filtered.toArray(new PsiMethod[filtered.size()]);
|
||||
}
|
||||
|
||||
private final static Condition<PsiMethod> NOT_DEPRECATED_CONDITION = new Condition<PsiMethod>() {
|
||||
@Override
|
||||
public boolean value(final PsiMethod method) {
|
||||
return !method.isDeprecated();
|
||||
}
|
||||
};
|
||||
|
||||
public final static Comparator<MethodIncompleteSignature> COMPARATOR = new Comparator<MethodIncompleteSignature>() {
|
||||
@Override
|
||||
public int compare(final MethodIncompleteSignature o1, final MethodIncompleteSignature o2) {
|
||||
int sub = o1.getOwner().compareTo(o2.getOwner());
|
||||
if (sub != 0) {
|
||||
return sub;
|
||||
}
|
||||
sub = o1.getName().compareTo(o2.getName());
|
||||
if (sub != 0) {
|
||||
return sub;
|
||||
}
|
||||
sub = o1.getReturnType().compareTo(o2.getReturnType());
|
||||
if (sub != 0) {
|
||||
return sub;
|
||||
}
|
||||
if (o1.isStatic() && !o2.isStatic()) {
|
||||
return 1;
|
||||
}
|
||||
if (o2.isStatic() && !o1.isStatic()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final MethodIncompleteSignature that = (MethodIncompleteSignature)o;
|
||||
|
||||
if (myStatic != that.myStatic) return false;
|
||||
if (!myName.equals(that.myName)) return false;
|
||||
if (!myOwner.equals(that.myOwner)) return false;
|
||||
if (!myReturnType.equals(that.myReturnType)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myOwner.hashCode();
|
||||
result = 31 * result + myReturnType.hashCode();
|
||||
result = 31 * result + myName.hashCode();
|
||||
result = 31 * result + (myStatic ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.intellij.compilerOutputIndex.impl;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class MethodIncompleteSignatureChain {
|
||||
private final List<MethodIncompleteSignature> myMethodIncompleteSignatures;
|
||||
|
||||
public MethodIncompleteSignatureChain(final List<MethodIncompleteSignature> methodIncompleteSignatures) {
|
||||
myMethodIncompleteSignatures = methodIncompleteSignatures;
|
||||
}
|
||||
|
||||
public List<MethodIncompleteSignature> list() {
|
||||
return myMethodIncompleteSignatures;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return myMethodIncompleteSignatures.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodIncompleteSignature getFirstInvocation() {
|
||||
final int size = myMethodIncompleteSignatures.size();
|
||||
return size == 0 ? null : myMethodIncompleteSignatures.get(0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MethodIncompleteSignature getLastInvocation() {
|
||||
final int size = myMethodIncompleteSignatures.size();
|
||||
return size == 0 ? null : myMethodIncompleteSignatures.get(size -1);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myMethodIncompleteSignatures.size();
|
||||
}
|
||||
|
||||
public MethodIncompleteSignature get(final int index) {
|
||||
return myMethodIncompleteSignatures.get(index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.intellij.compilerOutputIndex.impl;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class MethodsUsageIndex extends CompilerOutputBaseGramsIndex<String> {
|
||||
|
||||
public static MethodsUsageIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(MethodsUsageIndex.class);
|
||||
}
|
||||
|
||||
public MethodsUsageIndex() {
|
||||
super(new EnumeratorStringDescriptor());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassReader> getIndexer() {
|
||||
return new DataIndexer<String, Multiset<MethodIncompleteSignature>, ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Multiset<MethodIncompleteSignature>> map(final ClassReader inputData) {
|
||||
final Map<String, Multiset<MethodIncompleteSignature>> map = new HashMap<String, Multiset<MethodIncompleteSignature>>();
|
||||
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
|
||||
for (final ClassFileData.MethodInsnSignature ms : data.getMethodInsnSignatures()) {
|
||||
final String ownerClassName = AsmUtil.getQualifiedClassName(ms.getOwner());
|
||||
final String returnType = AsmUtil.getReturnType(ms.getDesc());
|
||||
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(ms.getName())) {
|
||||
addToIndex(map, ownerClassName, MethodIncompleteSignature.constructor(ownerClassName));
|
||||
}
|
||||
else {
|
||||
final boolean isStatic = ms.getOpcode() == Opcodes.INVOKESTATIC;
|
||||
if (!ownerClassName.equals(returnType) || isStatic) {
|
||||
addToIndex(map, returnType, new MethodIncompleteSignature(ownerClassName, returnType, ms.getName(), isStatic));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
myIndex.clear();
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<String, Multiset<MethodIncompleteSignature>> getIndexId() {
|
||||
return generateIndexId(MethodsUsageIndex.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void addToIndex(final Map<String, Multiset<MethodIncompleteSignature>> map,
|
||||
final String key,
|
||||
final MethodIncompleteSignature mi) {
|
||||
Multiset<MethodIncompleteSignature> occurrences = map.get(key);
|
||||
if (occurrences == null) {
|
||||
occurrences = HashMultiset.create();
|
||||
map.put(key, occurrences);
|
||||
}
|
||||
occurrences.add(mi);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.intellij.compilerOutputIndex.impl;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class UsageIndexValue implements Comparable<UsageIndexValue> {
|
||||
private final int myOccurrences;
|
||||
private final MethodIncompleteSignature myMethodIncompleteSignature;
|
||||
|
||||
public UsageIndexValue(final MethodIncompleteSignature signature, final int occurrences) {
|
||||
myOccurrences = occurrences;
|
||||
myMethodIncompleteSignature = signature;
|
||||
}
|
||||
|
||||
public int getOccurrences() {
|
||||
return myOccurrences;
|
||||
}
|
||||
|
||||
public MethodIncompleteSignature getMethodIncompleteSignature() {
|
||||
return myMethodIncompleteSignature;
|
||||
}
|
||||
|
||||
public static DataExternalizer<UsageIndexValue> createDataExternalizer() {
|
||||
final DataExternalizer<MethodIncompleteSignature> methodInvocationDataExternalizer = MethodIncompleteSignature.createKeyDescriptor();
|
||||
return new DataExternalizer<UsageIndexValue>() {
|
||||
@Override
|
||||
public void save(final DataOutput out, final UsageIndexValue value) throws IOException {
|
||||
methodInvocationDataExternalizer.save(out, value.myMethodIncompleteSignature);
|
||||
out.writeInt(value.myOccurrences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsageIndexValue read(final DataInput in) throws IOException {
|
||||
return new UsageIndexValue(methodInvocationDataExternalizer.read(in), in.readInt());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final UsageIndexValue that = (UsageIndexValue)o;
|
||||
|
||||
return myOccurrences == that.myOccurrences && myMethodIncompleteSignature.equals(that.myMethodIncompleteSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myOccurrences;
|
||||
result = 31 * result + myMethodIncompleteSignature.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull final UsageIndexValue that) {
|
||||
final int sub = -myOccurrences + that.myOccurrences;
|
||||
if (sub != 0) return sub;
|
||||
return MethodIncompleteSignature.COMPARATOR.compare(myMethodIncompleteSignature, that.myMethodIncompleteSignature);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.intellij.compilerOutputIndex.impl.bigram;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class Bigram<E> extends Pair<E, E> {
|
||||
public Bigram(@NotNull final E first, @NotNull final E second) {
|
||||
super(first, second);
|
||||
}
|
||||
|
||||
public Bigram<E> swap() {
|
||||
return new Bigram<E>(second, first);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s - %s", first, second);
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package com.intellij.compilerOutputIndex.impl.bigram;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.compilerOutputIndex.impl.ClassFileData;
|
||||
import com.intellij.compilerOutputIndex.impl.CompilerOutputBaseGramsIndex;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignatureChain;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class BigramMethodsUsageIndex extends CompilerOutputBaseGramsIndex<MethodIncompleteSignature> {
|
||||
public static BigramMethodsUsageIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(BigramMethodsUsageIndex.class);
|
||||
}
|
||||
|
||||
public BigramMethodsUsageIndex() {
|
||||
super(MethodIncompleteSignature.createKeyDescriptor());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> getIndexId() {
|
||||
return generateIndexId(BigramMethodsUsageIndex.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>, ClassReader> getIndexer() {
|
||||
//
|
||||
// not fair way, but works fast
|
||||
//
|
||||
return new DataIndexer<MethodIncompleteSignature,Multiset<MethodIncompleteSignature>,ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map(final ClassReader inputData) {
|
||||
final Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map =
|
||||
new HashMap<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>>();
|
||||
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
|
||||
final SimpleBigramsExtractor extractor = new SimpleBigramsExtractor(new SimpleBigramsExtractor.BigramMethodIncompleteSignatureProcessor() {
|
||||
@Override
|
||||
public void process(final Bigram<MethodIncompleteSignature> bigram) {
|
||||
final MethodIncompleteSignature secondGram = bigram.getSecond();
|
||||
Multiset<MethodIncompleteSignature> occurrences = map.get(secondGram);
|
||||
if (occurrences == null) {
|
||||
occurrences = HashMultiset.create();
|
||||
map.put(secondGram, occurrences);
|
||||
}
|
||||
occurrences.add(bigram.getFirst());
|
||||
}
|
||||
});
|
||||
for (final ClassFileData.MethodInsnSignature ms : data.getMethodInsnSignatures()) {
|
||||
final List<MethodIncompleteSignature> methodInvocations = new SmartList<MethodIncompleteSignature>();
|
||||
final String ownerClassName = AsmUtil.getQualifiedClassName(ms.getOwner());
|
||||
final String returnType = AsmUtil.getReturnType(ms.getDesc());
|
||||
|
||||
if (ms.getName().equals(MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME)) {
|
||||
methodInvocations.add(MethodIncompleteSignature.constructor(ownerClassName));
|
||||
}
|
||||
else {
|
||||
methodInvocations.add(new MethodIncompleteSignature(ownerClassName, returnType, ms.getName(), ms.getOpcode() == Opcodes.INVOKESTATIC));
|
||||
}
|
||||
extractor.addChain(new MethodIncompleteSignatureChain(methodInvocations));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.intellij.compilerOutputIndex.impl.bigram;
|
||||
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignatureChain;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
class SimpleBigramsExtractor {
|
||||
private final Map<String, MethodIncompleteSignature> myHolder = new HashMap<String, MethodIncompleteSignature>();
|
||||
private final BigramMethodIncompleteSignatureProcessor myProcessor;
|
||||
|
||||
public SimpleBigramsExtractor(final BigramMethodIncompleteSignatureProcessor processor) {
|
||||
myProcessor = processor;
|
||||
}
|
||||
|
||||
public void addChain(final MethodIncompleteSignatureChain chain) {
|
||||
if (chain.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
final MethodIncompleteSignature firstInvocation = chain.getFirstInvocation();
|
||||
assert firstInvocation != null;
|
||||
final MethodIncompleteSignature head = firstInvocation.isStatic() ? null : myHolder.get(firstInvocation.getOwner());
|
||||
for (final Bigram<MethodIncompleteSignature> bigram : toBigrams(head, chain)) {
|
||||
myProcessor.process(bigram);
|
||||
}
|
||||
final MethodIncompleteSignature lastInvocation = chain.getLastInvocation();
|
||||
assert lastInvocation != null;
|
||||
myHolder.put(lastInvocation.getReturnType(), lastInvocation);
|
||||
}
|
||||
|
||||
private static Collection<Bigram<MethodIncompleteSignature>> toBigrams(final @Nullable MethodIncompleteSignature head,
|
||||
final @NotNull MethodIncompleteSignatureChain chain) {
|
||||
MethodIncompleteSignature currentLast = null;
|
||||
if (head != null) {
|
||||
currentLast = head;
|
||||
}
|
||||
final List<Bigram<MethodIncompleteSignature>> bigrams = new ArrayList<Bigram<MethodIncompleteSignature>>(chain.size());
|
||||
for (final MethodIncompleteSignature current : chain.list()) {
|
||||
if (currentLast != null) {
|
||||
bigrams.add(new Bigram<MethodIncompleteSignature>(currentLast, current));
|
||||
}
|
||||
currentLast = current;
|
||||
}
|
||||
return bigrams;
|
||||
}
|
||||
|
||||
public interface BigramMethodIncompleteSignatureProcessor {
|
||||
void process(Bigram<MethodIncompleteSignature> bigram);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.intellij.compilerOutputIndex.impl.callingLocation;
|
||||
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class CallingLocation {
|
||||
@NotNull
|
||||
private final MethodIncompleteSignature myMethodIncompleteSignature;
|
||||
@NotNull
|
||||
private final VariableType myVariableType;
|
||||
|
||||
public CallingLocation(@NotNull final MethodIncompleteSignature methodIncompleteSignature, @NotNull final VariableType variableType) {
|
||||
myMethodIncompleteSignature = methodIncompleteSignature;
|
||||
myVariableType = variableType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MethodIncompleteSignature getMethodIncompleteSignature() {
|
||||
return myMethodIncompleteSignature;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public VariableType getVariableType() {
|
||||
return myVariableType;
|
||||
}
|
||||
|
||||
public static DataExternalizer<CallingLocation> createDataExternalizer() {
|
||||
final KeyDescriptor<MethodIncompleteSignature> methodIncompleteSignatureKeyDescriptor = MethodIncompleteSignature.createKeyDescriptor();
|
||||
return new DataExternalizer<CallingLocation>() {
|
||||
@Override
|
||||
public void save(final DataOutput out, final CallingLocation value) throws IOException {
|
||||
methodIncompleteSignatureKeyDescriptor.save(out, value.getMethodIncompleteSignature());
|
||||
VariableType.KEY_DESCRIPTOR.save(out, value.getVariableType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallingLocation read(final DataInput in) throws IOException {
|
||||
return new CallingLocation(methodIncompleteSignatureKeyDescriptor.read(in), VariableType.KEY_DESCRIPTOR.read(in));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final CallingLocation that = (CallingLocation)o;
|
||||
|
||||
if (!myMethodIncompleteSignature.equals(that.myMethodIncompleteSignature)) return false;
|
||||
if (myVariableType != that.myVariableType) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myMethodIncompleteSignature.hashCode();
|
||||
result = 31 * result + myVariableType.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package com.intellij.compilerOutputIndex.impl.callingLocation;
|
||||
|
||||
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.*;
|
||||
import org.jetbrains.asm4.commons.AnalyzerAdapter;
|
||||
import org.jetbrains.asm4.commons.JSRInlinerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class MethodCallingLocationExtractor {
|
||||
private MethodCallingLocationExtractor() {
|
||||
}
|
||||
|
||||
public static Map<MethodNameAndQualifier, List<CallingLocation>> extract(final ClassReader classReader) {
|
||||
final MyClassVisitor classVisitor = new MyClassVisitor();
|
||||
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES);
|
||||
return classVisitor.getExtractedMethodsCallings();
|
||||
}
|
||||
|
||||
private static class MyClassVisitor extends ClassVisitor {
|
||||
public MyClassVisitor() {
|
||||
super(Opcodes.ASM4);
|
||||
}
|
||||
|
||||
private final Map<MethodNameAndQualifier, List<CallingLocation>> myExtractedMethodsCallings =
|
||||
new HashMap<MethodNameAndQualifier, List<CallingLocation>>();
|
||||
|
||||
private String myClassName;
|
||||
private String myRawClassName;
|
||||
|
||||
private Map<MethodNameAndQualifier, List<CallingLocation>> getExtractedMethodsCallings() {
|
||||
return myExtractedMethodsCallings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(final int version,
|
||||
final int access,
|
||||
final String className,
|
||||
final String signature,
|
||||
final String superName,
|
||||
final String[] interfaces) {
|
||||
myRawClassName = className;
|
||||
myClassName = AsmUtil.getQualifiedClassName(className);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String desc,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
|
||||
if (name.charAt(0) == '<') {
|
||||
return null;
|
||||
}
|
||||
final boolean isStaticMethod = AsmUtil.isStaticMethodDeclaration(access);
|
||||
if (isStaticMethod) {
|
||||
return null;
|
||||
}
|
||||
@SuppressWarnings("UnnecessaryLocalVariable") final String methodName = name;
|
||||
final String[] methodParams = AsmUtil.getParamsTypes(desc);
|
||||
final MethodIncompleteSignature currentMethodSignature =
|
||||
new MethodIncompleteSignature(myClassName, AsmUtil.getReturnType(desc), methodName, isStaticMethod);
|
||||
return new JSRInlinerAdapter(new AnalyzerAdapter(Opcodes.ASM4, myRawClassName, access, name, desc, null) {
|
||||
private final Map<Integer, Variable> myFieldsAndParamsPositionInStack = new HashMap<Integer, Variable>();
|
||||
|
||||
@Override
|
||||
public void visitInsn(final int opcode) {
|
||||
super.visitInsn(opcode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
|
||||
boolean onThis = false;
|
||||
if (stack != null && opcode == Opcodes.GETFIELD && !ChainCompletionStringUtil.isPrimitiveOrArray(AsmUtil.getReturnType(desc))) {
|
||||
final Object objectRef = stack.get(stack.size() - 1);
|
||||
if (objectRef instanceof String && objectRef.equals(myRawClassName)) {
|
||||
onThis = true;
|
||||
}
|
||||
}
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
if (onThis) {
|
||||
final int index = stack.size() - 1;
|
||||
final Object marker = stack.get(index);
|
||||
myFieldsAndParamsPositionInStack.put(index, new Variable(marker, VariableType.FIELD));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitVarInsn(final int opcode, final int varIndex) {
|
||||
super.visitVarInsn(opcode, varIndex);
|
||||
if (stack != null && opcode == Opcodes.ALOAD &&
|
||||
varIndex > 0 &&
|
||||
varIndex <= methodParams.length &&
|
||||
!ChainCompletionStringUtil.isPrimitiveOrArray(methodParams[varIndex - 1])) {
|
||||
final int stackPos = stack.size() - 1;
|
||||
myFieldsAndParamsPositionInStack.put(stackPos, new Variable(stack.get(stackPos), VariableType.METHOD_PARAMETER));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
|
||||
if (stack != null && opcode != Opcodes.INVOKESTATIC && !methodName.startsWith("<")) {
|
||||
final int index = stack.size() - 1 - AsmUtil.getParamsTypes(desc).length;
|
||||
final Object stackValue = stack.get(index);
|
||||
final Variable variable = myFieldsAndParamsPositionInStack.get(index);
|
||||
if (variable != null && variable.getMarker() == stackValue /*equality by reference is not mistake*/) {
|
||||
final CallingLocation callingLocation = new CallingLocation(currentMethodSignature, variable.getVariableType());
|
||||
final MethodNameAndQualifier invokedMethod = new MethodNameAndQualifier(name, AsmUtil.getQualifiedClassName(owner));
|
||||
List<CallingLocation> callingLocations = myExtractedMethodsCallings.get(invokedMethod);
|
||||
if (callingLocations == null) {
|
||||
callingLocations = new ArrayList<CallingLocation>();
|
||||
myExtractedMethodsCallings.put(invokedMethod, callingLocations);
|
||||
}
|
||||
callingLocations.add(callingLocation);
|
||||
}
|
||||
}
|
||||
super.visitMethodInsn(opcode, owner, name, desc);
|
||||
}
|
||||
}, access, name, desc, signature, exceptions);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Variable {
|
||||
private final Object myMarker;
|
||||
private final VariableType myVariableType;
|
||||
|
||||
private Variable(final Object marker, final VariableType variableType) {
|
||||
myMarker = marker;
|
||||
myVariableType = variableType;
|
||||
}
|
||||
|
||||
private Object getMarker() {
|
||||
return myMarker;
|
||||
}
|
||||
|
||||
private VariableType getVariableType() {
|
||||
return myVariableType;
|
||||
}
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package com.intellij.compilerOutputIndex.impl.callingLocation;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.compilerOutputIndex.api.descriptor.ArrayListKeyDescriptor;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.indexing.ValueContainer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodCallingLocationIndex extends CompilerOutputBaseIndex<MethodNameAndQualifier, List<CallingLocation>> {
|
||||
|
||||
public static MethodCallingLocationIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(MethodCallingLocationIndex.class);
|
||||
}
|
||||
|
||||
public MethodCallingLocationIndex() {
|
||||
super(MethodNameAndQualifier.createKeyDescriptor(),
|
||||
new ArrayListKeyDescriptor<CallingLocation>(CallingLocation.createDataExternalizer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<MethodNameAndQualifier, List<CallingLocation>> getIndexId() {
|
||||
return generateIndexId(MethodCallingLocationIndex.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public List<CallingLocation> getAllLocations(final MethodNameAndQualifier methodNameAndQualifier) {
|
||||
try {
|
||||
final List<CallingLocation> result = new ArrayList<CallingLocation>();
|
||||
myIndex.getData(methodNameAndQualifier).forEach(new ValueContainer.ContainerAction<List<CallingLocation>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final List<CallingLocation> values) {
|
||||
result.addAll(values);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Multiset<MethodIncompleteSignature> getLocationsAsParam(final MethodNameAndQualifier methodNameAndQualifier) {
|
||||
final Multiset<MethodIncompleteSignature> result = HashMultiset.create();
|
||||
try {
|
||||
myIndex.getData(methodNameAndQualifier).forEach(new ValueContainer.ContainerAction<List<CallingLocation>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final List<CallingLocation> values) {
|
||||
for (final CallingLocation value : values) {
|
||||
if (value.getVariableType().equals(VariableType.METHOD_PARAMETER)) {
|
||||
result.add(value.getMethodIncompleteSignature());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected DataIndexer<MethodNameAndQualifier, List<CallingLocation>, ClassReader> getIndexer() {
|
||||
return new DataIndexer<MethodNameAndQualifier, List<CallingLocation>, ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<MethodNameAndQualifier, List<CallingLocation>> map(final ClassReader inputData) {
|
||||
return MethodCallingLocationExtractor.extract(inputData);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package com.intellij.compilerOutputIndex.impl.callingLocation;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodNameAndQualifier {
|
||||
@NotNull
|
||||
private final String myMethodName;
|
||||
@NotNull
|
||||
private final String myQualifierName;
|
||||
|
||||
public MethodNameAndQualifier(@NotNull final String methodName, @NotNull final String qualifierName) {
|
||||
myMethodName = methodName;
|
||||
myQualifierName = qualifierName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getMethodName() {
|
||||
return myMethodName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getQualifierName() {
|
||||
return myQualifierName;
|
||||
}
|
||||
|
||||
public static KeyDescriptor<MethodNameAndQualifier> createKeyDescriptor() {
|
||||
final DataExternalizer<String> stringDataExternalizer = new EnumeratorStringDescriptor();
|
||||
return new KeyDescriptor<MethodNameAndQualifier>() {
|
||||
@Override
|
||||
public void save(final DataOutput out, final MethodNameAndQualifier value) throws IOException {
|
||||
stringDataExternalizer.save(out, value.myMethodName);
|
||||
stringDataExternalizer.save(out, value.myQualifierName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodNameAndQualifier read(final DataInput in) throws IOException {
|
||||
return new MethodNameAndQualifier(stringDataExternalizer.read(in), stringDataExternalizer.read(in));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(final MethodNameAndQualifier value) {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(final MethodNameAndQualifier val1, final MethodNameAndQualifier val2) {
|
||||
return val1.equals(val2);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final MethodNameAndQualifier that = (MethodNameAndQualifier)o;
|
||||
|
||||
if (!myMethodName.equals(that.myMethodName)) return false;
|
||||
if (!myQualifierName.equals(that.myQualifierName)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myMethodName.hashCode();
|
||||
result = 31 * result + myQualifierName.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.intellij.compilerOutputIndex.impl.callingLocation;
|
||||
|
||||
import com.intellij.util.io.EnumDataDescriptor;
|
||||
import com.intellij.util.io.KeyDescriptor;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public enum VariableType {
|
||||
FIELD,
|
||||
METHOD_PARAMETER,
|
||||
OTHER;
|
||||
|
||||
public static final KeyDescriptor<VariableType> KEY_DESCRIPTOR = new EnumDataDescriptor<VariableType>(VariableType.class);
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.intellij.compilerOutputIndex.impl.quickInheritance;
|
||||
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.CommonClassNames;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.indexing.ValueContainer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.compilerOutputIndex.api.descriptor.HashSetKeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
import org.jetbrains.asm4.ClassVisitor;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
import org.jetbrains.asm4.Type;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class QuickInheritanceIndex extends CompilerOutputBaseIndex<String, Set<String>> {
|
||||
|
||||
public static QuickInheritanceIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(QuickInheritanceIndex.class);
|
||||
}
|
||||
|
||||
public QuickInheritanceIndex() {
|
||||
super(new EnumeratorStringDescriptor(), new HashSetKeyDescriptor<String>(new EnumeratorStringDescriptor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<String, Set<String>> getIndexId() {
|
||||
return generateIndexId(QuickInheritanceIndex.class);
|
||||
}
|
||||
|
||||
protected Set<String> getSupers(final String classQName) {
|
||||
try {
|
||||
final ValueContainer<Set<String>> valueContainer = myIndex.getData(classQName);
|
||||
final Ref<Set<String>> setRef = Ref.create();
|
||||
valueContainer.forEach(new ValueContainer.ContainerAction<Set<String>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final Set<String> value) {
|
||||
setRef.set(value);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
final Set<String> supers = setRef.get();
|
||||
if (supers == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return supers;
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataIndexer<String, Set<String>, ClassReader> getIndexer() {
|
||||
return new DataIndexer<String, Set<String>, ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Set<String>> map(final ClassReader inputData) {
|
||||
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();
|
||||
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
|
||||
@Override
|
||||
public void visit(final int version,
|
||||
final int access,
|
||||
final String name,
|
||||
final String signature,
|
||||
final String superName,
|
||||
final String[] interfaces) {
|
||||
final String className = Type.getObjectType(name).getClassName();
|
||||
if (className != null) {
|
||||
final HashSet<String> value = ContainerUtil.newHashSet(AsmUtil.getQualifiedClassNames(interfaces, superName));
|
||||
value.remove(CommonClassNames.JAVA_LANG_OBJECT);
|
||||
map.put(className, value);
|
||||
}
|
||||
}
|
||||
}, Opcodes.ASM4);
|
||||
return map;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package com.intellij.compilerOutputIndex.impl.quickInheritance;
|
||||
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.indexing.ValueContainer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.compilerOutputIndex.api.descriptor.HashSetKeyDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.ClassReader;
|
||||
import org.jetbrains.asm4.ClassVisitor;
|
||||
import org.jetbrains.asm4.MethodVisitor;
|
||||
import org.jetbrains.asm4.Opcodes;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class QuickMethodsIndex extends CompilerOutputBaseIndex<String, Set<String>> {
|
||||
|
||||
public static QuickMethodsIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(QuickMethodsIndex.class);
|
||||
}
|
||||
|
||||
public QuickMethodsIndex() {
|
||||
super(new EnumeratorStringDescriptor(), new HashSetKeyDescriptor<String>(new EnumeratorStringDescriptor()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<String, Set<String>> getIndexId() {
|
||||
return generateIndexId(QuickMethodsIndex.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected Set<String> getMethodsNames(final String classQName) {
|
||||
final Ref<Set<String>> methodsRef = Ref.create();
|
||||
try {
|
||||
myIndex.getData(classQName).forEach(new ValueContainer.ContainerAction<Set<String>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final Set<String> value) {
|
||||
methodsRef.set(value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
final Set<String> methods = methodsRef.get();
|
||||
return methods == null ? Collections.<String>emptySet() : methods;
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataIndexer<String, Set<String>, ClassReader> getIndexer() {
|
||||
return new DataIndexer<String, Set<String>, ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Set<String>> map(final ClassReader inputData) {
|
||||
final Map<String, Set<String>> map = new HashMap<String, Set<String>>();
|
||||
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
|
||||
|
||||
private String myClassName;
|
||||
private final HashSet<String> myMethodNames = new HashSet<String>();
|
||||
|
||||
@Override
|
||||
public void visit(final int i, final int i2, final String name, final String s2, final String s3, final String[] strings) {
|
||||
myClassName = AsmUtil.getQualifiedClassName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
map.put(myClassName, myMethodNames);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String desc,
|
||||
final String sign,
|
||||
final String[] exceptions) {
|
||||
if ((access & Opcodes.ACC_STATIC) == 0) {
|
||||
myMethodNames.add(name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, Opcodes.ASM4);
|
||||
return map;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.intellij.compilerOutputIndex.impl.quickInheritance;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public final class QuickOverrideUtil {
|
||||
|
||||
private QuickOverrideUtil() {}
|
||||
|
||||
public static boolean isMethodOverriden(final String classQName, final String methodName,
|
||||
final QuickInheritanceIndex quickInheritanceIndex,
|
||||
final QuickMethodsIndex quickMethodsIndex) {
|
||||
for (final String aSuper : quickInheritanceIndex.getSupers(classQName)) {
|
||||
if (quickMethodsIndex.getMethodsNames(aSuper).contains(methodName)) {
|
||||
return true;
|
||||
}
|
||||
if (isMethodOverriden(aSuper, methodName, quickInheritanceIndex, quickMethodsIndex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.intellij.compilerOutputIndex.impl.singleton;
|
||||
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodShortSignature {
|
||||
@NotNull
|
||||
private final String myName;
|
||||
@NotNull
|
||||
private final String mySignature; //in raw asm type
|
||||
|
||||
public MethodShortSignature(final @NotNull String name, final @NotNull String signature) {
|
||||
myName = name;
|
||||
mySignature = signature;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getSignature() {
|
||||
return mySignature;
|
||||
}
|
||||
|
||||
public static DataExternalizer<MethodShortSignature> createDataExternalizer() {
|
||||
final EnumeratorStringDescriptor stringDescriptor = new EnumeratorStringDescriptor();
|
||||
return new DataExternalizer<MethodShortSignature>() {
|
||||
|
||||
@Override
|
||||
public void save(final DataOutput out, final MethodShortSignature value) throws IOException {
|
||||
stringDescriptor.save(out, value.getName());
|
||||
stringDescriptor.save(out, value.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodShortSignature read(final DataInput in) throws IOException {
|
||||
return new MethodShortSignature(stringDescriptor.read(in), stringDescriptor.read(in));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final MethodShortSignature that = (MethodShortSignature) o;
|
||||
|
||||
if (!myName.equals(that.myName)) return false;
|
||||
if (!mySignature.equals(that.mySignature)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myName.hashCode();
|
||||
result = 31 * result + mySignature.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package com.intellij.compilerOutputIndex.impl.singleton;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class MethodShortSignatureWithWeight {
|
||||
private final MethodShortSignature myMethodShortSignature;
|
||||
private final int myWeight;
|
||||
|
||||
public MethodShortSignatureWithWeight(final MethodShortSignature methodShortSignature, final int weight) {
|
||||
myMethodShortSignature = methodShortSignature;
|
||||
myWeight = weight;
|
||||
}
|
||||
|
||||
public MethodShortSignature getMethodShortSignature() {
|
||||
return myMethodShortSignature;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return myWeight;
|
||||
}
|
||||
|
||||
public static Comparator<MethodShortSignatureWithWeight> COMPARATOR = new Comparator<MethodShortSignatureWithWeight>() {
|
||||
@Override
|
||||
public int compare(final MethodShortSignatureWithWeight o1, final MethodShortSignatureWithWeight o2) {
|
||||
return o1.getWeight() - o2.getWeight();
|
||||
}
|
||||
} ;
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final MethodShortSignatureWithWeight that = (MethodShortSignatureWithWeight) o;
|
||||
|
||||
if (myWeight != that.myWeight) return false;
|
||||
if (!myMethodShortSignature.equals(that.myMethodShortSignature)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myMethodShortSignature.hashCode();
|
||||
result = 31 * result + myWeight;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
package com.intellij.compilerOutputIndex.impl.singleton;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.compilerOutputIndex.impl.GuavaHashMultiSetExternalizer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.indexing.ValueContainer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class ParamsInMethodOccurrencesIndex extends CompilerOutputBaseIndex<String, Multiset<MethodShortSignature>> {
|
||||
|
||||
public static ParamsInMethodOccurrencesIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(ParamsInMethodOccurrencesIndex.class);
|
||||
}
|
||||
|
||||
public ParamsInMethodOccurrencesIndex() {
|
||||
super(new EnumeratorStringDescriptor(), new GuavaHashMultiSetExternalizer<MethodShortSignature>(MethodShortSignature.createDataExternalizer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<String, Multiset<MethodShortSignature>> getIndexId() {
|
||||
return generateIndexId(ParamsInMethodOccurrencesIndex.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Pair<List<MethodShortSignatureWithWeight>, Integer> getParameterOccurrences(final String parameterTypeName) {
|
||||
try {
|
||||
final Multiset<MethodShortSignature> resultAsMultiset = HashMultiset.create();
|
||||
final ValueContainer<Multiset<MethodShortSignature>> valueContainer = myIndex.getData(parameterTypeName);
|
||||
valueContainer.forEach(new ValueContainer.ContainerAction<Multiset<MethodShortSignature>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final Multiset<MethodShortSignature> localMap) {
|
||||
for (final Multiset.Entry<MethodShortSignature> e : localMap.entrySet()) {
|
||||
resultAsMultiset.add(e.getElement(), e.getCount());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final List<MethodShortSignatureWithWeight> result = new ArrayList<MethodShortSignatureWithWeight>(resultAsMultiset.elementSet().size());
|
||||
int sumWeight = 0;
|
||||
for (final Multiset.Entry<MethodShortSignature> e : resultAsMultiset.entrySet()) {
|
||||
sumWeight += e.getCount();
|
||||
result.add(new MethodShortSignatureWithWeight(e.getElement(), e.getCount()));
|
||||
}
|
||||
Collections.sort(result, MethodShortSignatureWithWeight.COMPARATOR);
|
||||
|
||||
return Pair.create(result, sumWeight);
|
||||
} catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataIndexer<String, Multiset<MethodShortSignature>, ClassReader> getIndexer() {
|
||||
return new DataIndexer<String, Multiset<MethodShortSignature>, ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Multiset<MethodShortSignature>> map(final ClassReader inputData) {
|
||||
final Map<String, Multiset<MethodShortSignature>> result = new HashMap<String, Multiset<MethodShortSignature>>();
|
||||
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
|
||||
@Nullable
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int i, final String name, final String desc, final String signature, final String[] exception) {
|
||||
if (CompilerOutputIndexUtil.isSetterOrConstructorMethodName(name)) {
|
||||
return null;
|
||||
}
|
||||
final String[] parameters = AsmUtil.getParamsTypes(desc);
|
||||
final MethodShortSignature thisMethodShortSignature = new MethodShortSignature(name, desc);
|
||||
for (final String parameter : parameters) {
|
||||
Multiset<MethodShortSignature> methods = result.get(parameter);
|
||||
if (methods == null) {
|
||||
methods = HashMultiset.create();
|
||||
result.put(parameter, methods);
|
||||
}
|
||||
methods.add(thisMethodShortSignature);
|
||||
}
|
||||
return new MethodVisitor(Opcodes.ASM4) {
|
||||
@Override
|
||||
public void visitLocalVariable(final String s, final String desc, final String signature, final Label label, final Label label2, final int i) {
|
||||
final String varType = AsmUtil.getQualifiedClassName(desc);
|
||||
Multiset<MethodShortSignature> methods = result.get(varType);
|
||||
if (methods == null) {
|
||||
methods = HashMultiset.create();
|
||||
result.put(varType, methods);
|
||||
}
|
||||
methods.add(thisMethodShortSignature);
|
||||
}
|
||||
};
|
||||
}
|
||||
}, Opcodes.ASM4);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
package com.intellij.compilerOutputIndex.impl.singleton;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
|
||||
import com.intellij.compilerOutputIndex.api.descriptor.ArrayListKeyDescriptor;
|
||||
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexUtil;
|
||||
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.util.indexing.DataIndexer;
|
||||
import com.intellij.util.indexing.ID;
|
||||
import com.intellij.util.indexing.StorageException;
|
||||
import com.intellij.util.indexing.ValueContainer;
|
||||
import com.intellij.util.io.EnumeratorIntegerDescriptor;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.asm4.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class TwinVariablesIndex extends CompilerOutputBaseIndex<String, List<Integer>> {
|
||||
public static TwinVariablesIndex getInstance(final Project project) {
|
||||
return CompilerOutputIndexer.getInstance(project).getIndex(TwinVariablesIndex.class);
|
||||
}
|
||||
|
||||
public TwinVariablesIndex() {
|
||||
super(new EnumeratorStringDescriptor(), new ArrayListKeyDescriptor<Integer>(EnumeratorIntegerDescriptor.INSTANCE));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ID<String, List<Integer>> getIndexId() {
|
||||
return generateIndexId(TwinVariablesIndex.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Integer> getTwinInfo(final String typeQName) {
|
||||
try {
|
||||
final ValueContainer<List<Integer>> valueContainer = myIndex.getData(typeQName);
|
||||
final List<Integer> result = new ArrayList<Integer>(valueContainer.size());
|
||||
valueContainer.forEach(new ValueContainer.ContainerAction<List<Integer>>() {
|
||||
@Override
|
||||
public boolean perform(final int id, final List<Integer> value) {
|
||||
result.addAll(value);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
catch (StorageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DataIndexer<String, List<Integer>, ClassReader> getIndexer() {
|
||||
return new DataIndexer<String, List<Integer>, ClassReader>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, List<Integer>> map(final ClassReader inputData) {
|
||||
final Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
|
||||
inputData.accept(new ClassVisitor(Opcodes.ASM4) {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access,
|
||||
final String name,
|
||||
final String desc,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
if (CompilerOutputIndexUtil.isSetterOrConstructorMethodName(name)) {
|
||||
return null;
|
||||
}
|
||||
final Multiset<String> myTypesOccurrences = HashMultiset.create();
|
||||
final String[] paramsTypes = AsmUtil.getParamsTypes(desc);
|
||||
Collections.addAll(myTypesOccurrences, paramsTypes);
|
||||
return new MethodVisitor(Opcodes.ASM4) {
|
||||
private final Set<String> myLocalVarNames = new HashSet<String>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
for (final Multiset.Entry<String> e: myTypesOccurrences.entrySet()) {
|
||||
final String key = e.getElement();
|
||||
if (!ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(key)) {
|
||||
List<Integer> values = map.get(key);
|
||||
if (values == null) {
|
||||
values = new ArrayList<Integer>();
|
||||
map.put(key, values);
|
||||
}
|
||||
values.add(e.getCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Set<String> myUsedReadFieldsIndex = new HashSet<String>();
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
|
||||
final String fieldTypeQName = AsmUtil.getReturnType(desc);
|
||||
if ((opcode == Opcodes.GETSTATIC || opcode == Opcodes.GETFIELD)) {
|
||||
if (myUsedReadFieldsIndex.add(owner + name)) {
|
||||
myTypesOccurrences.add(fieldTypeQName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(final String name,
|
||||
final String desc,
|
||||
final String signature,
|
||||
final Label start,
|
||||
final Label end,
|
||||
final int index) {
|
||||
if (index > paramsTypes.length && myLocalVarNames.add(name)) {
|
||||
final String type = AsmUtil.getReturnType(desc);
|
||||
myTypesOccurrences.add(type);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}, Opcodes.ASM4);
|
||||
return map;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class PsiElement {
|
||||
PsiFile getContainingFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PsiFile {
|
||||
VirtualFile getVirtualFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualFile {}
|
||||
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
PsiElement e;
|
||||
|
||||
public void method() {
|
||||
VirtualFile vf = <caret>
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiElement e) {
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
e.getContainingFile().getVirtualFile();
|
||||
}
|
||||
}
|
||||
|
||||
class PsiElement {
|
||||
PsiFile getContainingFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PsiFile {
|
||||
VirtualFile getVirtualFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualFile {}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
|
||||
|
||||
interface PsiFile extends PsiElement {
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiElement findElementAt(int position);
|
||||
}
|
||||
|
||||
class PsiManager {
|
||||
static PsiManager getInstance(Project p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiFile findFile(VirtualFile vf) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface VirtualFile {}
|
||||
|
||||
interface Project {}
|
||||
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method() {
|
||||
PsiElement e = <caret>
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
PsiElement e;
|
||||
|
||||
public void statMethod() {
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
e = PsiManager.getInstance(null).findFile(null).findElementAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
interface PsiFile extends PsiElement {
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiElement findElementAt(int position);
|
||||
}
|
||||
|
||||
class PsiManager {
|
||||
static PsiManager getInstance(Project p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiFile findFile(VirtualFile vf) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface VirtualFile {}
|
||||
|
||||
interface Project {}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
class Project {
|
||||
Object o;
|
||||
|
||||
void setObject(Object o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
<T> Object getObject(Class<T> tClass) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
class Manager {
|
||||
|
||||
Project p1;
|
||||
|
||||
Manager(Project p1) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
|
||||
static Manager getManager(Project project) {
|
||||
return (Manager) project.getObject(Manager.class);
|
||||
}
|
||||
|
||||
Project getProject() {
|
||||
return p1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method() {
|
||||
Manager m = <caret>;
|
||||
Project p = m.getProject();
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
public class TestIndex {
|
||||
|
||||
Manager manager = new Manager(new Project());
|
||||
|
||||
public void statMethod() {
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
}
|
||||
}
|
||||
|
||||
class TestIndex2 {
|
||||
|
||||
Project p2 = new Project();
|
||||
|
||||
public void statMethod() {
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Project {
|
||||
Object o;
|
||||
|
||||
void setObject(Object o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
<T> Object getObject(Class<T> tClass) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
class Manager {
|
||||
|
||||
Project p1;
|
||||
|
||||
Manager(Project p1) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
|
||||
static Manager getManager(Project project) {
|
||||
return (Manager) project.getObject(Manager.class);
|
||||
}
|
||||
|
||||
Project getProject() {
|
||||
return p1;
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
class Project {
|
||||
Object o;
|
||||
|
||||
void setObject(Object o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
<T> Object getObject(Class<T> tClass) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
class Manager {
|
||||
|
||||
Project p1;
|
||||
|
||||
Manager(Project p1) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
|
||||
static Manager getManager(Project project) {
|
||||
return (Manager) project.getObject(Manager.class);
|
||||
}
|
||||
|
||||
Project getProject() {
|
||||
return p1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method() {
|
||||
Manager m = <caret>;
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
public class TestIndex {
|
||||
|
||||
Manager manager = new Manager(new Project());
|
||||
|
||||
public void statMethod() {
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
manager.getProject();
|
||||
}
|
||||
}
|
||||
|
||||
class TestIndex2 {
|
||||
|
||||
Project p2 = new Project();
|
||||
|
||||
public void statMethod() {
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
Manager.getManager(p2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Project {
|
||||
Object o;
|
||||
|
||||
void setObject(Object o) {
|
||||
this.o = o;
|
||||
}
|
||||
|
||||
<T> Object getObject(Class<T> tClass) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
class Manager {
|
||||
|
||||
Project p1;
|
||||
|
||||
Manager(Project p1) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
|
||||
static Manager getManager(Project project) {
|
||||
return (Manager) project.getObject(Manager.class);
|
||||
}
|
||||
|
||||
Project getProject() {
|
||||
return p1;
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
class PsiElement {
|
||||
public PsiElement getPrevSibling() {
|
||||
return null;
|
||||
}
|
||||
public PsiElement getParent() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
public void method(PsiElement e) {
|
||||
PsiElement anotherElement = <caret>
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiElement e) {
|
||||
e.getParent();
|
||||
e.getParent();
|
||||
e.getParent();
|
||||
e.getParent();
|
||||
e.getPrevSibling();
|
||||
e.getPrevSibling();
|
||||
e.getPrevSibling();
|
||||
e.getPrevSibling();
|
||||
}
|
||||
}
|
||||
|
||||
class PsiElement {
|
||||
public PsiElement getPrevSibling() {
|
||||
return null;
|
||||
}
|
||||
public PsiElement getParent() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
interface Project {}
|
||||
|
||||
interface PsiElement {
|
||||
Project getProject();
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
private PsiElement getMyElement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void method() {
|
||||
Project p = <caret>
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiElement e) {
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
}
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
|
||||
interface PsiElement {
|
||||
Project getProject();
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
|
||||
class JarFile {
|
||||
class JarEntry {
|
||||
}
|
||||
|
||||
JarEntry getEntry() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method(JarFile j) {
|
||||
JarFile.JarEntry e = <caret>
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(JarFile f) {
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
f.getEntry();
|
||||
}
|
||||
}
|
||||
|
||||
class JarFile {
|
||||
class JarEntry {
|
||||
}
|
||||
|
||||
JarEntry getEntry() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import java.jang.String;
|
||||
|
||||
|
||||
interface PsiElement {
|
||||
PsiClass getContainingClass();
|
||||
}
|
||||
|
||||
interface PsiClass extends PsiElement {}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method(PsiElement e) {
|
||||
int j = 1 + method123(<caret>);
|
||||
}
|
||||
|
||||
public int method123(PsiClass c) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiElement e) {
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
e.getContainingClass();
|
||||
}
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiClass getContainingClass();
|
||||
}
|
||||
|
||||
interface PsiClass extends PsiElement {}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
|
||||
class PsiMethod implements PsiElement {
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
}
|
||||
|
||||
class PsiClass {
|
||||
public PsiMethod findMethodByName(String methodName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
PsiClass c;
|
||||
|
||||
public void method() {
|
||||
PsiElement element = <caret>
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiClass c) {
|
||||
c.findMethodByName("asd");
|
||||
c.findMethodByName("asd");
|
||||
c.findMethodByName("asd");
|
||||
c.findMethodByName("asd");
|
||||
}
|
||||
}
|
||||
|
||||
class PsiMethod implements PsiElement {
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
}
|
||||
|
||||
class PsiClass {
|
||||
public PsiMethod findMethodByName(String methodName) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
|
||||
class ElementFactory {
|
||||
PsiClassType createType(PsiClass c, Object obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClassType createType(PsiClass c) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PsiClassType {}
|
||||
|
||||
class PsiClass {}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method(ElementFactory f) {
|
||||
PsiClassType t = <caret>
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
import java.lang.Object;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(ElementFactory ef) {
|
||||
ef.createType(null);
|
||||
ef.createType(null);
|
||||
ef.createType(null);
|
||||
ef.createType(null);
|
||||
ef.createType(null, null);
|
||||
ef.createType(null, null);
|
||||
ef.createType(null, null);
|
||||
ef.createType(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
class ElementFactory {
|
||||
PsiClassType createType(PsiClass c, Object obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClassType createType(PsiClass c) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class PsiClassType {}
|
||||
|
||||
class PsiClass {}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import java.jang.String;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
class PsiManager {
|
||||
Project getProject() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
class Project {}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method() {
|
||||
Project p = <caret>
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
|
||||
//
|
||||
// if component obtained in implicit way
|
||||
//
|
||||
|
||||
public class TestIndex {
|
||||
|
||||
|
||||
//
|
||||
// simple test for spring like configuration
|
||||
//
|
||||
|
||||
private PsiManager a;
|
||||
|
||||
public void setA(PsiManager a) {
|
||||
this.a = a;
|
||||
}
|
||||
|
||||
void m() {
|
||||
a.getProject();
|
||||
a.getProject();
|
||||
}
|
||||
|
||||
void b() {
|
||||
a.getProject();
|
||||
}
|
||||
|
||||
void c() {
|
||||
a.getProject();
|
||||
}
|
||||
|
||||
void c22() {
|
||||
a.getProject();
|
||||
}
|
||||
|
||||
void c11() {
|
||||
a.getProject();
|
||||
}
|
||||
|
||||
void c1() {
|
||||
a.getProject();
|
||||
}
|
||||
}
|
||||
|
||||
class PsiManager {
|
||||
Project getProject() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
class Project {}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
class PsiManager {
|
||||
Project getProject() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface Project {
|
||||
VirtualFile getBaseDir();
|
||||
}
|
||||
|
||||
class VirtualFile {
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
PsiManager m;
|
||||
|
||||
public void method(Project p) {
|
||||
VirtualFile projectBaseDir = <caret>
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(PsiManager m) {
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
m.getProject().getBaseDir();
|
||||
}
|
||||
}
|
||||
|
||||
class PsiManager {
|
||||
Project getProject() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface Project {
|
||||
VirtualFile getBaseDir();
|
||||
}
|
||||
|
||||
class VirtualFile {
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
|
||||
class Element {
|
||||
public Project getProject() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Project {
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
public void method(Element e) {
|
||||
Project project = <caret>
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod(Element e) {
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
e.getProject();
|
||||
}
|
||||
}
|
||||
|
||||
class Element {
|
||||
public Project getProject() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Project {
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import java.jang.String;
|
||||
|
||||
class B {
|
||||
C getC() {
|
||||
return null;
|
||||
}
|
||||
|
||||
static B b = new B();
|
||||
|
||||
static B getB1() {
|
||||
return b;
|
||||
}
|
||||
static B getB2() {
|
||||
return b;
|
||||
}
|
||||
static B getB3() {
|
||||
return b;
|
||||
}
|
||||
static B getB4() {
|
||||
return b;
|
||||
}
|
||||
static B getB5() {
|
||||
return b;
|
||||
}
|
||||
static B getB6() {
|
||||
return b;
|
||||
}
|
||||
static B getB7() {
|
||||
return b;
|
||||
}
|
||||
static B getB8() {
|
||||
return b;
|
||||
}
|
||||
static B getB9() {
|
||||
return b;
|
||||
}
|
||||
static B getB10() {
|
||||
return b;
|
||||
}
|
||||
static B getB11() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
class C {}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method() {
|
||||
C c = <caret>
|
||||
}
|
||||
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
public void statMethod1() {
|
||||
B.getB1().getC();
|
||||
B.getB1().getC();
|
||||
B.getB1().getC();
|
||||
}
|
||||
public void statMethod2() {
|
||||
B.getB2().getC();
|
||||
B.getB2().getC();
|
||||
B.getB2().getC();
|
||||
}
|
||||
public void statMethod3() {
|
||||
B.getB3().getC();
|
||||
B.getB3().getC();
|
||||
B.getB3().getC();
|
||||
}
|
||||
public void statMethod4() {
|
||||
B.getB4().getC();
|
||||
B.getB4().getC();
|
||||
B.getB4().getC();
|
||||
}
|
||||
public void statMethod5() {
|
||||
B.getB5().getC();
|
||||
B.getB5().getC();
|
||||
B.getB5().getC();
|
||||
}
|
||||
public void statMethod6() {
|
||||
B.getB6().getC();
|
||||
B.getB6().getC();
|
||||
B.getB6().getC();
|
||||
}
|
||||
public void statMethod7() {
|
||||
B.getB7().getC();
|
||||
B.getB7().getC();
|
||||
B.getB7().getC();
|
||||
}
|
||||
public void statMethod8() {
|
||||
B.getB8().getC();
|
||||
B.getB8().getC();
|
||||
B.getB8().getC();
|
||||
}
|
||||
public void statMethod9() {
|
||||
B.getB9().getC();
|
||||
B.getB9().getC();
|
||||
B.getB9().getC();
|
||||
}
|
||||
public void statMethod10() {
|
||||
B.getB10().getC();
|
||||
B.getB10().getC();
|
||||
B.getB10().getC();
|
||||
}
|
||||
public void statMethod11() {
|
||||
B.getB11().getC();
|
||||
B.getB11().getC();
|
||||
B.getB11().getC();
|
||||
}
|
||||
}
|
||||
class B {
|
||||
C getC() {
|
||||
return null;
|
||||
}
|
||||
|
||||
static B b = new B();
|
||||
|
||||
static B getB1() {
|
||||
return b;
|
||||
}
|
||||
static B getB2() {
|
||||
return b;
|
||||
}
|
||||
static B getB3() {
|
||||
return b;
|
||||
}
|
||||
static B getB4() {
|
||||
return b;
|
||||
}
|
||||
static B getB5() {
|
||||
return b;
|
||||
}
|
||||
static B getB6() {
|
||||
return b;
|
||||
}
|
||||
static B getB7() {
|
||||
return b;
|
||||
}
|
||||
static B getB8() {
|
||||
return b;
|
||||
}
|
||||
static B getB9() {
|
||||
return b;
|
||||
}
|
||||
static B getB10() {
|
||||
return b;
|
||||
}
|
||||
static B getB11() {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
class C {}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import java.jang.String;
|
||||
|
||||
interface PsiManager {
|
||||
Project getProject();
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
|
||||
public class TestCompletion {
|
||||
void m() {
|
||||
PsiManager psiManager = null;
|
||||
Project p = psiManager.getProject()
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
import java.jang.String;
|
||||
|
||||
interface PsiManager {
|
||||
Project getProject();
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
|
||||
public class TestCompletion {
|
||||
void m() {
|
||||
Project p = <caret>
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
import java.lang.String;
|
||||
|
||||
public class TestIndex {
|
||||
|
||||
PsiManager m;
|
||||
|
||||
void m() {
|
||||
m.getProject();
|
||||
m.getProject();
|
||||
m.getProject();
|
||||
m.getProject();
|
||||
m.getProject();
|
||||
m.getProject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface PsiManager {
|
||||
Project getProject();
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import java.jang.String;
|
||||
|
||||
class JavaPsiFacade {
|
||||
static JavaPsiFacade getInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClass findClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiClass getContainingClass();
|
||||
}
|
||||
|
||||
interface PsiClass extends PsiElement {
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method(PsiElement e) {
|
||||
PsiClass c = JavaPsiFacade.getInstance().findClass()
|
||||
}
|
||||
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import java.jang.String;
|
||||
|
||||
class JavaPsiFacade {
|
||||
static JavaPsiFacade getInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClass findClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiClass getContainingClass();
|
||||
}
|
||||
|
||||
interface PsiClass extends PsiElement {
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method(PsiElement e) {
|
||||
PsiClass c = <caret>
|
||||
}
|
||||
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
|
||||
*/
|
||||
public class TestIndex {
|
||||
|
||||
void m() {
|
||||
JavaPsiFacade.getInstance().findClass();
|
||||
JavaPsiFacade.getInstance().findClass();
|
||||
JavaPsiFacade.getInstance().findClass();
|
||||
JavaPsiFacade.getInstance().findClass();
|
||||
JavaPsiFacade.getInstance().findClass();
|
||||
JavaPsiFacade.getInstance().findClass();
|
||||
}
|
||||
}
|
||||
|
||||
class JavaPsiFacade {
|
||||
static JavaPsiFacade getInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClass findClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiClass getContainingClass();
|
||||
}
|
||||
|
||||
interface PsiClass extends PsiElement {
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
import java.jang.String;
|
||||
import java.lang.String;
|
||||
|
||||
interface PsiManager {
|
||||
Project getProject(String asd, String zxc);
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
|
||||
public class TestCompletion {
|
||||
void m() {
|
||||
String asd = "123";
|
||||
PsiManager psiManager = null;
|
||||
Project p = psiManager.getProject(asd, zxc)
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import java.jang.String;
|
||||
import java.lang.String;
|
||||
|
||||
interface PsiManager {
|
||||
Project getProject(String asd, String zxc);
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
|
||||
public class TestCompletion {
|
||||
void m() {
|
||||
String asd = "123";
|
||||
Project p = <caret>
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import java.lang.String;
|
||||
|
||||
public class TestIndex {
|
||||
|
||||
PsiManager m;
|
||||
|
||||
void m() {
|
||||
m.getProject(null, null);
|
||||
m.getProject(null, null);
|
||||
m.getProject(null, null);
|
||||
m.getProject(null, null);
|
||||
m.getProject(null, null);
|
||||
m.getProject(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface PsiManager {
|
||||
Project getProject(String asd, String zxc);
|
||||
}
|
||||
|
||||
interface Project {}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
import java.jang.String;
|
||||
|
||||
class JavaPsiFacade {
|
||||
static JavaPsiFacade getInstance() {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClass findClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
interface PsiElement {
|
||||
PsiClass getContainingClass();
|
||||
}
|
||||
|
||||
interface PsiClass extends PsiElement {
|
||||
}
|
||||
|
||||
public class TestCompletion {
|
||||
|
||||
public void method(PsiElement e) {
|
||||
PsiClass c = <caret>
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user