Java tutorial
/* * 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 java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Set; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.codeInsight.completion.AllClassesGetter; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.lang.Language; import com.intellij.lang.java.JavaLanguage; 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.impl.source.PsiDiamondTypeElementImpl; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.util.TypeConversionUtil; 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; public class CodeInsightUtil { @Nullable public static PsiExpression findExpressionInRange(PsiFile file, int startOffset, int endOffset) { if (!file.getViewProvider().getLanguages().contains(JavaLanguage.INSTANCE)) 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, JavaLanguage.INSTANCE); 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 CodeInsightUtilBase.findElementInRange(file, startOffset, endOffset, klass, JavaLanguage.INSTANCE); } @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 (JspPsiUtil.isInJspFile(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 == JavaLanguage.INSTANCE) return language; } for (final Language language : languages) { if (language.isKindOf(JavaLanguage.INSTANCE)) 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 (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 boolean areExpressionsEquivalent(PsiExpression expr1, PsiExpression expr2) { return PsiEquivalenceUtil.areElementsEquivalent(expr1, expr2, new Comparator<PsiElement>() { @Override public int compare(PsiElement o1, PsiElement o2) { if (o1 instanceof PsiParameter && o2 instanceof PsiParameter && ((PsiParameter) o1).getDeclarationScope() instanceof PsiMethod) { return ((PsiParameter) o1).getName().compareTo(((PsiParameter) o2).getName()); } return 1; } }, new Comparator<PsiElement>() { @Override public int compare(PsiElement o1, PsiElement o2) { if (!o1.textMatches(o2)) return 1; if (o1 instanceof PsiDiamondTypeElementImpl && o2 instanceof PsiDiamondTypeElementImpl) { final PsiDiamondType.DiamondInferenceResult thisInferenceResult = new PsiDiamondTypeImpl( o1.getManager(), (PsiTypeElement) o1).resolveInferredTypes(); final PsiDiamondType.DiamondInferenceResult otherInferenceResult = new PsiDiamondTypeImpl( o2.getManager(), (PsiTypeElement) o2).resolveInferredTypes(); return thisInferenceResult.equals(otherInferenceResult) ? 0 : 1; } return 0; } }, null, false); } 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); 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); } } 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; } }