/* * 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 findElementInRange(PsiFile file, int startOffset, int endOffset, Class 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 array = new ArrayList(); 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 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 array = new ArrayList(); 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 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 array = new ArrayList(); if (scope != null) { addReferenceExpressions(array, scope, referee); } return array.toArray(new PsiExpression[array.size()]); } private static void addReferenceExpressions(ArrayList 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 consumer) { int arrayDim = psiType.getArrayDimensions(); psiType = psiType.getDeepComponentType(); if (!(psiType instanceof PsiClassType)) return; final Condition shortNameCondition = new Condition() { @Override public boolean value(String s) { return matcher.prefixMatches(s); } }; final PsiClassType baseType = (PsiClassType)psiType; final PsiClassType.ClassResolveResult baseResult = ApplicationManager.getApplication().runReadAction(new Computable() { @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() { @Override public GlobalSearchScope compute() { return context.getResolveScope(); } }); final Processor 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() { @Override public boolean process(PsiClass psiClass) { if (psiClass.isInheritor(baseClass, true)) { return inheritorsProcessor.process(psiClass); } return true; } }); } else { final Query baseQuery = ClassInheritorsSearch.search( new ClassInheritorsSearch.SearchParameters(baseClass, scope, true, false, false, shortNameCondition)); final Query query = new FilteredQuery(baseQuery, new Condition() { @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 inheritorsProcessor) { ApplicationManager.getApplication().runReadAction(new Runnable() { @Override public void run() { Set 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 createInheritorsProcessor(final PsiElement context, final PsiClassType baseType, final int arrayDim, final boolean getRawSubtypes, final Consumer 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() { @Override public boolean process(final PsiClass inheritor) { ProgressManager.checkCanceled(); return ApplicationManager.getApplication().runReadAction(new Computable() { @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; } }