diff options
Diffstat (limited to 'java/java-impl/src/com/intellij/codeInsight/completion/methodChains')
15 files changed, 721 insertions, 557 deletions
diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/MethodsChainsCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/MethodsChainsCompletionContributor.java index 27c63a0c2a50..8781e46630bf 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/MethodsChainsCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/MethodsChainsCompletionContributor.java @@ -9,7 +9,7 @@ import com.intellij.codeInsight.completion.methodChains.search.MethodChainsSearc 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.api.indexer.CompilerOutputIndexFeature; import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.text.StringUtil; @@ -35,13 +35,13 @@ import static com.intellij.patterns.PsiJavaPatterns.or; 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_SEARCH_RESULT_SIZE = 5; 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()) { + if (parameters.getInvocationCount() >= INVOCATIONS_THRESHOLD && CompilerOutputIndexFeature.METHOD_CHAINS_COMPLETION.isEnabled()) { super.fillCompletionVariants(parameters, result); if (ApplicationManager.getApplication().isUnitTestMode()) { result.stopHere(); @@ -73,16 +73,19 @@ public class MethodsChainsCompletionContributor extends CompletionContributor { } contextRelevantTypes.remove(targetClassQName); - final List<LookupElement> foundedElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext); - result.addAllElements(foundedElements); + //final boolean useBigrams = ApplicationManager.getApplication().isUnitTestMode() || parameters.getInvocationCount() == 3; + final boolean useBigrams = true; + final List<LookupElement> foundElements = searchForLookups(targetClassQName, contextRelevantTypes, completionContext, useBigrams); + result.addAllElements(foundElements); } }); } private static List<LookupElement> searchForLookups(final String targetClassQName, final Set<String> contextRelevantTypes, - final ChainCompletionContext completionContext) { - final MethodChainsSearchService searchService = new MethodChainsSearchService(completionContext.getProject()); + final ChainCompletionContext completionContext, + final boolean useBigrams) { + final MethodChainsSearchService searchService = new MethodChainsSearchService(completionContext.getProject(), useBigrams); final List<MethodsChain> searchResult = searchChains(targetClassQName, contextRelevantTypes, MAX_SEARCH_RESULT_SIZE, MAX_CHAIN_SIZE, completionContext, searchService); if (searchResult.size() < MAX_SEARCH_RESULT_SIZE) { @@ -100,7 +103,8 @@ public class MethodsChainsCompletionContributor extends CompletionContributor { completionContext, searchService)) { boolean insert = true; for (final MethodsChain baseChain : searchResult) { - if (baseChain.weakContains(chain)) { + final MethodsChain.CompareResult r = MethodsChain.compare(baseChain, chain, completionContext); + if (r != MethodsChain.CompareResult.NOT_EQUAL) { insert = false; break; } @@ -116,8 +120,19 @@ public class MethodsChainsCompletionContributor extends CompletionContributor { }); } } - return MethodsChainLookupRangingHelper.chainsToWeightableLookupElements(filterTailAndGetSumLastMethodOccurrence(searchResult), - completionContext); + final List<MethodsChain> chains = searchResult.size() > MAX_CHAIN_SIZE ? chooseHead(searchResult) : searchResult; + return MethodsChainLookupRangingHelper + .chainsToWeightableLookupElements(filterTailAndGetSumLastMethodOccurrence(chains), completionContext); + } + + private static List<MethodsChain> chooseHead(final List<MethodsChain> elements) { + Collections.sort(elements, new Comparator<MethodsChain>() { + @Override + public int compare(final MethodsChain o1, final MethodsChain o2) { + return o2.getChainWeight() - o1.getChainWeight(); + } + }); + return elements.subList(0, MAX_CHAIN_SIZE); } @SuppressWarnings("unchecked") @@ -188,8 +203,7 @@ public class MethodsChainsCompletionContributor extends CompletionContributor { final MethodChainsSearchService searchService) { return ChainsSearcher.search(searchService, targetQName, contextVarsQNames, maxResultSize, maxChainSize, createNotDeprecatedMethodsResolver(JavaPsiFacade.getInstance(context.getProject()), - context.getResolveScope()), - context.getExcludedQNames(), context.getContextMethodName()); + context.getResolveScope()), context.getExcludedQNames(), context); } private static FactoryMap<MethodIncompleteSignature, PsiMethod[]> createNotDeprecatedMethodsResolver(final JavaPsiFacade javaPsiFacade, diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/ChainCompletionNewVariableLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/ChainCompletionNewVariableLookupElement.java index 53dc9bc9eed5..b775640edbdb 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/ChainCompletionNewVariableLookupElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/ChainCompletionNewVariableLookupElement.java @@ -1,10 +1,15 @@ package com.intellij.codeInsight.completion.methodChains.completion.lookup; +import com.intellij.codeInsight.completion.CompletionInitializationContext; import com.intellij.codeInsight.completion.InsertionContext; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementDecorator; import com.intellij.codeInsight.lookup.LookupElementPresentation; -import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; @@ -12,59 +17,92 @@ import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; +import java.util.Collection; + /** * @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com> */ -public class ChainCompletionNewVariableLookupElement extends LookupItem<PsiClass> { +public class ChainCompletionNewVariableLookupElement extends LookupElementDecorator<LookupElement> { + private final static Logger log = Logger.getInstance(ChainCompletionNewVariableLookupElement.class); - private final PsiClass psiClass; - private final String newVarName; + private final PsiClass myPsiClass; + private final String myNewVarName; - public ChainCompletionNewVariableLookupElement(final PsiClass psiClass, final String newVarName) { - super(psiClass, newVarName); - this.newVarName = newVarName; - this.psiClass = psiClass; + public ChainCompletionNewVariableLookupElement(final PsiClass psiClass, final String newVarName, final LookupElement calledMethods) { + super(calledMethods); + myNewVarName = newVarName; + myPsiClass = psiClass; } - public static ChainCompletionNewVariableLookupElement create(final PsiClass psiClass) { + public static ChainCompletionNewVariableLookupElement create(final PsiClass psiClass, final LookupElement calledMethods) { 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)); + final String newVarName = chooseLongestName(JavaCodeStyleManager.getInstance(project). + suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, JavaPsiFacade.getElementFactory(project).createType(psiClass))); + return new ChainCompletionNewVariableLookupElement(psiClass, newVarName, calledMethods); } @Override public void handleInsert(final InsertionContext context) { + final RangeMarker rangeMarker = context.getDocument().createRangeMarker(context.getStartOffset(), context.getStartOffset()); + getDelegate().handleInsert(context); final PsiFile file = context.getFile(); - ((PsiJavaFile) file).importClass(psiClass); - final PsiStatement statement = PsiTreeUtil.getParentOfType(file.findElementAt(context.getEditor().getCaretModel().getOffset()), PsiStatement.class); + ((PsiJavaFile)file).importClass(myPsiClass); + final PsiElement caretElement = file.findElementAt(context.getEditor().getCaretModel().getOffset()); + if (caretElement == null) { + log.error("element on caret position MUST BE not null"); + return; + } + final PsiStatement statement = (PsiStatement) caretElement.getPrevSibling(); final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(statement, PsiCodeBlock.class); - assert codeBlock != null; + if (codeBlock == null) { + log.error("code block MUST BE not null"); + return; + } final Project project = context.getProject(); + final Ref<PsiElement> insertedStatementRef = Ref.create(); + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + context.commitDocument(); 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); + final PsiStatement statementFromText = elementFactory.createStatementFromText(String.format("%s %s = null;", myPsiClass.getName(), myNewVarName), null); + insertedStatementRef.set(codeBlock.addBefore(statementFromText, statement)); } }.execute(); + final PsiLiteralExpression nullKeyword = findNull(insertedStatementRef.get()); PsiDocumentManager.getInstance(context.getProject()).doPostponedOperationsAndUnblockDocument(context.getDocument()); + context.getDocument().insertString(rangeMarker.getStartOffset(), myNewVarName + "."); + context.commitDocument(); + final int offset = nullKeyword.getTextOffset(); + final int endOffset = offset + nullKeyword.getTextLength(); + context.getEditor().getSelectionModel().setSelection(offset, endOffset); + context.getEditor().getCaretModel().moveToOffset(offset); } @NotNull @Override public String getLookupString() { - return newVarName; + return getDelegate().getLookupString(); } @Override public void renderElement(final LookupElementPresentation presentation) { super.renderElement(presentation); - presentation.setItemText(newVarName); + presentation.setItemText(myNewVarName + "." + presentation.getItemText()); + } + + private static PsiLiteralExpression findNull(final PsiElement psiElement) { + final Collection<PsiLiteralExpression> literalExpressions = PsiTreeUtil.findChildrenOfType(psiElement, PsiLiteralExpression.class); + for (final PsiLiteralExpression literalExpression : literalExpressions) { + if (PsiKeyword.NULL.equals(literalExpression.getText())) { + return literalExpression; + } + } + throw new IllegalArgumentException(); } - private static String chooseLongest(final String[] names) { + private static String chooseLongestName(final SuggestedNameInfo suggestedNameInfo) { + final String[] names = suggestedNameInfo.names; String longestWord = names[0]; int maxLength = longestWord.length(); for (int i = 1; i < names.length; i++) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/WeightableChainLookupElement.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/WeightableChainLookupElement.java index e58d2c84e7df..16ee64688c41 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/WeightableChainLookupElement.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/completion/lookup/WeightableChainLookupElement.java @@ -3,6 +3,7 @@ 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 com.intellij.codeInsight.lookup.LookupElementPresentation; import org.jetbrains.annotations.NotNull; /** diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/CachedNotDeprecatedMethodsResolver.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/CachedNotDeprecatedMethodsResolver.java deleted file mode 100644 index 018065a7ed56..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/CachedNotDeprecatedMethodsResolver.java +++ /dev/null @@ -1,36 +0,0 @@ -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; - } -} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainRelevance.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainRelevance.java index 09e008a7aa4b..0aa7231ead1d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainRelevance.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainRelevance.java @@ -7,7 +7,7 @@ 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); + public static final ChainRelevance LOWEST = new ChainRelevance(Integer.MAX_VALUE, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, false, false, 0); private final int myChainSize; private final int myLastMethodOccurrences; @@ -15,19 +15,22 @@ public class ChainRelevance implements Comparable<ChainRelevance> { private final int myNotMatchedStringVars; private final boolean myHasCallingVariableInContext; private final boolean myFirstMethodStatic; + private final int myParametersInContext; public ChainRelevance(final int chainSize, final int lastMethodOccurrences, final int unreachableParametersCount, final int notMatchedStringVars, final boolean hasCallingVariableInContext, - final boolean firstMethodStatic) { + final boolean firstMethodStatic, + final int parametersInContext) { myChainSize = chainSize; myLastMethodOccurrences = lastMethodOccurrences; myUnreachableParametersCount = unreachableParametersCount; myNotMatchedStringVars = notMatchedStringVars; myHasCallingVariableInContext = hasCallingVariableInContext; myFirstMethodStatic = firstMethodStatic; + myParametersInContext = parametersInContext; } @TestOnly @@ -62,16 +65,22 @@ public class ChainRelevance implements Comparable<ChainRelevance> { @Override public int compareTo(@NotNull final ChainRelevance that) { + if (myHasCallingVariableInContext && !that.myHasCallingVariableInContext) { + return 1; + } + if (that.myHasCallingVariableInContext && !myHasCallingVariableInContext) { + return -1; + } if (myFirstMethodStatic && !that.myFirstMethodStatic) { return -1; } if (that.myFirstMethodStatic && !myFirstMethodStatic) { return 1; } - if (myHasCallingVariableInContext && !that.myHasCallingVariableInContext) { + if (myParametersInContext > that.myParametersInContext) { return 1; } - if (that.myHasCallingVariableInContext && !myHasCallingVariableInContext) { + if (myParametersInContext <= that.myParametersInContext) { return -1; } int sub = myLastMethodOccurrences - that.myLastMethodOccurrences; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainsSearcher.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainsSearcher.java index b47095a096f4..660e0f7f67c3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainsSearcher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ChainsSearcher.java @@ -1,12 +1,15 @@ package com.intellij.codeInsight.completion.methodChains.search; -import com.intellij.codeInsight.completion.methodChains.Constants; +import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext; import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature; import com.intellij.compilerOutputIndex.impl.UsageIndexValue; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiMethod; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FactoryMap; -import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -21,95 +24,59 @@ public class ChainsSearcher { 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 ChainCompletionContext context) { 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; + return search(searchService, initializer, contextQNames, pathMaximalLength, maxResultSize, resolver, targetQName, + excludedParamsTypesQNames, context); } private static SearchInitializer createInitializer(final String targetQName, - final FactoryMap<MethodIncompleteSignature, PsiMethod[]> context, + final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver, final MethodChainsSearchService searchService, final Set<String> excludedParamsTypesQNames) { - return new SearchInitializer(searchService.getMethods(targetQName), context, targetQName, excludedParamsTypesQNames); + return new SearchInitializer(searchService.getMethods(targetQName), resolver, 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) { + @NotNull + private static List<MethodsChain> search(final MethodChainsSearchService searchService, + final SearchInitializer initializer, + final Set<String> toSet, + final int pathMaximalLength, + final int maxResultSize, + final FactoryMap<MethodIncompleteSignature, PsiMethod[]> resolver, + final String targetQName, + final Set<String> excludedParamsTypesQNames, + final ChainCompletionContext context) { final Set<String> allExcludedNames = MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName); - ProgressManager.checkCanceled(); - final SearchInitializer.InitResult initResult = initializer.init(excludedEdgeNames, toSet, searchService, contextMethodName); + final SearchInitializer.InitResult initResult = initializer.init(Collections.<String>emptySet()); + final Map<MethodIncompleteSignature, MethodsChain> knownDistance = initResult.getChains(); - final PriorityQueue<WeightAware<MethodIncompleteSignature>> q = - new PriorityQueue<WeightAware<MethodIncompleteSignature>>(initResult.getVertexes()); - MethodsChain result = initResult.getCurrentBestTargetChain(); + + final List<WeightAware<MethodIncompleteSignature>> allInitialVertexes = initResult.getVertexes(); + + final LinkedList<WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>> q = + new LinkedList<WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>>(ContainerUtil.map(allInitialVertexes, + new Function<WeightAware<MethodIncompleteSignature>, WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>>() { + @Override + public WeightAware<Pair<MethodIncompleteSignature, MethodsChain>> fun( + final WeightAware<MethodIncompleteSignature> methodIncompleteSignatureWeightAware) { + return new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>( + new Pair<MethodIncompleteSignature, MethodsChain>( + methodIncompleteSignatureWeightAware + .getUnderlying(), + new MethodsChain(resolver.get( + methodIncompleteSignatureWeightAware + .getUnderlying()), + methodIncompleteSignatureWeightAware + .getWeight(), + methodIncompleteSignatureWeightAware + .getUnderlying() + .getOwner())), + methodIncompleteSignatureWeightAware + .getWeight()); + } + })); int maxWeight = 0; for (final MethodsChain methodsChain : knownDistance.values()) { @@ -118,89 +85,175 @@ public class ChainsSearcher { } } - final WeightAware<MethodIncompleteSignature> maxVertex = q.peek(); - final int maxDistance; - if (maxVertex != null) { - maxDistance = maxVertex.getWeight(); - } - else { - return null; - } + final ResultHolder result = new ResultHolder(context); while (!q.isEmpty()) { - final WeightAware<MethodIncompleteSignature> currentVertex = q.poll(); + ProgressManager.checkCanceled(); + final WeightAware<Pair<MethodIncompleteSignature, MethodsChain>> 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); + final Pair<MethodIncompleteSignature, MethodsChain> currentVertexUnderlying = currentVertex.getUnderlying(); + final MethodsChain currentVertexMethodsChain = knownDistance.get(currentVertexUnderlying.getFirst()); if (currentVertexDistance != currentVertexMethodsChain.getChainWeight()) { continue; } - final SortedSet<UsageIndexValue> bigrams = searchService.getBigram(currentVertexUnderlying); - int bigramsSumWeight = 0; - int maxUpdatedWeight = 0; + if (currentVertex.getUnderlying().getFirst().isStatic() || toSet.contains(currentVertex.getUnderlying().getFirst().getOwner())) { + result.add(currentVertex.getUnderlying().getSecond()); + continue; + } + final SortedSet<UsageIndexValue> bigrams = searchService.getBigram(currentVertexUnderlying.getFirst()); + final MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>> currentSignatures = + new MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>>(maxResultSize); 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) { + if (!vertex.getOwner().equals(targetQName)) { 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()) { + if ((knownVertexMethodsChain == null || knownVertexMethodsChain.getChainWeight() < vertexDistance)) { + if (currentSignatures.isEmpty() || currentSignatures.last().getWeight() < vertexDistance) { 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())); + final MethodsChain newBestMethodsChain = + currentVertexMethodsChain.addEdge(psiMethods, indexValue.getMethodIncompleteSignature().getOwner(), vertexDistance); + if (newBestMethodsChain.size() <= pathMaximalLength - 1) { + currentSignatures + .add(new WeightAware<MethodIncompleteSignature>(indexValue.getMethodIncompleteSignature(), vertexDistance)); } 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; + } + } + } + boolean updated = false; + if (!currentSignatures.isEmpty()) { + boolean isBreak = false; + for (final WeightAware<MethodIncompleteSignature> sign : currentSignatures) { + final PsiMethod[] resolved = resolver.get(sign.getUnderlying()); + if (!isBreak) { + if (sign.getWeight() * maxResultSize > currentVertex.getWeight()) { + final boolean stopChain = sign.getUnderlying().isStatic() || toSet.contains(sign.getUnderlying().getOwner()); + if (stopChain) { + updated = true; + result.add(currentVertex.getUnderlying().getSecond().addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight())); + continue; + } + else { + updated = true; + final MethodsChain methodsChain = + currentVertexUnderlying.second.addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight()); + q.add(new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>( + new Pair<MethodIncompleteSignature, MethodsChain>(sign.getUnderlying(), methodsChain), sign.getWeight())); + continue; + } } } + final MethodsChain methodsChain = + currentVertexUnderlying.second.addEdge(resolved, sign.getUnderlying().getOwner(), sign.getWeight()); + if (ParametersMatcher.matchParameters(methodsChain, context).noUnmatchedAndHasMatched()) { + updated = true; + q.addFirst(new WeightAware<Pair<MethodIncompleteSignature, MethodsChain>>( + new Pair<MethodIncompleteSignature, MethodsChain>(sign.getUnderlying(), methodsChain), sign.getWeight())); + } + isBreak = true; } } - //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 (!updated && + (currentVertex.getUnderlying().getFirst().isStatic() || + !targetQName.equals(currentVertex.getUnderlying().getFirst().getOwner()))) { + result.add(currentVertex.getUnderlying().getSecond()); } + if (result.size() > maxResultSize) { + return result.getResult(); + } + } + return result.getResult(); + } + + private static class ResultHolder { + + private final List<MethodsChain> myResult; + private final ChainCompletionContext myContext; + + private ResultHolder(final ChainCompletionContext context) { + myContext = context; + myResult = new ArrayList<MethodsChain>(); } - if (result != null && result.getChainWeight() * Constants.CHAIN_SEARCH_MAGIC_RATIO >= maxWeight) { - return result; + public void add(final MethodsChain newChain) { + if (myResult.isEmpty()) { + myResult.add(newChain); + return; + } + boolean doAdd = true; + final Stack<Integer> indexesToRemove = new Stack<Integer>(); + for (int i = 0; i < myResult.size(); i++) { + final MethodsChain chain = myResult.get(i); + // + final MethodsChain.CompareResult r = MethodsChain.compare(chain, newChain, myContext); + switch (r) { + case LEFT_CONTAINS_RIGHT: + indexesToRemove.add(i); + break; + case RIGHT_CONTAINS_LEFT: + case EQUAL: + doAdd = false; + break; + case NOT_EQUAL: + break; + } + } + while (!indexesToRemove.empty()) { + myResult.remove((int)indexesToRemove.pop()); + } + if (doAdd) { + myResult.add(newChain); + } + } + + public List<MethodsChain> getResult() { + return myResult; + } + + public int size() { + return myResult.size(); + } + } + + private static int sumWeight(MaxSizeTreeSet<WeightAware<MethodIncompleteSignature>> weightAwareSignatures) { + int weight = 0; + for (WeightAware<MethodIncompleteSignature> weightAware : weightAwareSignatures) { + weight += weightAware.getWeight(); + } + return weight; + } + + private static boolean doChoose(final SortedSet<UsageIndexValue> bigrams, final int currentWeight, final int maxResultSize) { + if (bigrams.size() == 1) { + return true; + } + int sumWeight = 0; + for (final UsageIndexValue bigram : bigrams) { + sumWeight += bigram.getOccurrences(); + } + if (Math.abs(sumWeight - currentWeight) < currentWeight / maxResultSize) { + return true; + } + final List<UsageIndexValue> essentialValues = new ArrayList<UsageIndexValue>(); + Integer max = null; + for (UsageIndexValue bigram : bigrams) { + if (max == null) { + max = bigram.getOccurrences(); + } + if (max / bigram.getOccurrences() > maxResultSize) { + break; + } + essentialValues.add(bigram); + if (essentialValues.size() > maxResultSize) { + return false; + } } - return null; + return true; } -} +}
\ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MaxSizeTreeSet.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MaxSizeTreeSet.java new file mode 100644 index 000000000000..535cf090df2c --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MaxSizeTreeSet.java @@ -0,0 +1,209 @@ +package com.intellij.codeInsight.completion.methodChains.search; + +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * @author Dmitry Batkovich + */ +public class MaxSizeTreeSet<E> implements NavigableSet<E> { + + @NotNull + private final NavigableSet<E> myUnderlying; + private final int myMaxSize; + + public MaxSizeTreeSet(final int maxSize) { + myMaxSize = maxSize; + if (myMaxSize < 1) { + throw new IllegalArgumentException(); + } + myUnderlying = new TreeSet<E>(); + } + + public E lower(final E e) { + return myUnderlying.lower(e); + } + + public E floor(final E e) { + return myUnderlying.floor(e); + } + + public E ceiling(final E e) { + return myUnderlying.ceiling(e); + } + + public E higher(final E e) { + return myUnderlying.higher(e); + } + + @Override + public E pollFirst() { + return myUnderlying.pollFirst(); + } + + @Override + public E pollLast() { + return myUnderlying.pollLast(); + } + + @NotNull + @Override + public Iterator<E> iterator() { + return myUnderlying.iterator(); + } + + @NotNull + @Override + public NavigableSet<E> descendingSet() { + return myUnderlying.descendingSet(); + } + + @NotNull + @Override + public Iterator<E> descendingIterator() { + return myUnderlying.descendingIterator(); + } + + @NotNull + public NavigableSet<E> subSet(final E fromElement, final boolean fromInclusive, final E toElement, final boolean toInclusive) { + return myUnderlying.subSet(fromElement, fromInclusive, toElement, toInclusive); + } + + @NotNull + public NavigableSet<E> headSet(final E toElement, final boolean inclusive) { + return myUnderlying.headSet(toElement, inclusive); + } + + @NotNull + public NavigableSet<E> tailSet(final E fromElement, final boolean inclusive) { + return myUnderlying.tailSet(fromElement, inclusive); + } + + @NotNull + public SortedSet<E> subSet(final E fromElement, final E toElement) { + return myUnderlying.subSet(fromElement, toElement); + } + + @NotNull + public SortedSet<E> headSet(final E toElement) { + return myUnderlying.headSet(toElement); + } + + @NotNull + public SortedSet<E> tailSet(final E fromElement) { + return myUnderlying.tailSet(fromElement); + } + + @Nullable + @Override + public Comparator<? super E> comparator() { + return myUnderlying.comparator(); + } + + @Override + public E first() { + return myUnderlying.first(); + } + + @Override + public E last() { + return myUnderlying.last(); + } + + @Override + public int size() { + return myUnderlying.size(); + } + + @Override + public boolean isEmpty() { + return myUnderlying.isEmpty(); + } + + @Override + public boolean contains(final Object o) { + return myUnderlying.contains(o); + } + + @NotNull + @Override + public Object[] toArray() { + return myUnderlying.toArray(); + } + + @NotNull + @Override + public <T> T[] toArray(final T[] a) { + return myUnderlying.toArray(a); + } + + public boolean add(final E e) { + if (myUnderlying.size() == myMaxSize) { + //noinspection ConstantConditions + final Comparator<? super E> comparator = comparator(); + if ((comparator == null ? ((Comparable)e).compareTo(last()) : comparator.compare(e, last())) < 0) { + final boolean isAdded = myUnderlying.add(e); + if (isAdded) { + pollLast(); + return true; + } + } + return false; + } + return myUnderlying.add(e); + } + + @Override + public boolean remove(final Object o) { + return myUnderlying.remove(o); + } + + @Override + public boolean containsAll(final Collection<?> c) { + return myUnderlying.containsAll(c); + } + + public boolean addAll(final Collection<? extends E> c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection<?> c) { + return myUnderlying.retainAll(c); + } + + @Override + public boolean removeAll(final Collection<?> c) { + return myUnderlying.removeAll(c); + } + + @Override + public void clear() { + myUnderlying.clear(); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (!(o instanceof MaxSizeTreeSet)) return false; + + final MaxSizeTreeSet that = (MaxSizeTreeSet)o; + + if (!myUnderlying.equals(that.myUnderlying)) return false; + + return true; + } + + @Override + public int hashCode() { + return myUnderlying.hashCode(); + } + + @Override + public String toString() { + return myUnderlying.toString(); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodChainsSearchService.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodChainsSearchService.java index 0bc1991c715a..2c004593929e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodChainsSearchService.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodChainsSearchService.java @@ -4,17 +4,10 @@ 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 com.intellij.psi.PsiManager; 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; @@ -26,20 +19,14 @@ public class MethodChainsSearchService { 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; + private final boolean myUseBigrams; - public MethodChainsSearchService(final Project project) { - myOverridenMethodsService = new OverridenMethodsService(project); + public MethodChainsSearchService(final Project project, final boolean useBigrams) { + myUseBigrams = useBigrams; 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() { @@ -49,9 +36,11 @@ public class MethodChainsSearchService { @NotNull @SuppressWarnings("unchecked") public SortedSet<UsageIndexValue> getBigram(final MethodIncompleteSignature methodIncompleteSignature) { - final TreeSet<UsageIndexValue> value = myBigramMethodsUsageIndex.getValues(methodIncompleteSignature); - if (value != null) { - return value; + final TreeSet<UsageIndexValue> values = myUseBigrams + ? myBigramMethodsUsageIndex.getValues(methodIncompleteSignature) + : myMethodsUsageIndex.getValues(methodIncompleteSignature.getOwner()); + if (values != null) { + return values; } return EMPTY_SORTED_SET; } @@ -66,28 +55,7 @@ public class MethodChainsSearchService { 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(); + public PsiManager getPsiManager() { + return PsiManager.getInstance(getProject()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChain.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChain.java index c7f11c3af979..517a1d6181b8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChain.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChain.java @@ -1,11 +1,15 @@ package com.intellij.codeInsight.completion.methodChains.search; +import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; +import com.intellij.psi.PsiManager; +import com.intellij.psi.PsiMethod; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import static com.intellij.util.containers.ContainerUtil.reverse; @@ -15,27 +19,26 @@ import static com.intellij.util.containers.ContainerUtil.reverse; public class MethodsChain { private final List<PsiMethod[]> myRevertedPath; private final int myWeight; + // + // chain qualifier class could be different with method.getContainingClass() + private final String myQualifierClassName; - public MethodsChain(final PsiMethod[] methods, final int weight) { - this(ContainerUtil.<PsiMethod[]>newArrayList(methods), weight); + public MethodsChain(final PsiMethod[] methods, final int weight, final String qualifierClassName) { + this(ContainerUtil.<PsiMethod[]>newArrayList(methods), weight, qualifierClassName); } - public MethodsChain(final List<PsiMethod[]> revertedPath, final int weight) { + public MethodsChain(final List<PsiMethod[]> revertedPath, final int weight, final String qualifierClassName) { myRevertedPath = revertedPath; myWeight = weight; + myQualifierClassName = qualifierClassName; } 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(); + public String getQualifierClassName() { + return myQualifierClassName; } @Nullable @@ -51,43 +54,11 @@ public class MethodsChain { return myWeight; } - public MethodsChain addEdge(final PsiMethod[] psiMethods) { + public MethodsChain addEdge(final PsiMethod[] psiMethods, final String newQualifierClassName, final int newWeight) { 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(); + return new MethodsChain(newRevertedPath, newWeight, newQualifierClassName); } @Override @@ -95,34 +66,8 @@ public class MethodsChain { 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) { + public static CompareResult compare(final MethodsChain left, final MethodsChain right, final ChainCompletionContext context) { if (left.size() == 0) { return CompareResult.RIGHT_CONTAINS_LEFT; } @@ -132,13 +77,13 @@ public class MethodsChain { 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())) { + if (thisNext == null || thatNext == null) { + throw new NullPointerException(); + } + if (((thisNext.isConstructor() != thatNext.isConstructor())) || !thisNext.getName().equals(thatNext.getName())) { return CompareResult.NOT_EQUAL; } } @@ -148,7 +93,27 @@ public class MethodsChain { if (!leftIterator.hasNext() && rightIterator.hasNext()) { return CompareResult.RIGHT_CONTAINS_LEFT; } - return CompareResult.EQUAL; + + + return hasBaseMethod(left.getPath().get(0), right.getPath().get(0), PsiManager.getInstance(context.getProject())) + ? CompareResult.EQUAL + : CompareResult.NOT_EQUAL; + } + + private static boolean hasBaseMethod(final PsiMethod[] left, final PsiMethod[] right, final PsiManager psiManager) { + for (PsiMethod rightMethod : right) { + final PsiMethod[] rightSupers = rightMethod.findDeepestSuperMethods(); + if (rightSupers.length != 0) { + for (final PsiMethod leftMethod : left) { + final PsiMethod[] leftSupers = leftMethod.findDeepestSuperMethods(); + if (leftSupers.length != 0) { + if (psiManager.areElementsEquivalent(leftSupers[0], rightSupers[0])) { + return true; + } + } + } + } + } return false; } public enum CompareResult { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChainLookupRangingHelper.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChainLookupRangingHelper.java index 58a76937b801..cd6d5516bd40 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChainLookupRangingHelper.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/MethodsChainLookupRangingHelper.java @@ -53,10 +53,11 @@ public class MethodsChainLookupRangingHelper { final int lastMethodWeight = chain.getChainWeight(); int unreachableParametersCount = 0; int notMatchedStringVars = 0; + int matchedParametersInContext = 0; Boolean isFirstMethodStatic = null; Boolean hasCallingVariableInContext = null; LookupElement chainLookupElement = null; - + PsiClass newVariableClass = null; final NullableNotNullManager nullableNotNullManager = NullableNotNullManager.getInstance(context.getProject()); for (final PsiMethod[] psiMethods : chain.getPath()) { @@ -68,40 +69,58 @@ public class MethodsChainLookupRangingHelper { if (isFirstMethodStatic == null) { isFirstMethodStatic = psiMethods[0].hasModifierProperty(PsiModifier.STATIC); } - final MethodProcResult procResult = - processMethod(method, context, lastMethodWeight, chainLookupElement == null, nullableNotNullManager); + final PsiClass qualifierClass; + final boolean isHead = chainLookupElement == null; + if (isHead) { + final String qualifierClassName = chain.getQualifierClassName(); + qualifierClass = JavaPsiFacade.getInstance(context.getProject()). + findClass(qualifierClassName, context.getResolveScope()); + } + else { + qualifierClass = null; + } + + final MethodProcResult procResult = processMethod(method, qualifierClass, context, lastMethodWeight, isHead, nullableNotNullManager); if (procResult == null) { return null; } if (hasCallingVariableInContext == null) { hasCallingVariableInContext = procResult.hasCallingVariableInContext(); } + if (isHead && procResult.isIntroduceNewVariable()) { + newVariableClass = qualifierClass; + } + matchedParametersInContext += procResult.getMatchedParametersInContext(); unreachableParametersCount += procResult.getUnreachableParametersCount(); notMatchedStringVars += procResult.getNotMatchedStringVars(); - chainLookupElement = chainLookupElement == null - ? procResult.getLookupElement() - : new JavaChainLookupElement(chainLookupElement, procResult.getLookupElement()); + chainLookupElement = + isHead ? procResult.getLookupElement() : new JavaChainLookupElement(chainLookupElement, procResult.getLookupElement()); + } + + if (newVariableClass != null) { + chainLookupElement = ChainCompletionNewVariableLookupElement.create(newVariableClass, chainLookupElement); } - final ChainRelevance relevance = new ChainRelevance(chainSize, - lastMethodWeight, - unreachableParametersCount, - notMatchedStringVars, - hasCallingVariableInContext, - isFirstMethodStatic); + final ChainRelevance relevance = + new ChainRelevance(chainSize, lastMethodWeight, unreachableParametersCount, notMatchedStringVars, hasCallingVariableInContext, + isFirstMethodStatic, matchedParametersInContext); return new WeightableChainLookupElement(chainLookupElement, relevance); } + @Nullable private static MethodProcResult processMethod(@NotNull final PsiMethod method, + @Nullable final PsiClass qualifierClass, final ChainCompletionContext context, final int weight, final boolean isHeadMethod, final NullableNotNullManager nullableNotNullManager) { int unreachableParametersCount = 0; int notMatchedStringVars = 0; + int matchedParametersInContext = 0; boolean hasCallingVariableInContext = false; + boolean introduceNewVariable = false; final PsiParameterList parameterList = method.getParameterList(); final TIntObjectHashMap<SubLookupElement> parametersMap = new TIntObjectHashMap<SubLookupElement>(parameterList.getParametersCount()); final PsiParameter[] parameters = parameterList.getParameters(); @@ -123,18 +142,21 @@ public class MethodsChainLookupRangingHelper { final PsiVariable contextVariable = ContainerUtil.getFirstItem(contextVariables, null); if (contextVariable != null) { if (contextVariables.size() == 1) parametersMap.put(i, new VariableSubLookupElement(contextVariable)); + matchedParametersInContext++; continue; } final Collection<ContextRelevantVariableGetter> relevantVariablesGetters = context.getRelevantVariablesGetters(typeQName); final ContextRelevantVariableGetter contextVariableGetter = ContainerUtil.getFirstItem(relevantVariablesGetters, null); if (contextVariableGetter != null) { if (relevantVariablesGetters.size() == 1) parametersMap.put(i, contextVariableGetter.createSubLookupElement()); + matchedParametersInContext++; continue; } final Collection<PsiMethod> containingClassMethods = context.getContainingClassMethods(typeQName); final PsiMethod contextRelevantGetter = ContainerUtil.getFirstItem(containingClassMethods, null); if (contextRelevantGetter != null) { if (containingClassMethods.size() == 1) parametersMap.put(i, new GetterLookupSubLookupElement(method.getName())); + matchedParametersInContext++; continue; } final ContextRelevantStaticMethod contextRelevantStaticMethod = @@ -144,6 +166,7 @@ public class MethodsChainLookupRangingHelper { // In most cases it is not really relevant // //parametersMap.put(i, contextRelevantStaticMethod.createLookupElement()); + matchedParametersInContext++; continue; } if (!nullableNotNullManager.isNullable(parameter, true)) { @@ -155,14 +178,15 @@ public class MethodsChainLookupRangingHelper { final LookupElement lookupElement; if (isHeadMethod) { if (method.hasModifierProperty(PsiModifier.STATIC)) { + hasCallingVariableInContext = true; lookupElement = createLookupElement(method, parametersMap); } else if (method.isConstructor()) { return null; } else { - final PsiClass containingClass = method.getContainingClass(); - final String classQName = containingClass.getQualifiedName(); + @SuppressWarnings("ConstantConditions") + final String classQName = qualifierClass.getQualifiedName(); if (classQName == null) return null; final Object e = ContainerUtil.getFirstItem(context.getContextRefElements(classQName), null); if (e != null) { @@ -182,16 +206,23 @@ public class MethodsChainLookupRangingHelper { } 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); + if (!context.getContainingClassQNames().contains(classQName)) { + introduceNewVariable = true; + } + } } } else { lookupElement = createLookupElement(method, parametersMap); } - return new MethodProcResult(lookupElement, unreachableParametersCount, notMatchedStringVars, hasCallingVariableInContext); + return new MethodProcResult(lookupElement, + unreachableParametersCount, + notMatchedStringVars, + hasCallingVariableInContext, + introduceNewVariable, + matchedParametersInContext); } private static class MethodProcResult { @@ -199,15 +230,25 @@ public class MethodsChainLookupRangingHelper { private final int myUnreachableParametersCount; private final int myNotMatchedStringVars; private final boolean myHasCallingVariableInContext; + private final boolean myIntroduceNewVariable; + private final int myMatchedParametersInContext; private MethodProcResult(final LookupElement methodLookup, final int unreachableParametersCount, final int notMatchedStringVars, - final boolean hasCallingVariableInContext) { + final boolean hasCallingVariableInContext, + final boolean introduceNewVariable, + final int matchedParametersInContext) { myMethodLookup = methodLookup; myUnreachableParametersCount = unreachableParametersCount; myNotMatchedStringVars = notMatchedStringVars; myHasCallingVariableInContext = hasCallingVariableInContext; + myIntroduceNewVariable = introduceNewVariable; + myMatchedParametersInContext = matchedParametersInContext; + } + + private boolean isIntroduceNewVariable() { + return myIntroduceNewVariable; } private boolean hasCallingVariableInContext() { @@ -225,6 +266,10 @@ public class MethodsChainLookupRangingHelper { private int getNotMatchedStringVars() { return myNotMatchedStringVars; } + + public int getMatchedParametersInContext() { + return myMatchedParametersInContext; + } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ParametersMatcher.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ParametersMatcher.java new file mode 100644 index 000000000000..2c2a563cef8c --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/ParametersMatcher.java @@ -0,0 +1,82 @@ +package com.intellij.codeInsight.completion.methodChains.search; + +import com.intellij.codeInsight.completion.methodChains.completion.context.ChainCompletionContext; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameter; +import com.intellij.psi.PsiPrimitiveType; +import com.intellij.psi.PsiType; +import org.jetbrains.annotations.NotNull; + +import java.util.NavigableSet; +import java.util.TreeSet; + +/** + * @author Dmitry Batkovich + */ +public class ParametersMatcher { + + public static MatchResult matchParameters(final MethodsChain chain, final ChainCompletionContext context) { + MatchResult overallResult = EMPTY; + for (final PsiMethod[] methods : chain.getPath()) { + final NavigableSet<MatchResult> matchResults = new TreeSet<MatchResult>(); + for (final PsiMethod method : methods) { + matchResults.add(matchParameters(method, context)); + } + final MatchResult best = matchResults.first(); + overallResult = overallResult.add(best); + } + return overallResult; + } + + public static MatchResult matchParameters(final PsiMethod method, final ChainCompletionContext context) { + int matched = 0; + int unMatched = 0; + for (final PsiParameter parameter : method.getParameterList().getParameters()) { + final PsiType type = parameter.getType(); + if (context.contains(type.getCanonicalText()) || type instanceof PsiPrimitiveType) { + matched++; + } + else { + unMatched++; + } + } + return new MatchResult(matched, unMatched); + } + + private static final MatchResult EMPTY = new MatchResult(0, 0); + + public static class MatchResult implements Comparable<MatchResult> { + private final int myMatched; + private final int myUnMatched; + + private MatchResult(final int matched, final int unMatched) { + myMatched = matched; + myUnMatched = unMatched; + } + + public int getMatched() { + return myMatched; + } + + public int getUnMatched() { + return myUnMatched; + } + + public MatchResult add(final MatchResult other) { + return new MatchResult(getMatched() + other.getMatched(), getUnMatched() + other.getUnMatched()); + } + + public boolean noUnmatchedAndHasMatched() { + return myUnMatched == 0 && myMatched != 0; + } + + @Override + public int compareTo(@NotNull final MatchResult other) { + final int sub = getUnMatched() - other.getUnMatched(); + if (sub != 0) { + return sub; + } + return getMatched() - other.getMatched(); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/SearchInitializer.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/SearchInitializer.java index 1f9831361299..b89edb677836 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/SearchInitializer.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/SearchInitializer.java @@ -3,11 +3,8 @@ 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.*; @@ -15,9 +12,8 @@ import java.util.*; * @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com> */ public class SearchInitializer { - private final List<WeightAware<MethodIncompleteSignature>> myVertexes; + private final List<WeightAware<MethodIncompleteSignature>> myVertices; private final LinkedHashMap<MethodIncompleteSignature, MethodsChain> myChains; - private final Map<MethodIncompleteSignature, Integer> myOccurrencesMap; private final FactoryMap<MethodIncompleteSignature, PsiMethod[]> myResolver; public SearchInitializer(final SortedSet<UsageIndexValue> indexValues, @@ -26,9 +22,8 @@ public class SearchInitializer { final Set<String> excludedParamsTypesQNames) { myResolver = resolver; final int size = indexValues.size(); - myVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size); + myVertices = new ArrayList<WeightAware<MethodIncompleteSignature>>(size); myChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(size); - myOccurrencesMap = new HashMap<MethodIncompleteSignature, Integer>(size); add(indexValues, MethodChainsSearchUtil.unionToHashSet(excludedParamsTypesQNames, targetQName)); } @@ -39,7 +34,8 @@ public class SearchInitializer { final int occurrences = indexValue.getOccurrences(); if (bestOccurrences == -1) { bestOccurrences = occurrences; - } else if (bestOccurrences > occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO) { + } + else if (bestOccurrences > occurrences * Constants.CHAIN_SEARCH_MAGIC_RATIO) { return; } } @@ -51,65 +47,40 @@ public class SearchInitializer { 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); + final MethodsChain methodsChain = new MethodsChain(psiMethods, occurrences, indexValue.getMethodIncompleteSignature().getOwner()); myChains.put(methodInvocation, methodsChain); - myVertexes.add(new WeightAware<MethodIncompleteSignature>(methodInvocation, occurrences)); - myOccurrencesMap.put(methodInvocation, occurrences); + myVertices.add(new WeightAware<MethodIncompleteSignature>(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; + public InitResult init(final Set<String> excludedEdgeNames) { + final int size = myVertices.size(); final List<WeightAware<MethodIncompleteSignature>> initedVertexes = new ArrayList<WeightAware<MethodIncompleteSignature>>(size); - final LinkedHashMap<MethodIncompleteSignature, MethodsChain> initedChains = new LinkedHashMap<MethodIncompleteSignature, MethodsChain>(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) { + for (final WeightAware<MethodIncompleteSignature> vertex : myVertices) { 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); + return new InitResult(initedVertexes, initedChains); } 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; + final LinkedHashMap<MethodIncompleteSignature, MethodsChain> chains) { + myVertexes = vertexes; + myChains = chains; } public List<WeightAware<MethodIncompleteSignature>> getVertexes() { @@ -119,10 +90,5 @@ public class SearchInitializer { public LinkedHashMap<MethodIncompleteSignature, MethodsChain> getChains() { return myChains; } - - @Nullable - public MethodsChain getCurrentBestTargetChain() { - return myCurrentBestTargetChain; - } } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/WeightAware.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/WeightAware.java index 617053d57baf..7fd682457348 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/WeightAware.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/WeightAware.java @@ -24,6 +24,10 @@ public class WeightAware<V> implements Comparable<WeightAware<V>> { @Override public int compareTo(@NotNull final WeightAware<V> that) { - return -getWeight() + that.getWeight(); + final int sub = -getWeight() + that.getWeight(); + if (sub != 0) { + return sub; + } + return myUnderlying.hashCode() - that.myUnderlying.hashCode(); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/service/OverridenMethodsService.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/service/OverridenMethodsService.java deleted file mode 100644 index f310a463b49c..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/service/OverridenMethodsService.java +++ /dev/null @@ -1,65 +0,0 @@ -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); - } -} diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/service/SingletonService.java b/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/service/SingletonService.java deleted file mode 100644 index 4f27ad24cf1f..000000000000 --- a/java/java-impl/src/com/intellij/codeInsight/completion/methodChains/search/service/SingletonService.java +++ /dev/null @@ -1,89 +0,0 @@ -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(); - } - -} |