blob: 48a4879a7bf4c1024b48a2949931b327faaa624a [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.util;
import com.intellij.find.FindManager;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesManager;
import com.intellij.find.findUsages.FindUsagesUtil;
import com.intellij.find.impl.FindManagerImpl;
import com.intellij.lang.ASTNode;
import com.intellij.lang.LanguageParserDefinitions;
import com.intellij.lang.ParserDefinition;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.*;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.PairProcessor;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class TextOccurrencesUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.TextOccurrencesUtil");
private TextOccurrencesUtil() {
}
public static void addTextOccurences(@NotNull PsiElement element,
@NotNull String stringToSearch,
@NotNull GlobalSearchScope searchScope,
@NotNull final Collection<UsageInfo> results,
@NotNull final UsageInfoFactory factory) {
processTextOccurences(element, stringToSearch, searchScope, new Processor<UsageInfo>() {
@Override
public boolean process(UsageInfo t) {
results.add(t);
return true;
}
}, factory);
}
public static boolean processTextOccurences(@NotNull final PsiElement element,
@NotNull String stringToSearch,
@NotNull GlobalSearchScope searchScope,
@NotNull final Processor<UsageInfo> processor,
@NotNull final UsageInfoFactory factory) {
PsiSearchHelper helper = ApplicationManager.getApplication().runReadAction(new Computable<PsiSearchHelper>() {
@Override
public PsiSearchHelper compute() {
return PsiSearchHelper.SERVICE.getInstance(element.getProject());
}
});
return helper.processUsagesInNonJavaFiles(element, stringToSearch, new PsiNonJavaFileReferenceProcessor() {
@Override
public boolean process(final PsiFile psiFile, final int startOffset, final int endOffset) {
try {
UsageInfo usageInfo = ApplicationManager.getApplication().runReadAction(new Computable<UsageInfo>() {
@Override
public UsageInfo compute() {
return factory.createUsageInfo(psiFile, startOffset, endOffset);
}
});
return usageInfo == null || processor.process(usageInfo);
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Exception e) {
LOG.error(e);
return true;
}
}
}, searchScope);
}
private static boolean processStringLiteralsContainingIdentifier(@NotNull String identifier, @NotNull SearchScope searchScope, PsiSearchHelper helper, final Processor<PsiElement> processor) {
TextOccurenceProcessor occurenceProcessor = new TextOccurenceProcessor() {
@Override
public boolean execute(@NotNull PsiElement element, int offsetInElement) {
final ParserDefinition definition = LanguageParserDefinitions.INSTANCE.forLanguage(element.getLanguage());
final ASTNode node = element.getNode();
if (definition != null && node != null && definition.getStringLiteralElements().contains(node.getElementType())) {
return processor.process(element);
}
return true;
}
};
return helper.processElementsWithWord(occurenceProcessor,
searchScope,
identifier,
UsageSearchContext.IN_STRINGS,
true);
}
public static boolean processUsagesInStringsAndComments(@NotNull final PsiElement element,
@NotNull final String stringToSearch,
final boolean ignoreReferences,
@NotNull final PairProcessor<PsiElement, TextRange> processor) {
PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(element.getProject());
SearchScope scope = helper.getUseScope(element);
scope = GlobalSearchScope.projectScope(element.getProject()).intersectWith(scope);
Processor<PsiElement> commentOrLiteralProcessor = new Processor<PsiElement>() {
@Override
public boolean process(PsiElement literal) {
return processTextIn(literal, stringToSearch, ignoreReferences, processor);
}
};
return processStringLiteralsContainingIdentifier(stringToSearch, scope, helper, commentOrLiteralProcessor) &&
helper.processCommentsContainingIdentifier(stringToSearch, scope, commentOrLiteralProcessor);
}
public static void addUsagesInStringsAndComments(@NotNull PsiElement element,
@NotNull String stringToSearch,
@NotNull final Collection<UsageInfo> results,
@NotNull final UsageInfoFactory factory) {
final Object lock = new Object();
processUsagesInStringsAndComments(element, stringToSearch, false, new PairProcessor<PsiElement, TextRange>() {
@Override
public boolean process(PsiElement commentOrLiteral, TextRange textRange) {
UsageInfo usageInfo = factory.createUsageInfo(commentOrLiteral, textRange.getStartOffset(), textRange.getEndOffset());
if (usageInfo != null) {
synchronized (lock) {
results.add(usageInfo);
}
}
return true;
}
});
}
private static boolean processTextIn(PsiElement scope, String stringToSearch, final boolean ignoreReferences, PairProcessor<PsiElement, TextRange> processor) {
String text = scope.getText();
for (int offset = 0; offset < text.length(); offset++) {
offset = text.indexOf(stringToSearch, offset);
if (offset < 0) break;
final PsiReference referenceAt = scope.findReferenceAt(offset);
if (!ignoreReferences && referenceAt != null
&& (referenceAt.resolve() != null || referenceAt instanceof PsiPolyVariantReference
&& ((PsiPolyVariantReference)referenceAt).multiResolve(true).length > 0)) continue;
if (offset > 0) {
char c = text.charAt(offset - 1);
if (Character.isJavaIdentifierPart(c) && c != '$') {
if (offset < 2 || text.charAt(offset - 2) != '\\') continue; //escape sequence
}
}
if (offset + stringToSearch.length() < text.length()) {
char c = text.charAt(offset + stringToSearch.length());
if (Character.isJavaIdentifierPart(c) && c != '$') {
continue;
}
}
TextRange textRange = new TextRange(offset, offset + stringToSearch.length());
if (!processor.process(scope, textRange)) {
return false;
}
offset += stringToSearch.length();
}
return true;
}
public static boolean isSearchTextOccurencesEnabled(@NotNull PsiElement element) {
final FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(element.getProject())).getFindUsagesManager();
final FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(element, true);
return FindUsagesUtil.isSearchForTextOccurrencesAvailable(element, false, handler);
}
public static void findNonCodeUsages(PsiElement element, String stringToSearch, boolean searchInStringsAndComments,
boolean searchInNonJavaFiles, String newQName, Collection<UsageInfo> results) {
if (searchInStringsAndComments || searchInNonJavaFiles) {
UsageInfoFactory factory = createUsageInfoFactory(element, newQName);
if (searchInStringsAndComments) {
addUsagesInStringsAndComments(element, stringToSearch, results, factory);
}
if (searchInNonJavaFiles) {
GlobalSearchScope projectScope = GlobalSearchScope.projectScope(element.getProject());
addTextOccurences(element, stringToSearch, projectScope, results, factory);
}
}
}
private static UsageInfoFactory createUsageInfoFactory(final PsiElement element,
final String newQName) {
return new UsageInfoFactory() {
@Override
public UsageInfo createUsageInfo(@NotNull PsiElement usage, int startOffset, int endOffset) {
int start = usage.getTextRange().getStartOffset();
return NonCodeUsageInfo.create(usage.getContainingFile(), start + startOffset, start + endOffset, element,
newQName);
}
};
}
public interface UsageInfoFactory {
@Nullable
UsageInfo createUsageInfo(@NotNull PsiElement usage, int startOffset, int endOffset);
}
}