blob: ca7c26dbc81143cb204c200a095016cafd9cfcbf [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight;
import com.intellij.codeInsight.completion.AllClassesGetter;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.completion.PrefixMatcher;
import com.intellij.lang.Language;
import com.intellij.lang.StdLanguages;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.*;
import com.intellij.psi.util.proximity.PsiProximityComparator;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.Consumer;
import com.intellij.util.FilteredQuery;
import com.intellij.util.Processor;
import com.intellij.util.Query;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class CodeInsightUtil {
@Nullable
public static PsiExpression findExpressionInRange(PsiFile file, int startOffset, int endOffset) {
if (!file.getViewProvider().getLanguages().contains(StdLanguages.JAVA)) return null;
PsiExpression expression = findElementInRange(file, startOffset, endOffset, PsiExpression.class);
if (expression == null && findStatementsInRange(file, startOffset, endOffset).length == 0) {
PsiElement element2 = file.getViewProvider().findElementAt(endOffset - 1, StdLanguages.JAVA);
if (element2 instanceof PsiJavaToken) {
final PsiJavaToken token = (PsiJavaToken)element2;
final IElementType tokenType = token.getTokenType();
if (tokenType.equals(JavaTokenType.SEMICOLON)) {
expression = findElementInRange(file, startOffset, element2.getTextRange().getStartOffset(), PsiExpression.class);
}
}
}
if (expression == null && findStatementsInRange(file, startOffset, endOffset).length == 0) {
PsiElement element = PsiTreeUtil.skipSiblingsBackward(file.findElementAt(endOffset), PsiWhiteSpace.class);
if (element != null) {
element = PsiTreeUtil.skipSiblingsBackward(element.getLastChild(), PsiWhiteSpace.class, PsiComment.class);
if (element != null) {
final int newEndOffset = element.getTextRange().getEndOffset();
if (newEndOffset < endOffset) {
expression = findExpressionInRange(file, startOffset, newEndOffset);
}
}
}
}
if (expression instanceof PsiReferenceExpression && expression.getParent() instanceof PsiMethodCallExpression) return null;
return expression;
}
public static <T extends PsiElement> T findElementInRange(PsiFile file, int startOffset, int endOffset, Class<T> klass) {
return CodeInsightUtilCore.findElementInRange(file, startOffset, endOffset, klass, StdLanguages.JAVA);
}
@NotNull
public static PsiElement[] findStatementsInRange(@NotNull PsiFile file, int startOffset, int endOffset) {
Language language = findJavaOrLikeLanguage(file);
if (language == null) return PsiElement.EMPTY_ARRAY;
FileViewProvider viewProvider = file.getViewProvider();
PsiElement element1 = viewProvider.findElementAt(startOffset, language);
PsiElement element2 = viewProvider.findElementAt(endOffset - 1, language);
if (element1 instanceof PsiWhiteSpace) {
startOffset = element1.getTextRange().getEndOffset();
element1 = file.findElementAt(startOffset);
}
if (element2 instanceof PsiWhiteSpace) {
endOffset = element2.getTextRange().getStartOffset();
element2 = file.findElementAt(endOffset - 1);
}
if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY;
PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2);
if (parent == null) return PsiElement.EMPTY_ARRAY;
while (true) {
if (parent instanceof PsiStatement) {
parent = parent.getParent();
break;
}
if (parent instanceof PsiCodeBlock) break;
if (FileTypeUtils.isInServerPageFile(parent) && parent instanceof PsiFile) break;
if (parent instanceof PsiCodeFragment) break;
if (parent == null || parent instanceof PsiFile) return PsiElement.EMPTY_ARRAY;
parent = parent.getParent();
}
if (!parent.equals(element1)) {
while (!parent.equals(element1.getParent())) {
element1 = element1.getParent();
}
}
if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY;
if (!parent.equals(element2)) {
while (!parent.equals(element2.getParent())) {
element2 = element2.getParent();
}
}
if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY;
if (parent instanceof PsiCodeBlock && parent.getParent() instanceof PsiBlockStatement &&
element1 == ((PsiCodeBlock)parent).getLBrace() && element2 == ((PsiCodeBlock)parent).getRBrace()) {
return new PsiElement[]{parent.getParent()};
}
/*
if(parent instanceof PsiCodeBlock && parent.getParent() instanceof PsiBlockStatement) {
return new PsiElement[]{parent.getParent()};
}
*/
PsiElement[] children = parent.getChildren();
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
boolean flag = false;
for (PsiElement child : children) {
if (child.equals(element1)) {
flag = true;
}
if (flag && !(child instanceof PsiWhiteSpace)) {
array.add(child);
}
if (child.equals(element2)) {
break;
}
}
for (PsiElement element : array) {
if (!(element instanceof PsiStatement || element instanceof PsiWhiteSpace || element instanceof PsiComment)) {
return PsiElement.EMPTY_ARRAY;
}
}
return PsiUtilCore.toPsiElementArray(array);
}
@Nullable
public static Language findJavaOrLikeLanguage(@NotNull final PsiFile file) {
final Set<Language> languages = file.getViewProvider().getLanguages();
for (final Language language : languages) {
if (language == StdLanguages.JAVA) return language;
}
for (final Language language : languages) {
if (language.isKindOf(StdLanguages.JAVA)) return language;
}
return null;
}
public static void sortIdenticalShortNameClasses(PsiClass[] classes, @NotNull PsiReference context) {
if (classes.length <= 1) return;
PsiElement leaf = context.getElement().getFirstChild(); // the same proximity weighers are used in completion, where the leafness is critical
Arrays.sort(classes, new PsiProximityComparator(leaf));
}
public static PsiExpression[] findExpressionOccurrences(PsiElement scope, PsiExpression expr) {
List<PsiExpression> array = new ArrayList<PsiExpression>();
addExpressionOccurrences(RefactoringUtil.unparenthesizeExpression(expr), array, scope);
if (expr.isPhysical()) {
boolean found = false;
for (PsiExpression psiExpression : array) {
if (PsiTreeUtil.isAncestor(expr, psiExpression, false) || PsiTreeUtil.isAncestor(psiExpression, expr, false)) {
found = true;
break;
}
}
if (!found) array.add(expr);
}
return array.toArray(new PsiExpression[array.size()]);
}
private static void addExpressionOccurrences(PsiExpression expr, List<PsiExpression> array, PsiElement scope) {
PsiElement[] children = scope.getChildren();
for (PsiElement child : children) {
if (child instanceof PsiExpression) {
if (JavaPsiEquivalenceUtil.areExpressionsEquivalent(RefactoringUtil.unparenthesizeExpression((PsiExpression)child), expr)) {
array.add((PsiExpression)child);
continue;
}
}
addExpressionOccurrences(expr, array, child);
}
}
public static PsiExpression[] findReferenceExpressions(PsiElement scope, PsiElement referee) {
ArrayList<PsiElement> array = new ArrayList<PsiElement>();
if (scope != null) {
addReferenceExpressions(array, scope, referee);
}
return array.toArray(new PsiExpression[array.size()]);
}
private static void addReferenceExpressions(ArrayList<PsiElement> array, PsiElement scope, PsiElement referee) {
PsiElement[] children = scope.getChildren();
for (PsiElement child : children) {
if (child instanceof PsiReferenceExpression) {
PsiElement ref = ((PsiReferenceExpression)child).resolve();
if (ref != null && PsiEquivalenceUtil.areElementsEquivalent(ref, referee)) {
array.add(child);
}
}
addReferenceExpressions(array, child, referee);
}
}
public static Editor positionCursor(final Project project, PsiFile targetFile, PsiElement element) {
TextRange range = element.getTextRange();
int textOffset = range.getStartOffset();
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, targetFile.getVirtualFile(), textOffset);
return FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
}
public static boolean preparePsiElementsForWrite(@NotNull PsiElement... elements) {
return FileModificationService.getInstance().preparePsiElementsForWrite(Arrays.asList(elements));
}
public static void processSubTypes(PsiType psiType,
final PsiElement context,
boolean getRawSubtypes,
@NotNull final PrefixMatcher matcher,
Consumer<PsiType> consumer) {
int arrayDim = psiType.getArrayDimensions();
psiType = psiType.getDeepComponentType();
if (!(psiType instanceof PsiClassType)) return;
final Condition<String> shortNameCondition = new Condition<String>() {
@Override
public boolean value(String s) {
return matcher.prefixMatches(s);
}
};
final PsiClassType baseType = (PsiClassType)psiType;
final PsiClassType.ClassResolveResult baseResult =
ApplicationManager.getApplication().runReadAction(new Computable<PsiClassType.ClassResolveResult>() {
@Override
public PsiClassType.ClassResolveResult compute() {
return JavaCompletionUtil.originalize(baseType).resolveGenerics();
}
});
final PsiClass baseClass = baseResult.getElement();
final PsiSubstitutor baseSubstitutor = baseResult.getSubstitutor();
if(baseClass == null) return;
final GlobalSearchScope scope = ApplicationManager.getApplication().runReadAction(new Computable<GlobalSearchScope>() {
@Override
public GlobalSearchScope compute() {
return context.getResolveScope();
}
});
final Processor<PsiClass> inheritorsProcessor =
createInheritorsProcessor(context, baseType, arrayDim, getRawSubtypes, consumer, baseClass, baseSubstitutor);
addContextTypeArguments(context, baseType, inheritorsProcessor);
if (baseClass.hasModifierProperty(PsiModifier.FINAL)) return;
if (matcher.getPrefix().length() > 2) {
AllClassesGetter.processJavaClasses(matcher, context.getProject(), scope, new Processor<PsiClass>() {
@Override
public boolean process(PsiClass psiClass) {
if (psiClass.isInheritor(baseClass, true)) {
return inheritorsProcessor.process(psiClass);
}
return true;
}
});
} else {
final Query<PsiClass> baseQuery = ClassInheritorsSearch.search(
new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, false, false, shortNameCondition));
final Query<PsiClass> query = new FilteredQuery<PsiClass>(baseQuery, new Condition<PsiClass>() {
@Override
public boolean value(final PsiClass psiClass) {
return !(psiClass instanceof PsiTypeParameter);
}
});
query.forEach(inheritorsProcessor);
}
}
private static void addContextTypeArguments(final PsiElement context,
final PsiClassType baseType,
final Processor<PsiClass> inheritorsProcessor) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
Set<String> usedNames = ContainerUtil.newHashSet();
PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject());
PsiElement each = context;
while (true) {
PsiTypeParameterListOwner typed = PsiTreeUtil.getParentOfType(each, PsiTypeParameterListOwner.class);
if (typed == null) break;
for (PsiTypeParameter parameter : typed.getTypeParameters()) {
if (baseType.isAssignableFrom(factory.createType(parameter)) && usedNames.add(parameter.getName())) {
inheritorsProcessor.process(CompletionUtil.getOriginalOrSelf(parameter));
}
}
each = typed;
}
}
});
}
public static Processor<PsiClass> createInheritorsProcessor(final PsiElement context, final PsiClassType baseType,
final int arrayDim,
final boolean getRawSubtypes,
final Consumer<PsiType> result, @NotNull final PsiClass baseClass, final PsiSubstitutor baseSubstitutor) {
final PsiManager manager = context.getManager();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
final PsiResolveHelper resolveHelper = facade.getResolveHelper();
return new Processor<PsiClass>() {
@Override
public boolean process(final PsiClass inheritor) {
ProgressManager.checkCanceled();
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
@Override
public Boolean compute() {
if (!context.isValid() || !inheritor.isValid() || !facade.getResolveHelper().isAccessible(inheritor, context, null))
return true;
if (inheritor.getQualifiedName() == null &&
!manager.areElementsEquivalent(inheritor.getContainingFile(), context.getContainingFile().getOriginalFile())) {
return true;
}
if (JavaCompletionUtil.isInExcludedPackage(inheritor, false)) return true;
PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(baseClass, inheritor, PsiSubstitutor.EMPTY);
if (superSubstitutor == null) return true;
if (getRawSubtypes) {
result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim));
return true;
}
PsiSubstitutor inheritorSubstitutor = PsiSubstitutor.EMPTY;
for (PsiTypeParameter inheritorParameter : PsiUtil.typeParametersIterable(inheritor)) {
for (PsiTypeParameter baseParameter : PsiUtil.typeParametersIterable(baseClass)) {
final PsiType substituted = superSubstitutor.substitute(baseParameter);
PsiType arg = baseSubstitutor.substitute(baseParameter);
if (arg instanceof PsiWildcardType) {
PsiType bound = ((PsiWildcardType)arg).getBound();
arg = bound != null ? bound : ((PsiWildcardType)arg).getExtendsBound();
}
PsiType substitution = resolveHelper.getSubstitutionForTypeParameter(inheritorParameter,
substituted,
arg,
true,
PsiUtil.getLanguageLevel(context));
if (PsiType.NULL.equals(substitution) || substitution instanceof PsiWildcardType) continue;
if (substitution == null) {
result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim));
return true;
}
inheritorSubstitutor = inheritorSubstitutor.put(inheritorParameter, substitution);
break;
}
}
PsiType toAdd = createType(inheritor, inheritorSubstitutor, arrayDim);
if (baseType.isAssignableFrom(toAdd)) {
result.consume(toAdd);
}
return true;
}
}).booleanValue();
}
};
}
private static PsiType createType(PsiClass cls,
PsiSubstitutor currentSubstitutor,
int arrayDim) {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(cls.getProject()).getElementFactory();
PsiType newType = elementFactory.createType(cls, currentSubstitutor);
for(int i = 0; i < arrayDim; i++){
newType = newType.createArrayType();
}
return newType;
}
}