diff --git a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java index 3d9715e89932..bbad1ed8e70a 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearchHelper.java @@ -3,17 +3,15 @@ */ package com.intellij.psi.impl.search; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadActionProcessor; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.light.LightMemberReference; import com.intellij.psi.search.PsiSearchScopeUtil; +import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.util.PairProcessor; import com.intellij.util.Processor; /** @@ -30,38 +28,29 @@ public class ConstructorReferencesSearchHelper { final PsiMethod constructor, final SearchScope searchScope, boolean ignoreAccessScope, - final boolean isStrictSignatureSearch) { - final Ref result = new Ref(); - PsiClass aClass = ApplicationManager.getApplication().runReadAction(new Computable() { - public PsiClass compute() { - PsiClass aClass = constructor.getContainingClass(); - if (aClass == null) { - result.set(true); - return null; - } + final boolean isStrictSignatureSearch, SearchRequestCollector collector) { + PsiClass aClass = constructor.getContainingClass(); + if (aClass == null) { + return true; + } - if (aClass.isEnum()) { - PsiField[] fields = aClass.getFields(); - for (PsiField field : fields) { - if (field instanceof PsiEnumConstant) { - PsiReference reference = field.getReference(); - if (reference != null && reference.isReferenceTo(constructor)) { - if (!processor.process(reference)) { - result.set(false); - return null; - } - } + if (aClass.isEnum()) { + for (PsiField field : aClass.getFields()) { + if (field instanceof PsiEnumConstant) { + PsiReference reference = field.getReference(); + if (reference != null && reference.isReferenceTo(constructor)) { + if (!processor.process(reference)) { + return false; } } } - return aClass; } - }); - if (!result.isNull()) return result.get(); + } // search usages like "new XXX(..)" - Processor processor1 = new ReadActionProcessor() { - public boolean processInReadAction(final PsiReference reference) { + PairProcessor processor1 = new PairProcessor() { + @Override + public boolean process(PsiReference reference, SearchRequestCollector collector) { PsiElement parent = reference.getElement().getParent(); if (parent instanceof PsiAnonymousClass) { parent = parent.getParent(); @@ -85,12 +74,12 @@ public class ConstructorReferencesSearchHelper { } }; - if (!ReferencesSearch.search(aClass, searchScope, ignoreAccessScope).forEach(processor1)) return false; + ReferencesSearch.searchOptimized(aClass, searchScope, ignoreAccessScope, collector, true, processor1); final boolean constructorCanBeCalledImplicitly = constructor.getParameterList().getParametersCount() == 0; // search usages like "this(..)" if (!processSuperOrThis(processor, aClass, constructor, constructorCanBeCalledImplicitly, searchScope, isStrictSignatureSearch, - PsiKeyword.THIS)) { + PsiKeyword.THIS)) { return false; } @@ -98,7 +87,7 @@ public class ConstructorReferencesSearchHelper { Processor processor2 = new Processor() { public boolean process(PsiClass inheritor) { return processSuperOrThis(processor, (PsiClass)inheritor.getNavigationElement(), constructor, constructorCanBeCalledImplicitly, searchScope, isStrictSignatureSearch, - PsiKeyword.SUPER); + PsiKeyword.SUPER); } }; @@ -110,21 +99,6 @@ public class ConstructorReferencesSearchHelper { final PsiMethod constructor, final boolean constructorCanBeCalledImplicitly, final SearchScope searchScope, final boolean isStrictSignatureSearch, final String superOrThisKeyword) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - public Boolean compute() { - return processSuperOrThisInReadAction(inheritor, searchScope, superOrThisKeyword, isStrictSignatureSearch, constructor, - constructorCanBeCalledImplicitly, processor); - } - }); - } - - private boolean processSuperOrThisInReadAction(final PsiClass inheritor, - final SearchScope searchScope, - final String superOrThisKeyword, - final boolean isStrictSignatureSearch, - final PsiMethod constructor, - final boolean constructorCanBeCalledImplicitly, - final Processor processor) { PsiMethod[] constructors = inheritor.getConstructors(); if (constructors.length == 0 && constructorCanBeCalledImplicitly) { processImplicitConstructorCall(inheritor, processor, constructor, inheritor); diff --git a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java index acb87007dc39..85382259fdb0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/ConstructorReferencesSearcher.java @@ -3,24 +3,28 @@ */ package com.intellij.psi.impl.search; +import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; -import com.intellij.util.QueryExecutor; /** * @author max */ -public class ConstructorReferencesSearcher implements QueryExecutor { - public boolean execute(final ReferencesSearch.SearchParameters p, final Processor consumer) { +public class ConstructorReferencesSearcher extends QueryExecutorBase { + protected ConstructorReferencesSearcher() { + super(true); + } + + @Override + public void processQuery(ReferencesSearch.SearchParameters p, Processor consumer) { final PsiElement element = p.getElementToSearch(); if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()) { - return new ConstructorReferencesSearchHelper(PsiManager.getInstance(element.getProject())) - .processConstructorReferences(consumer, (PsiMethod)p.getElementToSearch(), p.getScope(), p.isIgnoreAccessScope(), true); + new ConstructorReferencesSearchHelper(PsiManager.getInstance(element.getProject())) + .processConstructorReferences(consumer, (PsiMethod)p.getElementToSearch(), p.getScope(), p.isIgnoreAccessScope(), true, p.getOptimizer()); } - return true; } } diff --git a/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java b/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java index a09f590f3c08..2e51faa888d2 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/MethodUsagesSearcher.java @@ -3,9 +3,7 @@ */ package com.intellij.psi.impl.search; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.QueryExecutorBase; -import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; @@ -18,6 +16,9 @@ import com.intellij.util.Processor; * @author max */ public class MethodUsagesSearcher extends QueryExecutorBase { + protected MethodUsagesSearcher() { + super(true); + } @Override public void processQuery(MethodReferencesSearch.SearchParameters p, Processor consumer) { @@ -28,60 +29,42 @@ public class MethodUsagesSearcher extends QueryExecutorBase() { - public PsiClass compute() { - return method.isValid() ? method.getContainingClass() : null; - } - }); + final PsiClass aClass = method.getContainingClass(); if (aClass == null) return; final boolean strictSignatureSearch = p.isStrictSignatureSearch(); if (method.isConstructor()) { - collector.searchCustom(new Processor>() { - public boolean process(Processor consumer) { - return new ConstructorReferencesSearchHelper(psiManager). - processConstructorReferences(consumer, method, searchScope, !strictSignatureSearch, strictSignatureSearch); - } - }); + new ConstructorReferencesSearchHelper(psiManager). + processConstructorReferences(consumer, method, searchScope, !strictSignatureSearch, strictSignatureSearch, collector); } - boolean needStrictSignatureSearch = ApplicationManager.getApplication().runReadAction(new Computable() { - public Boolean compute() { - return method.isValid() && strictSignatureSearch && (aClass instanceof PsiAnonymousClass + boolean needStrictSignatureSearch = strictSignatureSearch && (aClass instanceof PsiAnonymousClass || aClass.hasModifierProperty(PsiModifier.FINAL) || method.hasModifierProperty(PsiModifier.STATIC) || method.hasModifierProperty(PsiModifier.FINAL) || method.hasModifierProperty(PsiModifier.PRIVATE)); - } - }); if (needStrictSignatureSearch) { - ReferencesSearch.search(new ReferencesSearch.SearchParameters(method, searchScope, false, collector)).forEach(consumer); + ReferencesSearch.searchOptimized(method, searchScope, false, collector, consumer); return; } - ApplicationManager.getApplication().runReadAction(new Runnable() { - public void run() { - if (!method.isValid()) return; + final String textToSearch = method.getName(); + final PsiMethod[] methods = strictSignatureSearch ? new PsiMethod[]{method} : aClass.findMethodsByName(textToSearch, false); - final String textToSearch = method.getName(); - final PsiMethod[] methods = strictSignatureSearch ? new PsiMethod[]{method} : aClass.findMethodsByName(textToSearch, false); + SearchScope accessScope = methods[0].getUseScope(); + for (int i = 1; i < methods.length; i++) { + PsiMethod method1 = methods[i]; + accessScope = accessScope.union(method1.getUseScope()); + } - SearchScope accessScope = methods[0].getUseScope(); - for (int i = 1; i < methods.length; i++) { - PsiMethod method1 = methods[i]; - accessScope = accessScope.union(method1.getUseScope()); - } + final SearchScope restrictedByAccess = searchScope.intersectWith(accessScope); - final SearchScope restrictedByAccess = searchScope.intersectWith(accessScope); + short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES; + collector.searchWord(textToSearch, restrictedByAccess, searchContext, true, + new MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, methods)); - short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_COMMENTS | UsageSearchContext.IN_FOREIGN_LANGUAGES; - collector.searchWord(textToSearch, restrictedByAccess, searchContext, true, - new MethodTextOccurrenceProcessor(aClass, strictSignatureSearch, methods)); - - SimpleAccessorReferenceSearcher.addPropertyAccessUsages(method, restrictedByAccess, collector); - } - }); + SimpleAccessorReferenceSearcher.addPropertyAccessUsages(method, restrictedByAccess, collector); } diff --git a/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java b/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java index 93bfe3cb66c3..dfa15c5399c4 100644 --- a/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java +++ b/java/openapi/src/com/intellij/psi/search/searches/MethodReferencesSearch.java @@ -17,14 +17,10 @@ package com.intellij.psi.search.searches; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.SearchRequestCollector; -import com.intellij.psi.search.SearchRequestQuery; -import com.intellij.psi.search.SearchScope; -import com.intellij.util.MergeQuery; -import com.intellij.util.Query; -import com.intellij.util.UniqueResultsQuery; +import com.intellij.psi.search.*; +import com.intellij.util.*; import gnu.trove.TObjectHashingStrategy; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -75,6 +71,21 @@ public class MethodReferencesSearch extends ExtensibleQueryFactory processor) { + searchOptimized(method, scope, strictSignatureSearch, collector, false, new PairProcessor() { + @Override + public boolean process(PsiReference psiReference, SearchRequestCollector collector) { + return processor.process(psiReference); + } + }); + } +public static void searchOptimized(final PsiMethod method, SearchScope scope, final boolean strictSignatureSearch, SearchRequestCollector collector, final boolean inReadAction, PairProcessor processor) { + final SearchRequestCollector nested = new SearchRequestCollector(); + collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(method, scope, strictSignatureSearch, nested)), nested, + inReadAction, processor)); + } + public static Query search(final SearchParameters parameters) { final Query result = INSTANCE.createQuery(parameters); if (parameters.isSharedOptimizer) { diff --git a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java index 5e16c40b8c91..b2a61d521ae7 100644 --- a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java +++ b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java @@ -25,4 +25,8 @@ public class PsiSearchRequest { this.processor = processor; } + @Override + public String toString() { + return "PsiSearchRequest: " + word + "; " + processor; + } } diff --git a/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java new file mode 100644 index 000000000000..158ee91dc151 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/search/QuerySearchRequest.java @@ -0,0 +1,43 @@ +package com.intellij.psi.search; + +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.psi.PsiReference; +import com.intellij.util.PairProcessor; +import com.intellij.util.Processor; +import com.intellij.util.Query; + +/** + * @author peter + */ +public class QuerySearchRequest { + public final Query query; + public final SearchRequestCollector collector; + public final Processor processor; + + public QuerySearchRequest(Query query, + final SearchRequestCollector collector, + boolean inReadAction, final PairProcessor processor) { + this.query = query; + this.collector = collector; + if (inReadAction) { + this.processor = new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference psiReference) { + return processor.process(psiReference, collector); + } + }; + } else { + this.processor = new Processor() { + @Override + public boolean process(PsiReference psiReference) { + return processor.process(psiReference, collector); + } + }; + } + + } + + public void runQuery() { + query.forEach(processor); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java index b4e434ebcf6f..596282772625 100644 --- a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java +++ b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java @@ -3,6 +3,7 @@ package com.intellij.psi.search; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.util.Processor; +import com.intellij.util.containers.CollectionFactory; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -12,8 +13,10 @@ import java.util.List; * @author peter */ public class SearchRequestCollector { - private final List myRequests = new ArrayList(); - private final List>> myCustomSearchActions = new ArrayList>>(); + private final Object lock = new Object(); + private final List myWordRequests = CollectionFactory.arrayList(); + private final List myQueryRequests = CollectionFactory.arrayList(); + private final List>> myCustomSearchActions = CollectionFactory.arrayList(); public void searchWord(@NotNull String word, @NotNull SearchScope searchScope, boolean caseSensitive, @NotNull PsiElement searchTarget) { final short searchContext = UsageSearchContext.IN_CODE | UsageSearchContext.IN_FOREIGN_LANGUAGES | UsageSearchContext.IN_COMMENTS; @@ -29,18 +32,48 @@ public class SearchRequestCollector { return; } - myRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor)); + synchronized (lock) { + myWordRequests.add(new PsiSearchRequest(searchScope, word, searchContext, caseSensitive, processor)); + } + } + + public void searchQuery(QuerySearchRequest request) { + assert request.collector != this; + synchronized (lock) { + myQueryRequests.add(request); + } } public void searchCustom(Processor> searchAction) { - myCustomSearchActions.add(searchAction); + synchronized (lock) { + myCustomSearchActions.add(searchAction); + } } - public List getSearchRequests() { - return myRequests; + public boolean hasRequests() { + synchronized (lock) { + return !myWordRequests.isEmpty() || !myCustomSearchActions.isEmpty() || !myQueryRequests.isEmpty(); + } } - public List>> getCustomSearchActions() { - return myCustomSearchActions; + public List takeQueryRequests() { + return takeRequests(myQueryRequests); + } + + private List takeRequests(List list) { + synchronized (lock) { + final List requests = new ArrayList(list); + requests.addAll(list); + list.clear(); + return requests; + } + } + + public List takeSearchRequests() { + return takeRequests(myWordRequests); + } + + public List>> takeCustomSearchActions() { + return takeRequests(myCustomSearchActions); } } diff --git a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java index c43a8875a6f5..aa869f7b507c 100644 --- a/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java +++ b/platform/lang-api/src/com/intellij/psi/search/searches/ReferencesSearch.java @@ -17,10 +17,7 @@ package com.intellij.psi.search.searches; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.SearchRequestCollector; -import com.intellij.psi.search.SearchRequestQuery; -import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.*; import com.intellij.util.*; import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; @@ -111,4 +108,21 @@ public class ReferencesSearch extends ExtensibleQueryFactory processor) { + searchOptimized(element, searchScope, ignoreAccessScope, collector, false, new PairProcessor() { + @Override + public boolean process(PsiReference psiReference, SearchRequestCollector collector) { + return processor.process(psiReference); + } + }); + } + public static void searchOptimized(@NotNull PsiElement element, @NotNull SearchScope searchScope, boolean ignoreAccessScope, + @NotNull SearchRequestCollector collector, final boolean inReadAction, PairProcessor processor) { + final SearchRequestCollector nested = new SearchRequestCollector(); + collector.searchQuery(new QuerySearchRequest(search(new SearchParameters(element, searchScope, ignoreAccessScope, nested)), nested, + inReadAction, processor)); + } + + } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 425e5c86fd81..878523e34bd8 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -31,6 +31,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -487,34 +488,62 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { myManager.getCacheManager().processFilesWithWord(processor, word, UsageSearchContext.IN_STRINGS, scope, true); } - public boolean processRequests(@NotNull SearchRequestCollector request, Processor processor) { - final MultiMap, PsiSearchRequest> singles = new MultiMap, PsiSearchRequest>(); - final List>> customs = new ArrayList>>(); - distributePrimitives(request, singles, customs); - - if (!processRequestsOptimized(singles, processor)) { - return false; + private static class RequestWithProcessor extends Pair> { + private RequestWithProcessor(PsiSearchRequest first, Processor second) { + super(first, second); } - - for (Processor> custom : customs) { - if (!custom.process(processor)) { - return false; - } - } - - return true; - } - private boolean processRequestsOptimized(MultiMap, PsiSearchRequest> singles, final Processor consumer) { + public boolean processRequests(@NotNull SearchRequestCollector collector, Processor processor) { + Map> collectors = new HashMap>(); + collectors.put(collector, processor); + + appendCollectorsFromQueryRequests(collectors); + + do { + final MultiMap, RequestWithProcessor> singles = new MultiMap, RequestWithProcessor>(); + final List> customs = new ArrayList>(); + distributePrimitives(collectors, singles, customs); + + if (!processRequestsOptimized(singles)) { + return false; + } + + for (Computable custom : customs) { + if (!custom.compute()) { + return false; + } + } + } while (appendCollectorsFromQueryRequests(collectors)); + + return true; + } + + private static boolean appendCollectorsFromQueryRequests(Map> collectors) { + boolean changed = false; + LinkedList queue = new LinkedList(collectors.keySet()); + while (!queue.isEmpty()) { + final SearchRequestCollector each = queue.removeFirst(); + for (QuerySearchRequest request : each.takeQueryRequests()) { + request.runQuery(); + collectors.put(request.collector, request.processor); + queue.addLast(request.collector); + changed = true; + } + } + return changed; + } + + private boolean processRequestsOptimized(MultiMap, RequestWithProcessor> singles) { if (singles.isEmpty()) { return true; } if (singles.size() == 1) { - final Collection requests = singles.get(singles.keySet().iterator().next()); + final Collection requests = singles.get(singles.keySet().iterator().next()); if (requests.size() == 1) { - return processSingleRequest(requests.iterator().next(), consumer); + final RequestWithProcessor theOnly = requests.iterator().next(); + return processSingleRequest(theOnly.first, theOnly.second); } } @@ -524,13 +553,17 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { progress.setText(PsiBundle.message("psi.scanning.files.progress")); } - final MultiMap candidateFiles = collectFiles(singles); + final MultiMap candidateFiles = collectFiles(singles); - final Map searchers = new HashMap(); + if (candidateFiles.isEmpty()) { + return true; + } + + final Map searchers = new HashMap(); final Set allWords = new TreeSet(); - for (PsiSearchRequest singleRequest : candidateFiles.values()) { - searchers.put(singleRequest, new StringSearcher(singleRequest.word, singleRequest.caseSensitive, true)); - allWords.add(singleRequest.word); + for (RequestWithProcessor singleRequest : candidateFiles.values()) { + searchers.put(singleRequest, new StringSearcher(singleRequest.first.word, singleRequest.first.caseSensitive, true)); + allWords.add(singleRequest.first.word); } if (progress != null) { @@ -540,9 +573,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return processPsiFileRoots(progress, new ArrayList(candidateFiles.keySet()), new Processor() { public boolean process(PsiElement psiRoot) { final VirtualFile vfile = psiRoot.getContainingFile().getVirtualFile(); - for (final PsiSearchRequest singleRequest : candidateFiles.get(vfile)) { + for (final RequestWithProcessor singleRequest : candidateFiles.get(vfile)) { StringSearcher searcher = searchers.get(singleRequest); - if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest, consumer), psiRoot, searcher, false, progress)) { + if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest.first, singleRequest.second), psiRoot, searcher, false, progress)) { return false; } } @@ -565,18 +598,18 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }; } - private MultiMap collectFiles(MultiMap, PsiSearchRequest> singles) { + private MultiMap collectFiles(MultiMap, RequestWithProcessor> singles) { final ProjectFileIndex index = ProjectRootManager.getInstance(myManager.getProject()).getFileIndex(); - final MultiMap result = new MultiMap(); + final MultiMap result = new MultiMap(); for (Set key : singles.keySet()) { - final Collection data = singles.get(key); + final Collection data = singles.get(key); GlobalSearchScope commonScope = uniteScopes(data); - MultiMap intersection = null; + MultiMap intersection = null; boolean first = true; for (IdIndexEntry entry : key) { - final MultiMap local = findFilesWithIndexEntry(entry, index, data, commonScope); + final MultiMap local = findFilesWithIndexEntry(entry, index, data, commonScope); if (first) { intersection = local; first = false; @@ -593,31 +626,34 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return result; } - private static GlobalSearchScope uniteScopes(Collection requests) { + private static GlobalSearchScope uniteScopes(Collection requests) { GlobalSearchScope commonScope = null; - for (PsiSearchRequest r : requests) { - final GlobalSearchScope scope = (GlobalSearchScope)r.searchScope; + for (RequestWithProcessor r : requests) { + final GlobalSearchScope scope = (GlobalSearchScope)r.first.searchScope; commonScope = commonScope == null ? scope : commonScope.uniteWith(scope); } assert commonScope != null; return commonScope; } - private static MultiMap findFilesWithIndexEntry(final IdIndexEntry entry, + private static MultiMap findFilesWithIndexEntry(final IdIndexEntry entry, final ProjectFileIndex index, - final Collection data, + final Collection data, final GlobalSearchScope commonScope) { - final MultiMap local = new MultiMap(); + final MultiMap local = new MultiMap(); ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { + ProgressManager.checkCanceled(); FileBasedIndex.getInstance().processValues(IdIndex.NAME, entry, null, new FileBasedIndex.ValueProcessor() { public boolean process(VirtualFile file, Integer value) { + ProgressManager.checkCanceled(); if (!IndexCacheManagerImpl.shouldBeFound(file, index)) { return true; } int mask = value.intValue(); - for (PsiSearchRequest single : data) { - if ((mask & single.searchContext) != 0 && ((GlobalSearchScope)single.searchScope).contains(file)) { + for (RequestWithProcessor single : data) { + final PsiSearchRequest request = single.first; + if ((mask & request.searchContext) != 0 && ((GlobalSearchScope)request.searchScope).contains(file)) { local.putValue(file, single); } } @@ -630,27 +666,38 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return local; } - private void distributePrimitives(SearchRequestCollector request, - MultiMap, PsiSearchRequest> singles, - List>> customs) { - for (final PsiSearchRequest primitive : request.getSearchRequests()) { - final SearchScope scope = primitive.searchScope; - if (scope instanceof LocalSearchScope) { - customs.add(new Processor>() { - public boolean process(Processor processor) { - return processSingleRequest(primitive, processor); + private void distributePrimitives(final Map> collectors, + MultiMap, RequestWithProcessor> singles, + List> customs) { + for (final SearchRequestCollector collector : collectors.keySet()) { + final Processor processor = collectors.get(collector); + for (final PsiSearchRequest primitive : collector.takeSearchRequests()) { + final SearchScope scope = primitive.searchScope; + if (scope instanceof LocalSearchScope) { + customs.add(new Computable() { + @Override + public Boolean compute() { + return processSingleRequest(primitive, processor); + } + }); + } else { + final List words = StringUtil.getWordsIn(primitive.word); + final Set key = new HashSet(words.size() * 2); + for (String word : words) { + key.add(new IdIndexEntry(word, primitive.caseSensitive)); + } + singles.putValue(key, new RequestWithProcessor(primitive, processor)); + } + } + for (final Processor> customAction : collector.takeCustomSearchActions()) { + customs.add(new Computable() { + @Override + public Boolean compute() { + return customAction.process(processor); } }); - } else { - final List words = StringUtil.getWordsIn(primitive.word); - final Set key = new HashSet(words.size() * 2); - for (String word : words) { - key.add(new IdIndexEntry(word, primitive.caseSensitive)); - } - singles.putValue(key, primitive); } } - customs.addAll(request.getCustomSearchActions()); } private boolean processSingleRequest(PsiSearchRequest single, Processor consumer) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java index 138a9034642a..1e8ee89c70ff 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/ConstructorReferencesSearcher.java @@ -15,26 +15,30 @@ */ package org.jetbrains.plugins.groovy.findUsages; +import com.intellij.openapi.application.QueryExecutorBase; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiReference; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; -import com.intellij.util.QueryExecutor; /** * @author ven */ -public class ConstructorReferencesSearcher implements QueryExecutor { - public boolean execute(ReferencesSearch.SearchParameters queryParameters, final Processor consumer) { +public class ConstructorReferencesSearcher extends QueryExecutorBase { + protected ConstructorReferencesSearcher() { + super(true); + } + + @Override + public void processQuery(ReferencesSearch.SearchParameters queryParameters, Processor consumer) { final PsiElement element = queryParameters.getElementToSearch(); if (element instanceof PsiMethod) { final PsiMethod method = (PsiMethod)element; if (method.isConstructor()) { - return GroovyConstructorUsagesSearchHelper.execute(method, queryParameters.getScope(), consumer); + GroovyConstructorUsagesSearcher.processConstructorUsages(method, queryParameters.getScope(), consumer, queryParameters.getOptimizer(), true); } } - return true; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java deleted file mode 100644 index e44211b20192..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearchHelper.java +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Copyright 2000-2009 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 org.jetbrains.plugins.groovy.findUsages; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadActionProcessor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.NullableComputable; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; -import com.intellij.psi.impl.light.LightMemberReference; -import com.intellij.psi.search.DelegatingGlobalSearchScope; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.SearchScope; -import com.intellij.psi.search.searches.AnnotatedElementsSearch; -import com.intellij.psi.search.searches.DirectClassInheritorsSearch; -import com.intellij.psi.search.searches.MethodReferencesSearch; -import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Processor; -import com.intellij.util.containers.ConcurrentHashSet; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.groovy.GroovyFileType; -import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; -import org.jetbrains.plugins.groovy.gpp.GppTypeConverter; -import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; -import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; -import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; -import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; -import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; -import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; -import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; -import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -/** - * @author Maxim.Medvedev - * Date: May 2, 2009 3:48:53 PM - */ -public class GroovyConstructorUsagesSearchHelper { - private GroovyConstructorUsagesSearchHelper() { - } - - public static boolean execute(final PsiMethod constructor, final SearchScope searchScope, final Processor consumer) { - if (!constructor.isConstructor()) return true; - - SearchScope onlyGroovy = searchScope; - if (onlyGroovy instanceof GlobalSearchScope) { - onlyGroovy = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)onlyGroovy, GroovyFileType.GROOVY_FILE_TYPE); - } - - final PsiClass clazz = ApplicationManager.getApplication().runReadAction(new NullableComputable() { - public PsiClass compute() { - return constructor.getContainingClass(); - } - }); - if (clazz == null) return true; - - - //enum constants - if (!ApplicationManager.getApplication().runReadAction(new Computable() { - public Boolean compute() { - if (!clazz.isEnum()) return true; - if (!(clazz instanceof GroovyPsiElement)) return true; - final PsiField[] fields = clazz.getFields(); - for (PsiField field : fields) { - if (field instanceof GrEnumConstant) { - final PsiReference ref = field.getReference(); - if (ref.isReferenceTo(constructor)) { - if (!consumer.process(ref)) return false; - } - } - } - return true; - } - })) { - return false; - } - - - final Set processedMethods = new ConcurrentHashSet(); - ReferencesSearch.search(clazz, searchScope, true).forEach(new Processor() { - @Override - public boolean process(PsiReference ref) { - return processClassReference(ref, clazz, constructor, consumer, processedMethods, searchScope); - } - }); - - //this() - if (clazz instanceof GrTypeDefinition) { - if (!processConstructors(constructor, consumer, clazz, true)) { - return false; - } - } - //super : does not work now, need to invent a way for it to work without repository - if (!DirectClassInheritorsSearch.search(clazz, onlyGroovy).forEach(new Processor() { - public boolean process(PsiClass inheritor) { - if (inheritor instanceof GrTypeDefinition) { - if (!processConstructors(constructor, consumer, inheritor, false)) return false; - } - return true; - } - })) { - return false; - } - - return true; - } - - @Nullable - private static PsiMethod getMethodToSearchForCallsWithLiteralArguments(PsiElement element, PsiClass clazz, Set processedMethods) { - final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class); - if (parameter != null) { - final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class); - if (method != null && processedMethods.add(method) && Arrays.asList(method.getParameterList().getParameters()).contains(parameter)) { - final PsiType parameterType = parameter.getType(); - if (parameterType instanceof PsiClassType) { - if (method.getManager().areElementsEquivalent(clazz, ((PsiClassType)parameterType).resolve())) { - return method; - } - } - } - } - return null; - } - - private static boolean processClassReference(final PsiReference ref, - final PsiClass clazz, - final PsiMethod constructor, - final Processor consumer, - final Set processedMethods, SearchScope scope) { - final PsiElement element = ref.getElement(); - if (element instanceof GrCodeReferenceElement) { - if (!ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - return processGroovyConstructorUsages((GrCodeReferenceElement)element, constructor, consumer, ref); - } - })) { - return false; - } - - } - - final PsiMethod method = ApplicationManager.getApplication().runReadAction(new Computable() { - public PsiMethod compute() { - return getMethodToSearchForCallsWithLiteralArguments(element, clazz, processedMethods); - } - }); - if (method != null) { - return MethodReferencesSearch.search(method, getGppScope(clazz.getProject()).intersectWith(scope), true).forEach(new ReadActionProcessor() { - @Override - public boolean processInReadAction(PsiReference psiReference) { - if (psiReference instanceof GrReferenceElement) { - final PsiElement parent = ((GrReferenceElement)psiReference).getParent(); - if (parent instanceof GrCall) { - final GrArgumentList argList = ((GrCall)parent).getArgumentList(); - if (argList != null) { - boolean checkedTypedContext = false; - - for (GrExpression argument : argList.getExpressionArguments()) { - if (argument instanceof GrListOrMap && !((GrListOrMap)argument).isMap()) { - if (!checkedTypedContext) { - if (!GppTypeConverter.hasTypedContext(parent)) { - return true; - } - checkedTypedContext = true; - } - - for (PsiType psiType : GroovyExpectedTypesProvider.getDefaultExpectedTypes(argument)) { - if (psiType instanceof PsiClassType && - clazz.getManager().areElementsEquivalent(clazz,((PsiClassType)psiType).resolve()) && - !checkListInstantiation(constructor, consumer, (GrListOrMap)argument, (PsiClassType)psiType)) { - return false; - } - } - } - } - } - } - } - return true; - } - }); - } - - return true; - } - - private static GlobalSearchScope getGppScope(final Project project) { - return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider() { - @Override - public Result compute() { - return Result.create(calcGppScope(project), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); - } - }); - } - - private static boolean processGroovyConstructorUsages(GrCodeReferenceElement element, - final PsiMethod constructor, - final Processor consumer, - PsiReference ref) { - PsiElement parent = element.getParent(); - - if (parent instanceof GrAnonymousClassDefinition) { - parent = parent.getParent(); - } - if (parent instanceof GrNewExpression) { - final PsiMethod resolvedConstructor = ((GrNewExpression)parent).resolveConstructor(); - if (constructor.getManager().areElementsEquivalent(resolvedConstructor, constructor) && !consumer.process(ref)) { - return false; - } - } - else if (parent instanceof GrTypeElement) { - final GrTypeElement typeElement = (GrTypeElement)parent; - - final PsiElement grandpa = typeElement.getParent(); - if (grandpa instanceof GrVariableDeclaration) { - final GrVariable[] vars = ((GrVariableDeclaration)grandpa).getVariables(); - if (vars.length == 1) { - final GrVariable variable = vars[0]; - if (!checkListInstantiation(constructor, consumer, variable.getInitializerGroovy(), typeElement)) { - return false; - } - } - } - else if (grandpa instanceof GrMethod) { - final GrMethod method = (GrMethod)grandpa; - if (typeElement == method.getReturnTypeElementGroovy()) { - ControlFlowUtils.visitAllExitPoints(method.getBlock(), new ControlFlowUtils.ExitPointVisitor() { - @Override - public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { - if (!checkListInstantiation(constructor, consumer, returnValue, typeElement)) { - return false; - } - return true; - } - }); - } - } - else if (grandpa instanceof GrTypeCastExpression) { - final GrTypeCastExpression cast = (GrTypeCastExpression)grandpa; - if (cast.getCastTypeElement() == typeElement && - !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { - return false; - } - } - else if (grandpa instanceof GrSafeCastExpression) { - final GrSafeCastExpression cast = (GrSafeCastExpression)grandpa; - if (cast.getCastTypeElement() == typeElement && - !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { - return false; - } - } - } - return true; - } - - private static GlobalSearchScope calcGppScope(Project project) { - final GlobalSearchScope allScope = GlobalSearchScope.allScope(project); - final GlobalSearchScope maximal = GlobalSearchScope.getScopeRestrictedByFileTypes(allScope, GroovyFileType.GROOVY_FILE_TYPE); - GlobalSearchScope gppExtensions = new DelegatingGlobalSearchScope(maximal) { - @Override - public boolean contains(VirtualFile file) { - return super.contains(file) && GppTypeConverter.isGppExtension(file.getExtension()); - } - }; - final PsiClass typed = JavaPsiFacade.getInstance(project).findClass(GppTypeConverter.GROOVY_LANG_TYPED, allScope); - if (typed != null) { - final Set files = new HashSet(); - AnnotatedElementsSearch.searchElements(typed, maximal, PsiModifierListOwner.class).forEach(new Processor() { - @Override - public boolean process(PsiModifierListOwner occurrence) { - ContainerUtil.addIfNotNull(occurrence.getContainingFile().getVirtualFile(), files); - return true; - } - }); - - GlobalSearchScope withTypedAnno = GlobalSearchScope.filesScope(project, files); - return withTypedAnno.union(gppExtensions); - } - - return gppExtensions; - } - - private static boolean checkListInstantiation(PsiMethod constructor, - Processor consumer, - GrExpression expression, final GrTypeElement typeElement) { - if (expression instanceof GrListOrMap) { - final GrListOrMap list = (GrListOrMap)expression; - if (!list.isMap()) { - final PsiType expectedType = typeElement.getType(); - if (expectedType instanceof PsiClassType) { - return checkListInstantiation(constructor, consumer, list, (PsiClassType)expectedType); - } - } - } - return true; - } - - private static boolean checkListInstantiation(PsiMethod constructor, - Processor consumer, - GrListOrMap list, - PsiClassType expectedType) { - final PsiType listType = list.getType(); - if (listType instanceof GrTupleType) { - for (GroovyResolveResult candidate : PsiUtil.getConstructorCandidates(expectedType, ((GrTupleType)listType).getComponentTypes(), list)) { - if (constructor.getManager().areElementsEquivalent(candidate.getElement(), constructor)) { - if (!consumer.process(PsiReferenceBase.createSelfReference(list, TextRange.from(0, list.getTextLength()), constructor))) { - return false; - } - } - } - } - return true; - } - - private static boolean processConstructors(final PsiMethod constructor, final Processor consumer, final PsiClass clazz, - final boolean processThisRefs) { - return ApplicationManager.getApplication().runReadAction(new Computable() { - public Boolean compute() { - return processClassConstructors(clazz, constructor, consumer, processThisRefs); - } - }); - } - - private static boolean processClassConstructors(PsiClass clazz, - PsiMethod searchedConstructor, - Processor consumer, - boolean processThisRefs) { - final PsiMethod[] constructors = clazz.getConstructors(); - if (constructors.length == 0) { - processImplicitConstructorCall(clazz, consumer, searchedConstructor); - } - for (PsiMethod constructor : constructors) { - final GrOpenBlock block = ((GrMethod)constructor).getBlock(); - if (block != null) { - final GrStatement[] statements = block.getStatements(); - if (statements.length > 0 && statements[0] instanceof GrConstructorInvocation) { - final GrConstructorInvocation invocation = (GrConstructorInvocation)statements[0]; - if (invocation.isThisCall() == processThisRefs && - invocation.getManager().areElementsEquivalent(invocation.resolveConstructor(), searchedConstructor) && - !consumer.process(invocation)) { - return false; - } - } - else { - processImplicitConstructorCall(constructor, consumer, searchedConstructor); - } - } - } - return true; - } - - private static void processImplicitConstructorCall(final PsiMember usage, - final Processor processor, - final PsiMethod constructor) { - if (constructor instanceof GrMethod) { - GrParameter[] grParameters = (GrParameter[])constructor.getParameterList().getParameters(); - if (grParameters.length > 0 && !grParameters[0].isOptional()) return; - } - else if (constructor.getParameterList().getParameters().length > 0) return; - - - PsiManager manager = constructor.getManager(); - if (manager.areElementsEquivalent(usage, constructor) || manager.areElementsEquivalent(constructor.getContainingClass(), usage.getContainingClass())) return; - processor.process(new LightMemberReference(manager, usage, PsiSubstitutor.EMPTY) { - public PsiElement getElement() { - return usage; - } - - public TextRange getRangeInElement() { - if (usage instanceof PsiClass) { - PsiIdentifier identifier = ((PsiClass)usage).getNameIdentifier(); - if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength()); - } - else if (usage instanceof PsiMethod) { - PsiIdentifier identifier = ((PsiMethod)usage).getNameIdentifier(); - if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength()); - } - return super.getRangeInElement(); - } - }); - - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java index da1ef93bd10f..15642f8602ac 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyConstructorUsagesSearcher.java @@ -16,20 +16,399 @@ package org.jetbrains.plugins.groovy.findUsages; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiReference; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.QueryExecutorBase; +import com.intellij.openapi.application.ReadActionProcessor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.impl.light.LightMemberReference; +import com.intellij.psi.search.DelegatingGlobalSearchScope; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.SearchRequestCollector; import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.AnnotatedElementsSearch; +import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.PairProcessor; import com.intellij.util.Processor; -import com.intellij.util.QueryExecutor; +import com.intellij.util.containers.ConcurrentHashSet; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.gpp.GppTypeConverter; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; +import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * @author Maxim.Medvedev */ -public class GroovyConstructorUsagesSearcher implements QueryExecutor { - public boolean execute(MethodReferencesSearch.SearchParameters p, final Processor consumer) { - final PsiMethod method = p.getMethod(); - final SearchScope searchScope = p.getScope(); - return GroovyConstructorUsagesSearchHelper.execute(method, searchScope, consumer); +public class GroovyConstructorUsagesSearcher extends QueryExecutorBase { + public GroovyConstructorUsagesSearcher() { + super(true); } + + @Override + public void processQuery(MethodReferencesSearch.SearchParameters p, Processor consumer) { + processConstructorUsages(p.getMethod(), p.getScope(), consumer, p.getOptimizer(), true); + } + + static void processConstructorUsages(final PsiMethod constructor, final SearchScope searchScope, final Processor consumer, final SearchRequestCollector collector, final boolean searchGppCalls) { + if (!constructor.isConstructor()) return; + + SearchScope onlyGroovy = searchScope; + if (onlyGroovy instanceof GlobalSearchScope) { + onlyGroovy = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)onlyGroovy, GroovyFileType.GROOVY_FILE_TYPE); + } + + final PsiClass clazz = constructor.getContainingClass(); + if (clazz == null) return; + + + if (clazz.isEnum() && clazz instanceof GroovyPsiElement) { + for (PsiField field : clazz.getFields()) { + if (field instanceof GrEnumConstant) { + final PsiReference ref = field.getReference(); + if (ref != null && ref.isReferenceTo(constructor)) { + if (!consumer.process(ref)) return; + } + } + } + } + + final Set processedMethods = new ConcurrentHashSet(); + + ReferencesSearch.searchOptimized(clazz, searchScope, true, collector, true, new PairProcessor() { + @Override + public boolean process(PsiReference ref, SearchRequestCollector collector) { + final PsiElement element = ref.getElement(); + if (element instanceof GrCodeReferenceElement) { + if (!processGroovyConstructorUsages((GrCodeReferenceElement)element, constructor, consumer, ref, !searchGppCalls)) { + return false; + } + } + + if (searchGppCalls) { + final PsiMethod method = getMethodToSearchForCallsWithLiteralArguments(element, clazz); + if (method != null && processedMethods.add(method)) { + processGppMethodCalls(clazz, constructor, consumer, searchScope, collector, method); + } + } + return true; + } + }); + + //this() + if (clazz instanceof GrTypeDefinition) { + if (!processConstructors(constructor, consumer, clazz, true)) { + return; + } + } + //super() + DirectClassInheritorsSearch.search(clazz, onlyGroovy).forEach(new Processor() { + public boolean process(PsiClass inheritor) { + if (inheritor instanceof GrTypeDefinition) { + if (!processConstructors(constructor, consumer, inheritor, false)) return false; + } + return true; + } + }); + } + + @Nullable + static PsiMethod getMethodToSearchForCallsWithLiteralArguments(PsiElement element, PsiClass targetClass) { + final PsiParameter parameter = PsiTreeUtil.getParentOfType(element, PsiParameter.class); + if (parameter != null) { + final PsiMethod method = PsiTreeUtil.getParentOfType(parameter, PsiMethod.class); + if (method != null && Arrays.asList(method.getParameterList().getParameters()).contains(parameter)) { + final PsiType parameterType = parameter.getType(); + if (parameterType instanceof PsiClassType) { + if (method.getManager().areElementsEquivalent(targetClass, ((PsiClassType)parameterType).resolve())) { + return method; + } + } + } + } + return null; + } + + private static void processGppMethodCalls(final PsiClass targetClass, + final PsiMethod originalTarget, + final Processor originalProcessor, + SearchScope scope, + SearchRequestCollector originalCollector, @NotNull PsiMethod currentTarget) { + final SearchScope gppScope = getGppScope(targetClass.getProject()).intersectWith(scope); + final ReadActionProcessor gppCallProcessor = new ReadActionProcessor() { + @Override + public boolean processInReadAction(PsiReference psiReference) { + if (psiReference instanceof GrReferenceElement) { + final PsiElement parent = ((GrReferenceElement)psiReference).getParent(); + if (parent instanceof GrCall) { + final GrArgumentList argList = ((GrCall)parent).getArgumentList(); + if (argList != null) { + boolean checkedTypedContext = false; + + for (GrExpression argument : argList.getExpressionArguments()) { + if (argument instanceof GrListOrMap && !((GrListOrMap)argument).isMap()) { + if (!checkedTypedContext) { + if (!GppTypeConverter.hasTypedContext(parent)) { + return true; + } + checkedTypedContext = true; + } + + for (PsiType psiType : GroovyExpectedTypesProvider.getDefaultExpectedTypes(argument)) { + if (psiType instanceof PsiClassType && + targetClass.getManager().areElementsEquivalent(targetClass, ((PsiClassType)psiType).resolve()) && + !checkListInstantiation(originalTarget, originalProcessor, (GrListOrMap)argument, (PsiClassType)psiType)) { + return false; + } + } + } + } + } + } + } + return true; + } + }; + if (currentTarget.isConstructor()) { + processConstructorUsages(currentTarget, gppScope, gppCallProcessor, originalCollector, false); + } + else { + MethodReferencesSearch.searchOptimized(currentTarget, gppScope, true, originalCollector, gppCallProcessor); + } + } + + static GlobalSearchScope getGppScope(final Project project) { + return CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider() { + @Override + public Result compute() { + return Result.create(calcGppScope(project), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + } + }); + } + + static boolean processGroovyConstructorUsages(GrCodeReferenceElement element, + final PsiMethod constructor, + final Processor consumer, + PsiReference ref, boolean usualCallsOnly) { + PsiElement parent = element.getParent(); + + if (parent instanceof GrAnonymousClassDefinition) { + parent = parent.getParent(); + } + if (parent instanceof GrNewExpression) { + final PsiMethod resolvedConstructor = ((GrNewExpression)parent).resolveConstructor(); + if (constructor.getManager().areElementsEquivalent(resolvedConstructor, constructor) && !consumer.process(ref)) { + return false; + } + } + else if (!usualCallsOnly && parent instanceof GrTypeElement) { + final GrTypeElement typeElement = (GrTypeElement)parent; + + final PsiElement grandpa = typeElement.getParent(); + if (grandpa instanceof GrVariableDeclaration) { + final GrVariable[] vars = ((GrVariableDeclaration)grandpa).getVariables(); + if (vars.length == 1) { + final GrVariable variable = vars[0]; + if (!checkListInstantiation(constructor, consumer, variable.getInitializerGroovy(), typeElement)) { + return false; + } + } + } + else if (grandpa instanceof GrMethod) { + final GrMethod method = (GrMethod)grandpa; + if (typeElement == method.getReturnTypeElementGroovy()) { + ControlFlowUtils.visitAllExitPoints(method.getBlock(), new ControlFlowUtils.ExitPointVisitor() { + @Override + public boolean visitExitPoint(Instruction instruction, @Nullable GrExpression returnValue) { + if (!checkListInstantiation(constructor, consumer, returnValue, typeElement)) { + return false; + } + return true; + } + }); + } + } + else if (grandpa instanceof GrTypeCastExpression) { + final GrTypeCastExpression cast = (GrTypeCastExpression)grandpa; + if (cast.getCastTypeElement() == typeElement && + !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { + return false; + } + } + else if (grandpa instanceof GrSafeCastExpression) { + final GrSafeCastExpression cast = (GrSafeCastExpression)grandpa; + if (cast.getCastTypeElement() == typeElement && + !checkListInstantiation(constructor, consumer, cast.getOperand(), typeElement)) { + return false; + } + } + } + return true; + } + + static GlobalSearchScope calcGppScope(Project project) { + final GlobalSearchScope allScope = GlobalSearchScope.allScope(project); + final GlobalSearchScope maximal = GlobalSearchScope.getScopeRestrictedByFileTypes(allScope, GroovyFileType.GROOVY_FILE_TYPE); + GlobalSearchScope gppExtensions = new DelegatingGlobalSearchScope(maximal) { + @Override + public boolean contains(VirtualFile file) { + return super.contains(file) && GppTypeConverter.isGppExtension(file.getExtension()); + } + }; + final PsiClass typed = JavaPsiFacade.getInstance(project).findClass(GppTypeConverter.GROOVY_LANG_TYPED, allScope); + if (typed != null) { + final Set files = new HashSet(); + AnnotatedElementsSearch.searchElements(typed, maximal, PsiModifierListOwner.class).forEach(new Processor() { + @Override + public boolean process(PsiModifierListOwner occurrence) { + ContainerUtil.addIfNotNull(occurrence.getContainingFile().getVirtualFile(), files); + return true; + } + }); + + GlobalSearchScope withTypedAnno = GlobalSearchScope.filesScope(project, files); + return withTypedAnno.union(gppExtensions); + } + + return gppExtensions; + } + + static boolean checkListInstantiation(PsiMethod constructor, + Processor consumer, + GrExpression expression, final GrTypeElement typeElement) { + if (expression instanceof GrListOrMap) { + final GrListOrMap list = (GrListOrMap)expression; + if (!list.isMap()) { + final PsiType expectedType = typeElement.getType(); + if (expectedType instanceof PsiClassType) { + return checkListInstantiation(constructor, consumer, list, (PsiClassType)expectedType); + } + } + } + return true; + } + + static boolean checkListInstantiation(PsiMethod constructor, + Processor consumer, + GrListOrMap list, + PsiClassType expectedType) { + final PsiType listType = list.getType(); + if (listType instanceof GrTupleType) { + for (GroovyResolveResult candidate : PsiUtil.getConstructorCandidates(expectedType, ((GrTupleType)listType).getComponentTypes(), list)) { + if (constructor.getManager().areElementsEquivalent(candidate.getElement(), constructor)) { + if (!consumer.process(PsiReferenceBase.createSelfReference(list, TextRange.from(0, list.getTextLength()), constructor))) { + return false; + } + } + } + } + return true; + } + + static boolean processConstructors(final PsiMethod constructor, final Processor consumer, final PsiClass clazz, + final boolean processThisRefs) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + public Boolean compute() { + return processClassConstructors(clazz, constructor, consumer, processThisRefs); + } + }); + } + + static boolean processClassConstructors(PsiClass clazz, + PsiMethod searchedConstructor, + Processor consumer, + boolean processThisRefs) { + final PsiMethod[] constructors = clazz.getConstructors(); + if (constructors.length == 0) { + processImplicitConstructorCall(clazz, consumer, searchedConstructor); + } + for (PsiMethod constructor : constructors) { + final GrOpenBlock block = ((GrMethod)constructor).getBlock(); + if (block != null) { + final GrStatement[] statements = block.getStatements(); + if (statements.length > 0 && statements[0] instanceof GrConstructorInvocation) { + final GrConstructorInvocation invocation = (GrConstructorInvocation)statements[0]; + if (invocation.isThisCall() == processThisRefs && + invocation.getManager().areElementsEquivalent(invocation.resolveConstructor(), searchedConstructor) && + !consumer.process(invocation)) { + return false; + } + } + else { + processImplicitConstructorCall(constructor, consumer, searchedConstructor); + } + } + } + return true; + } + + static void processImplicitConstructorCall(final PsiMember usage, + final Processor processor, + final PsiMethod constructor) { + if (constructor instanceof GrMethod) { + GrParameter[] grParameters = (GrParameter[])constructor.getParameterList().getParameters(); + if (grParameters.length > 0 && !grParameters[0].isOptional()) return; + } + else if (constructor.getParameterList().getParameters().length > 0) return; + + + PsiManager manager = constructor.getManager(); + if (manager.areElementsEquivalent(usage, constructor) || manager.areElementsEquivalent(constructor.getContainingClass(), usage.getContainingClass())) return; + processor.process(new LightMemberReference(manager, usage, PsiSubstitutor.EMPTY) { + public PsiElement getElement() { + return usage; + } + + public TextRange getRangeInElement() { + if (usage instanceof PsiClass) { + PsiIdentifier identifier = ((PsiClass)usage).getNameIdentifier(); + if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength()); + } + else if (usage instanceof PsiMethod) { + PsiIdentifier identifier = ((PsiMethod)usage).getNameIdentifier(); + if (identifier != null) return TextRange.from(identifier.getStartOffsetInParent(), identifier.getTextLength()); + } + return super.getRangeInElement(); + } + }); + + } + }