blob: 0cc9819c4574421123cddcea742feef7dea78682 [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.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypesProvider;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.ControlFlowUtil;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.javadoc.PsiDocTagValue;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.PackageWrapper;
import com.intellij.refactoring.introduceField.ElementToWorkOn;
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.HashSet;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class RefactoringUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.RefactoringUtil");
public static final int EXPR_COPY_SAFE = 0;
public static final int EXPR_COPY_UNSAFE = 1;
public static final int EXPR_COPY_PROHIBITED = 2;
private RefactoringUtil() {
}
public static boolean isSourceRoot(final PsiDirectory directory) {
if (directory.getManager() == null) return false;
final Project project = directory.getProject();
final VirtualFile virtualFile = directory.getVirtualFile();
final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(virtualFile);
return Comparing.equal(virtualFile, sourceRootForFile);
}
public static boolean isInStaticContext(PsiElement element, @Nullable final PsiClass aClass) {
return PsiUtil.getEnclosingStaticElement(element, aClass) != null;
}
public static boolean isResolvableType(PsiType type) {
return type.accept(new PsiTypeVisitor<Boolean>() {
public Boolean visitPrimitiveType(PsiPrimitiveType primitiveType) {
return Boolean.TRUE;
}
public Boolean visitArrayType(PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(PsiClassType classType) {
if (classType.resolve() == null) return Boolean.FALSE;
PsiType[] parameters = classType.getParameters();
for (PsiType parameter : parameters) {
if (parameter != null && !parameter.accept(this).booleanValue()) return Boolean.FALSE;
}
return Boolean.TRUE;
}
public Boolean visitWildcardType(PsiWildcardType wildcardType) {
if (wildcardType.getBound() != null) return wildcardType.getBound().accept(this);
return Boolean.TRUE;
}
}).booleanValue();
}
public static PsiElement replaceOccurenceWithFieldRef(PsiExpression occurrence, PsiField newField, PsiClass destinationClass)
throws IncorrectOperationException {
final PsiManager manager = destinationClass.getManager();
final String fieldName = newField.getName();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
final PsiElement element = occurrence.getUserData(ElementToWorkOn.PARENT);
final PsiVariable psiVariable = facade.getResolveHelper().resolveAccessibleReferencedVariable(fieldName, element != null ? element : occurrence);
final PsiElementFactory factory = facade.getElementFactory();
if (psiVariable != null && psiVariable.equals(newField)) {
return IntroduceVariableBase.replace(occurrence, factory.createExpressionFromText(fieldName, null), manager.getProject());
}
else {
final PsiReferenceExpression ref = (PsiReferenceExpression)factory.createExpressionFromText("this." + fieldName, null);
if (!occurrence.isValid()) return null;
if (newField.hasModifierProperty(PsiModifier.STATIC)) {
ref.setQualifierExpression(factory.createReferenceExpression(destinationClass));
}
return IntroduceVariableBase.replace(occurrence, ref, manager.getProject());
}
}
/**
* @see com.intellij.psi.codeStyle.CodeStyleManager#suggestUniqueVariableName(String, com.intellij.psi.PsiElement, boolean)
* Cannot use method from code style manager: a collision with fieldToReplace is not a collision
*/
public static String suggestUniqueVariableName(String baseName, PsiElement place, PsiField fieldToReplace) {
for(int index = 0;;index++) {
final String name = index > 0 ? baseName + index : baseName;
final PsiManager manager = place.getManager();
PsiResolveHelper helper = JavaPsiFacade.getInstance(manager.getProject()).getResolveHelper();
PsiVariable refVar = helper.resolveAccessibleReferencedVariable(name, place);
if (refVar != null && !manager.areElementsEquivalent(refVar, fieldToReplace)) continue;
final boolean[] found = {false};
place.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitClass(PsiClass aClass) {
}
@Override
public void visitVariable(PsiVariable variable) {
if (name.equals(variable.getName())) {
found[0] = true;
stopWalking();
}
}
});
if (found[0]) {
continue;
}
return name;
}
}
@Nullable
public static String suggestNewOverriderName(String oldOverriderName, String oldBaseName, String newBaseName) {
if (oldOverriderName.equals(oldBaseName)) {
return newBaseName;
}
int i;
if (oldOverriderName.startsWith(oldBaseName)) {
i = 0;
}
else {
i = StringUtil.indexOfIgnoreCase(oldOverriderName, oldBaseName, 0);
}
if (i >= 0) {
String newOverriderName = oldOverriderName.substring(0, i);
if (Character.isUpperCase(oldOverriderName.charAt(i))) {
newOverriderName += StringUtil.capitalize(newBaseName);
}
else {
newOverriderName += newBaseName;
}
final int j = i + oldBaseName.length();
if (j < oldOverriderName.length()) {
newOverriderName += oldOverriderName.substring(j);
}
return newOverriderName;
}
return null;
}
public static boolean hasOnDemandStaticImport(final PsiElement element, final PsiClass aClass) {
if (element.getContainingFile() instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile)element.getContainingFile()).getImportList();
if (importList != null) {
final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements();
for(PsiImportStaticStatement stmt: importStaticStatements) {
if (stmt.isOnDemand() && stmt.resolveTargetClass() == aClass) {
return true;
}
}
}
}
return false;
}
public static boolean hasStaticImportOn(final PsiElement expr, final PsiMember member) {
if (expr.getContainingFile() instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile)expr.getContainingFile()).getImportList();
if (importList != null) {
final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements();
for(PsiImportStaticStatement stmt: importStaticStatements) {
final PsiClass containingClass = member.getContainingClass();
final String referenceName = stmt.getReferenceName();
if (!stmt.isOnDemand() && stmt.resolveTargetClass() == containingClass && Comparing.strEqual(referenceName, member.getName())) {
if (member instanceof PsiMethod) {
return containingClass.findMethodsByName(referenceName, false).length == 1;
}
return true;
}
}
}
}
return false;
}
public static PsiElement replaceElementsWithMap(PsiElement replaceIn, final Map<PsiElement, PsiElement> elementsToReplace) throws IncorrectOperationException {
for(Map.Entry<PsiElement, PsiElement> e: elementsToReplace.entrySet()) {
if (e.getKey() == replaceIn) {
return e.getKey().replace(e.getValue());
}
e.getKey().replace(e.getValue());
}
return replaceIn;
}
public static PsiElement getVariableScope(PsiLocalVariable localVar) {
if (!(localVar instanceof ImplicitVariable)) {
return localVar.getParent().getParent();
}
else {
return ((ImplicitVariable)localVar).getDeclarationScope();
}
}
public static PsiElement getParentStatement(PsiElement place, boolean skipScopingStatements) {
PsiElement parent = place;
while (true) {
if (parent instanceof PsiStatement) break;
if (parent instanceof PsiExpression && parent.getParent() instanceof PsiLambdaExpression) return parent;
parent = parent.getParent();
if (parent == null) return null;
}
PsiElement parentStatement = parent;
parent = parentStatement instanceof PsiStatement ? parentStatement : parentStatement.getParent();
while (parent instanceof PsiStatement) {
if (!skipScopingStatements && ((parent instanceof PsiForStatement && parentStatement == ((PsiForStatement)parent).getBody()) || (
parent instanceof PsiForeachStatement && parentStatement == ((PsiForeachStatement)parent).getBody()) || (
parent instanceof PsiWhileStatement && parentStatement == ((PsiWhileStatement)parent).getBody()) || (
parent instanceof PsiIfStatement &&
(parentStatement == ((PsiIfStatement)parent).getThenBranch() || parentStatement == ((PsiIfStatement)parent).getElseBranch())))) {
return parentStatement;
}
parentStatement = parent;
parent = parent.getParent();
}
return parentStatement;
}
public static PsiElement getParentExpressionAnchorElement(PsiElement place) {
PsiElement parent = place.getUserData(ElementToWorkOn.PARENT);
if (place.getUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK) != null) return parent;
if (parent == null) parent = place;
while (true) {
if (isExpressionAnchorElement(parent)) return parent;
parent = parent.getParent();
if (parent == null) return null;
}
}
public static boolean isExpressionAnchorElement(PsiElement element) {
if (element instanceof PsiDeclarationStatement && element.getParent() instanceof PsiForStatement) return false;
return element instanceof PsiStatement || element instanceof PsiClassInitializer || element instanceof PsiField ||
element instanceof PsiMethod;
}
/**
* @param expression
* @return loop body if expression is part of some loop's condition or for loop's increment part
* null otherwise
*/
public static PsiElement getLoopForLoopCondition(PsiExpression expression) {
PsiExpression outermost = expression;
while (outermost.getParent() instanceof PsiExpression) {
outermost = (PsiExpression)outermost.getParent();
}
if (outermost.getParent() instanceof PsiForStatement) {
final PsiForStatement forStatement = (PsiForStatement)outermost.getParent();
if (forStatement.getCondition() == outermost) {
return forStatement;
}
else {
return null;
}
}
if (outermost.getParent() instanceof PsiExpressionStatement && outermost.getParent().getParent() instanceof PsiForStatement) {
final PsiForStatement forStatement = (PsiForStatement)outermost.getParent().getParent();
if (forStatement.getUpdate() == outermost.getParent()) {
return forStatement;
}
else {
return null;
}
}
if (outermost.getParent() instanceof PsiWhileStatement) {
return outermost.getParent();
}
if (outermost.getParent() instanceof PsiDoWhileStatement) {
return outermost.getParent();
}
return null;
}
public static PsiClass getThisResolveClass(final PsiReferenceExpression place) {
final JavaResolveResult resolveResult = place.advancedResolve(false);
final PsiElement scope = resolveResult.getCurrentFileResolveScope();
if (scope instanceof PsiClass) {
return (PsiClass)scope;
}
return null;
}
public static PsiCall getEnclosingConstructorCall(PsiJavaCodeReferenceElement ref) {
PsiElement parent = ref.getParent();
if (ref instanceof PsiReferenceExpression && parent instanceof PsiMethodCallExpression) return (PsiCall)parent;
if (parent instanceof PsiAnonymousClass) {
parent = parent.getParent();
}
return parent instanceof PsiNewExpression ? (PsiNewExpression)parent : null;
}
public static PsiMethod getEnclosingMethod(PsiElement element) {
final PsiElement container = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class);
return container instanceof PsiMethod ? (PsiMethod)container : null;
}
public static void renameVariableReferences(PsiVariable variable, String newName, SearchScope scope) throws IncorrectOperationException {
renameVariableReferences(variable, newName, scope, false);
}
public static void renameVariableReferences(PsiVariable variable,
String newName,
SearchScope scope,
final boolean ignoreAccessScope) throws IncorrectOperationException {
for (PsiReference reference : ReferencesSearch.search(variable, scope, ignoreAccessScope)) {
reference.handleElementRename(newName);
}
}
public static boolean canBeDeclaredFinal(PsiVariable variable) {
LOG.assertTrue(variable instanceof PsiLocalVariable || variable instanceof PsiParameter);
final boolean isReassigned = HighlightControlFlowUtil
.isReassigned(variable, new THashMap<PsiElement, Collection<ControlFlowUtil.VariableInfo>>());
return !isReassigned;
}
/**
* removes a reference to the specified class from the reference list given
*
* @return if removed - a reference to the class or null if there were no references to this class in the reference list
*/
public static PsiJavaCodeReferenceElement removeFromReferenceList(PsiReferenceList refList, PsiClass aClass)
throws IncorrectOperationException {
PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
for (PsiJavaCodeReferenceElement ref : refs) {
if (ref.isReferenceTo(aClass)) {
PsiJavaCodeReferenceElement refCopy = (PsiJavaCodeReferenceElement)ref.copy();
ref.delete();
return refCopy;
}
}
return null;
}
public static PsiJavaCodeReferenceElement findReferenceToClass(PsiReferenceList refList, PsiClass aClass) {
PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
for (PsiJavaCodeReferenceElement ref : refs) {
if (ref.isReferenceTo(aClass)) {
return ref;
}
}
return null;
}
public static PsiType getTypeByExpressionWithExpectedType(PsiExpression expr) {
PsiType type = getTypeByExpression(expr);
if (type != null) return type;
ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getInstance(expr.getProject()).getExpectedTypes(expr, false);
if (expectedTypes.length == 1) {
type = expectedTypes[0].getType();
if (!type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return type;
}
return null;
}
public static PsiType getTypeByExpression(PsiExpression expr) {
PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory();
return getTypeByExpression(expr, factory);
}
private static PsiType getTypeByExpression(PsiExpression expr, final PsiElementFactory factory) {
PsiType type = RefactoringChangeUtil.getTypeByExpression(expr);
if (PsiType.NULL.equals(type)) {
ExpectedTypeInfo[] infos = ExpectedTypesProvider.getInstance(expr.getProject()).getExpectedTypes(expr, false);
if (infos.length == 1) {
type = infos[0].getType();
}
else {
type = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, expr.getResolveScope());
}
}
return type;
}
public static boolean isAssignmentLHS(PsiElement element) {
PsiElement parent = element.getParent();
return parent instanceof PsiAssignmentExpression && element.equals(((PsiAssignmentExpression)parent).getLExpression()) ||
isPlusPlusOrMinusMinus(parent);
}
public static boolean isPlusPlusOrMinusMinus(PsiElement element) {
if (element instanceof PsiPrefixExpression) {
return ((PsiPrefixExpression)element).getOperationTokenType() == JavaTokenType.PLUSPLUS ||
((PsiPrefixExpression)element).getOperationTokenType() == JavaTokenType.MINUSMINUS;
}
else if (element instanceof PsiPostfixExpression) {
IElementType operandTokenType = ((PsiPostfixExpression)element).getOperationTokenType();
return operandTokenType == JavaTokenType.PLUSPLUS || operandTokenType == JavaTokenType.MINUSMINUS;
}
else {
return false;
}
}
private static void removeFinalParameters(PsiMethod method) throws IncorrectOperationException {
PsiParameterList paramList = method.getParameterList();
PsiParameter[] params = paramList.getParameters();
for (PsiParameter param : params) {
if (param.hasModifierProperty(PsiModifier.FINAL)) {
PsiUtil.setModifierProperty(param, PsiModifier.FINAL, false);
}
}
}
public static PsiElement getAnchorElementForMultipleExpressions(@NotNull PsiExpression[] occurrences, PsiElement scope) {
PsiElement anchor = null;
for (PsiExpression occurrence : occurrences) {
if (scope != null && !PsiTreeUtil.isAncestor(scope, occurrence, false)) {
continue;
}
PsiElement anchor1 = getParentExpressionAnchorElement(occurrence);
if (anchor1 == null) {
if (occurrence.isPhysical()) return null;
continue;
}
if (anchor == null) {
anchor = anchor1;
}
else {
PsiElement commonParent = PsiTreeUtil.findCommonParent(anchor, anchor1);
if (commonParent == null || anchor.getTextRange() == null || anchor1.getTextRange() == null) return null;
PsiElement firstAnchor = anchor.getTextRange().getStartOffset() < anchor1.getTextRange().getStartOffset() ? anchor : anchor1;
if (commonParent.equals(firstAnchor)) {
anchor = firstAnchor;
}
else {
if (commonParent instanceof PsiStatement) {
anchor = commonParent;
}
else {
PsiElement parent = firstAnchor;
while (!parent.getParent().equals(commonParent)) {
parent = parent.getParent();
}
final PsiElement newAnchor = getParentExpressionAnchorElement(parent);
if (newAnchor != null) {
anchor = newAnchor;
}
else {
anchor = parent;
}
}
}
}
}
if (anchor == null) return null;
if (occurrences.length > 1 && anchor.getParent().getParent() instanceof PsiSwitchStatement) {
PsiSwitchStatement switchStatement = (PsiSwitchStatement)anchor.getParent().getParent();
if (switchStatement.getBody().equals(anchor.getParent())) {
int startOffset = occurrences[0].getTextRange().getStartOffset();
int endOffset = occurrences[occurrences.length - 1].getTextRange().getEndOffset();
PsiStatement[] statements = switchStatement.getBody().getStatements();
boolean isInDifferentCases = false;
for (PsiStatement statement : statements) {
if (statement instanceof PsiSwitchLabelStatement) {
int caseOffset = statement.getTextRange().getStartOffset();
if (startOffset < caseOffset && caseOffset < endOffset) {
isInDifferentCases = true;
break;
}
}
}
if (isInDifferentCases) {
anchor = switchStatement;
}
}
}
return anchor;
}
public static boolean isMethodUsage(PsiElement element) {
if (element instanceof PsiEnumConstant) {
return JavaLanguage.INSTANCE.equals(element.getLanguage());
}
if (!(element instanceof PsiJavaCodeReferenceElement)) return false;
PsiElement parent = element.getParent();
if (parent instanceof PsiCall) {
return true;
}
else if (parent instanceof PsiAnonymousClass) {
return element.equals(((PsiAnonymousClass)parent).getBaseClassReference());
}
return false;
}
@Nullable
public static PsiExpressionList getArgumentListByMethodReference(PsiElement ref) {
if (ref instanceof PsiEnumConstant) return ((PsiEnumConstant)ref).getArgumentList();
PsiElement parent = ref.getParent();
if (parent instanceof PsiCall) {
return ((PsiCall)parent).getArgumentList();
}
else if (parent instanceof PsiAnonymousClass) {
return ((PsiNewExpression)parent.getParent()).getArgumentList();
}
LOG.assertTrue(false);
return null;
}
public static PsiCall getCallExpressionByMethodReference(PsiElement ref) {
if (ref instanceof PsiEnumConstant) return (PsiCall)ref;
PsiElement parent = ref.getParent();
if (parent instanceof PsiMethodCallExpression) {
return (PsiMethodCallExpression)parent;
}
else if (parent instanceof PsiNewExpression) {
return (PsiNewExpression)parent;
}
else if (parent instanceof PsiAnonymousClass) {
return (PsiNewExpression)parent.getParent();
}
else {
LOG.assertTrue(false);
return null;
}
}
/**
* @return List of highlighters
*/
public static List<RangeHighlighter> highlightAllOccurrences(Project project, PsiElement[] occurrences, Editor editor) {
ArrayList<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
HighlightManager highlightManager = HighlightManager.getInstance(project);
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
if (occurrences.length > 1) {
for (PsiElement occurrence : occurrences) {
final RangeMarker rangeMarker = occurrence.getUserData(ElementToWorkOn.TEXT_RANGE);
if (rangeMarker != null && rangeMarker.isValid()) {
highlightManager
.addRangeHighlight(editor, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), attributes, true, highlighters);
}
else {
final TextRange textRange = occurrence.getTextRange();
highlightManager.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, highlighters);
}
}
}
return highlighters;
}
public static String createTempVar(PsiExpression expr, PsiElement context, boolean declareFinal) throws IncorrectOperationException {
PsiElement anchorStatement = getParentStatement(context, true);
LOG.assertTrue(anchorStatement != null && anchorStatement.getParent() != null);
Project project = expr.getProject();
String[] suggestedNames =
JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null).names;
final String prefix = suggestedNames.length > 0 ? suggestedNames[0] : "var";
final String id = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(prefix, context, true);
PsiElementFactory factory = JavaPsiFacade.getInstance(expr.getProject()).getElementFactory();
if (expr instanceof PsiParenthesizedExpression) {
PsiExpression expr1 = ((PsiParenthesizedExpression)expr).getExpression();
if (expr1 != null) {
expr = expr1;
}
}
PsiDeclarationStatement decl = factory.createVariableDeclarationStatement(id, expr.getType(), expr);
if (declareFinal) {
PsiUtil.setModifierProperty(((PsiLocalVariable)decl.getDeclaredElements()[0]), PsiModifier.FINAL, true);
}
anchorStatement.getParent().addBefore(decl, anchorStatement);
return id;
}
public static int verifySafeCopyExpression(PsiElement expr) {
return verifySafeCopyExpressionSubElement(expr);
}
private static int verifySafeCopyExpressionSubElement(PsiElement element) {
int result = EXPR_COPY_SAFE;
if (element == null) return result;
if (element instanceof PsiThisExpression || element instanceof PsiSuperExpression || element instanceof PsiIdentifier) {
return EXPR_COPY_SAFE;
}
if (element instanceof PsiMethodCallExpression) {
result = EXPR_COPY_UNSAFE;
}
if (element instanceof PsiNewExpression) {
return EXPR_COPY_PROHIBITED;
}
if (element instanceof PsiAssignmentExpression) {
return EXPR_COPY_PROHIBITED;
}
if (isPlusPlusOrMinusMinus(element)) {
return EXPR_COPY_PROHIBITED;
}
PsiElement[] children = element.getChildren();
for (PsiElement child : children) {
int childResult = verifySafeCopyExpressionSubElement(child);
result = Math.max(result, childResult);
}
return result;
}
public static PsiExpression convertInitializerToNormalExpression(PsiExpression expression, PsiType forcedReturnType)
throws IncorrectOperationException {
if (expression instanceof PsiArrayInitializerExpression && (forcedReturnType == null || forcedReturnType instanceof PsiArrayType)) {
return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)expression, forcedReturnType);
}
return expression;
}
public static PsiExpression createNewExpressionFromArrayInitializer(PsiArrayInitializerExpression initializer, PsiType forcedType)
throws IncorrectOperationException {
PsiType initializerType = null;
if (initializer != null) {
if (forcedType != null) {
initializerType = forcedType;
}
else {
initializerType = getTypeByExpression(initializer);
}
}
if (initializerType == null) {
return initializer;
}
LOG.assertTrue(initializerType instanceof PsiArrayType);
PsiElementFactory factory = JavaPsiFacade.getInstance(initializer.getProject()).getElementFactory();
PsiNewExpression result =
(PsiNewExpression)factory.createExpressionFromText("new " + initializerType.getPresentableText() + "{}", null);
result = (PsiNewExpression)CodeStyleManager.getInstance(initializer.getProject()).reformat(result);
PsiArrayInitializerExpression arrayInitializer = result.getArrayInitializer();
LOG.assertTrue(arrayInitializer != null);
arrayInitializer.replace(initializer);
return result;
}
public static void makeMethodAbstract(@NotNull PsiClass targetClass, @NotNull PsiMethod method) throws IncorrectOperationException {
if (!method.hasModifierProperty(PsiModifier.DEFAULT)) {
PsiCodeBlock body = method.getBody();
if (body != null) {
body.delete();
}
PsiUtil.setModifierProperty(method, PsiModifier.ABSTRACT, true);
}
prepareForInterface(method);
if (!targetClass.isInterface()) {
PsiUtil.setModifierProperty(targetClass, PsiModifier.ABSTRACT, true);
}
}
public static void makeMethodDefault(@NotNull PsiMethod method) throws IncorrectOperationException {
PsiUtil.setModifierProperty(method, PsiModifier.DEFAULT, !method.hasModifierProperty(PsiModifier.STATIC));
PsiUtil.setModifierProperty(method, PsiModifier.ABSTRACT, false);
prepareForInterface(method);
}
private static void prepareForInterface(PsiMethod method) {
PsiUtil.setModifierProperty(method, PsiModifier.FINAL, false);
PsiUtil.setModifierProperty(method, PsiModifier.SYNCHRONIZED, false);
PsiUtil.setModifierProperty(method, PsiModifier.NATIVE, false);
removeFinalParameters(method);
}
public static boolean isInsideAnonymousOrLocal(PsiElement element, PsiElement upTo) {
for (PsiElement current = element; current != null && current != upTo; current = current.getParent()) {
if (current instanceof PsiAnonymousClass) return true;
if (current instanceof PsiClass && current.getParent() instanceof PsiDeclarationStatement) {
return true;
}
}
return false;
}
public static PsiExpression unparenthesizeExpression(PsiExpression expression) {
while (expression instanceof PsiParenthesizedExpression) {
final PsiExpression innerExpression = ((PsiParenthesizedExpression)expression).getExpression();
if (innerExpression == null) return expression;
expression = innerExpression;
}
return expression;
}
public static PsiExpression outermostParenthesizedExpression(PsiExpression expression) {
while (expression.getParent() instanceof PsiParenthesizedExpression) {
expression = (PsiParenthesizedExpression)expression.getParent();
}
return expression;
}
public static String getNewInnerClassName(PsiClass aClass, String oldInnerClassName, String newName) {
if (!oldInnerClassName.endsWith(aClass.getName())) return newName;
StringBuilder buffer = new StringBuilder(oldInnerClassName);
buffer.replace(buffer.length() - aClass.getName().length(), buffer.length(), newName);
return buffer.toString();
}
public static void visitImplicitSuperConstructorUsages(PsiClass subClass,
final ImplicitConstructorUsageVisitor implicitConstructorUsageVistor,
PsiClass superClass) {
final PsiMethod baseDefaultConstructor = findDefaultConstructor(superClass);
final PsiMethod[] constructors = subClass.getConstructors();
if (constructors.length > 0) {
for (PsiMethod constructor : constructors) {
final PsiStatement[] statements = constructor.getBody().getStatements();
if (statements.length < 1 || !JavaHighlightUtil.isSuperOrThisCall(statements[0], true, true)) {
implicitConstructorUsageVistor.visitConstructor(constructor, baseDefaultConstructor);
}
}
}
else {
implicitConstructorUsageVistor.visitClassWithoutConstructors(subClass);
}
}
private static PsiMethod findDefaultConstructor(final PsiClass aClass) {
final PsiMethod[] constructors = aClass.getConstructors();
for (PsiMethod constructor : constructors) {
if (constructor.getParameterList().getParametersCount() == 0) return constructor;
}
return null;
}
public static void replaceMovedMemberTypeParameters(final PsiElement member,
final Iterable<PsiTypeParameter> parametersIterable,
final PsiSubstitutor substitutor,
final PsiElementFactory factory) {
final Map<PsiElement, PsiElement> replacement = new LinkedHashMap<PsiElement, PsiElement>();
for (PsiTypeParameter parameter : parametersIterable) {
PsiType substitutedType = substitutor.substitute(parameter);
if (substitutedType == null) {
substitutedType = TypeConversionUtil.erasure(factory.createType(parameter));
}
for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(member))) {
final PsiElement element = reference.getElement();
final PsiElement parent = element.getParent();
if (parent instanceof PsiTypeElement) {
replacement.put(parent, factory.createTypeElement(substitutedType));
} else if (element instanceof PsiJavaCodeReferenceElement && substitutedType instanceof PsiClassType) {
replacement.put(element, factory.createReferenceElementByType((PsiClassType)substitutedType));
}
}
}
for (PsiElement element : replacement.keySet()) {
if (element.isValid()) {
element.replace(replacement.get(element));
}
}
}
@Nullable
public static PsiMethod getChainedConstructor(PsiMethod constructor) {
final PsiCodeBlock constructorBody = constructor.getBody();
if (constructorBody == null) return null;
final PsiStatement[] statements = constructorBody.getStatements();
if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) {
final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
if (expression instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
final PsiReferenceExpression methodExpr = methodCallExpression.getMethodExpression();
if ("this".equals(methodExpr.getReferenceName())) {
return (PsiMethod)methodExpr.resolve();
}
}
}
return null;
}
public static boolean isInMovedElement(PsiElement element, Set<PsiMember> membersToMove) {
for (PsiMember member : membersToMove) {
if (PsiTreeUtil.isAncestor(member, element, false)) return true;
}
return false;
}
public static boolean inImportStatement(PsiReference ref, PsiElement element) {
if (PsiTreeUtil.getParentOfType(element, PsiImportStatement.class) != null) return true;
final PsiFile containingFile = element.getContainingFile();
if (containingFile instanceof PsiJavaFile) {
final PsiImportList importList = ((PsiJavaFile)containingFile).getImportList();
if (importList != null) {
final TextRange refRange = ref.getRangeInElement().shiftRight(element.getTextRange().getStartOffset());
for (PsiImportStatementBase importStatementBase : importList.getAllImportStatements()) {
final TextRange textRange = importStatementBase.getTextRange();
if (textRange.contains(refRange)) {
return true;
}
}
}
}
return false;
}
public static PsiStatement putStatementInLoopBody(PsiStatement declaration, PsiElement container, PsiElement finalAnchorStatement)
throws IncorrectOperationException {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(container.getProject()).getElementFactory();
if(isLoopOrIf(container)) {
PsiStatement loopBody = getLoopBody(container, finalAnchorStatement);
PsiStatement loopBodyCopy = loopBody != null ? (PsiStatement) loopBody.copy() : null;
PsiBlockStatement blockStatement = (PsiBlockStatement)elementFactory
.createStatementFromText("{}", null);
blockStatement = (PsiBlockStatement) CodeStyleManager.getInstance(container.getProject()).reformat(blockStatement);
final PsiElement prevSibling = loopBody.getPrevSibling();
if(prevSibling instanceof PsiWhiteSpace) {
final PsiElement pprev = prevSibling.getPrevSibling();
if (!(pprev instanceof PsiComment) || !((PsiComment)pprev).getTokenType().equals(JavaTokenType.END_OF_LINE_COMMENT)) {
prevSibling.delete();
}
}
blockStatement = (PsiBlockStatement) loopBody.replace(blockStatement);
final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
declaration = (PsiStatement) codeBlock.add(declaration);
JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
if (loopBodyCopy != null) codeBlock.add(loopBodyCopy);
} else if (container instanceof PsiLambdaExpression) {
final PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container;
final PsiElement lambdaExpressionBody = lambdaExpression.getBody();
LOG.assertTrue(lambdaExpressionBody != null);
final PsiLambdaExpression expressionFromText = (PsiLambdaExpression)elementFactory
.createExpressionFromText(lambdaExpression.getParameterList().getText() + " -> {}", lambdaExpression);
PsiCodeBlock newBody = (PsiCodeBlock)expressionFromText.getBody();
LOG.assertTrue(newBody != null);
newBody.add(declaration);
final PsiStatement lastBodyStatement;
if (LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression) == PsiType.VOID) {
lastBodyStatement = elementFactory.createStatementFromText("a;", lambdaExpression);
((PsiExpressionStatement)lastBodyStatement).getExpression().replace(lambdaExpressionBody);
}
else {
lastBodyStatement = elementFactory.createStatementFromText("return a;", lambdaExpression);
final PsiExpression returnValue = ((PsiReturnStatement)lastBodyStatement).getReturnValue();
LOG.assertTrue(returnValue != null);
returnValue.replace(lambdaExpressionBody);
}
newBody.add(lastBodyStatement);
final PsiLambdaExpression copy = (PsiLambdaExpression)lambdaExpression.replace(expressionFromText);
newBody = (PsiCodeBlock)copy.getBody();
LOG.assertTrue(newBody != null);
declaration = newBody.getStatements()[0];
declaration = (PsiStatement)JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
}
return declaration;
}
@Nullable
private static PsiStatement getLoopBody(PsiElement container, PsiElement anchorStatement) {
if(container instanceof PsiLoopStatement) {
return ((PsiLoopStatement) container).getBody();
}
else if (container instanceof PsiIfStatement) {
final PsiStatement thenBranch = ((PsiIfStatement)container).getThenBranch();
if (thenBranch != null && PsiTreeUtil.isAncestor(thenBranch, anchorStatement, false)) {
return thenBranch;
}
final PsiStatement elseBranch = ((PsiIfStatement)container).getElseBranch();
if (elseBranch != null && PsiTreeUtil.isAncestor(elseBranch, anchorStatement, false)) {
return elseBranch;
}
LOG.assertTrue(false);
}
LOG.assertTrue(false);
return null;
}
public static boolean isLoopOrIf(PsiElement element) {
return element instanceof PsiLoopStatement || element instanceof PsiIfStatement;
}
public interface ImplicitConstructorUsageVisitor {
void visitConstructor(PsiMethod constructor, PsiMethod baseConstructor);
void visitClassWithoutConstructors(PsiClass aClass);
}
public interface Graph<T> {
Set<T> getVertices();
Set<T> getTargets(T source);
}
/**
* Returns subset of <code>graph.getVertices()</code> that is a tranistive closure (by <code>graph.getTargets()<code>)
* of the following property: initialRelation.value() of vertex or <code>graph.getTargets(vertex)</code> is true.
* <p/>
* Note that <code>graph.getTargets()</code> is not neccesrily a subset of <code>graph.getVertex()</code>
*
* @param graph
* @param initialRelation
* @return subset of graph.getVertices()
*/
public static <T> Set<T> transitiveClosure(Graph<T> graph, Condition<T> initialRelation) {
Set<T> result = new HashSet<T>();
final Set<T> vertices = graph.getVertices();
boolean anyChanged;
do {
anyChanged = false;
for (T currentVertex : vertices) {
if (!result.contains(currentVertex)) {
if (!initialRelation.value(currentVertex)) {
Set<T> targets = graph.getTargets(currentVertex);
for (T currentTarget : targets) {
if (result.contains(currentTarget) || initialRelation.value(currentTarget)) {
result.add(currentVertex);
anyChanged = true;
break;
}
}
}
else {
result.add(currentVertex);
}
}
}
}
while (anyChanged);
return result;
}
public static boolean equivalentTypes(PsiType t1, PsiType t2, PsiManager manager) {
while (t1 instanceof PsiArrayType) {
if (!(t2 instanceof PsiArrayType)) return false;
t1 = ((PsiArrayType)t1).getComponentType();
t2 = ((PsiArrayType)t2).getComponentType();
}
if (t1 instanceof PsiPrimitiveType) {
return t2 instanceof PsiPrimitiveType && t1.equals(t2);
}
return manager.areElementsEquivalent(PsiUtil.resolveClassInType(t1), PsiUtil.resolveClassInType(t2));
}
public static List<PsiVariable> collectReferencedVariables(PsiElement scope) {
final List<PsiVariable> result = new ArrayList<PsiVariable>();
scope.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
final PsiElement element = expression.resolve();
if (element instanceof PsiVariable) {
result.add((PsiVariable)element);
}
final PsiExpression qualifier = expression.getQualifierExpression();
if (qualifier != null) {
qualifier.accept(this);
}
}
});
return result;
}
public static boolean isModifiedInScope(PsiVariable variable, PsiElement scope) {
for (PsiReference reference : ReferencesSearch.search(variable, new LocalSearchScope(scope), false)) {
if (isAssignmentLHS(reference.getElement())) return true;
}
return false;
}
private static String getNameOfReferencedParameter(PsiDocTag tag) {
LOG.assertTrue("param".equals(tag.getName()));
final PsiElement[] dataElements = tag.getDataElements();
if (dataElements.length < 1) return null;
return dataElements[0].getText();
}
public static void fixJavadocsForParams(PsiMethod method, Set<PsiParameter> newParameters) throws IncorrectOperationException {
fixJavadocsForParams(method, newParameters, Conditions.<Pair<PsiParameter,String>>alwaysFalse());
}
public static void fixJavadocsForParams(PsiMethod method,
Set<PsiParameter> newParameters,
Condition<Pair<PsiParameter, String>> eqCondition) throws IncorrectOperationException {
fixJavadocsForParams(method, newParameters, eqCondition, Conditions.<String>alwaysTrue());
}
public static void fixJavadocsForParams(PsiMethod method,
Set<PsiParameter> newParameters,
Condition<Pair<PsiParameter, String>> eqCondition,
Condition<String> matchedToOldParam) throws IncorrectOperationException {
final PsiDocComment docComment = method.getDocComment();
if (docComment == null) return;
final PsiParameter[] parameters = method.getParameterList().getParameters();
final PsiDocTag[] paramTags = docComment.findTagsByName("param");
if (parameters.length > 0 && newParameters.size() < parameters.length && paramTags.length == 0) return;
Map<PsiParameter, PsiDocTag> tagForParam = new HashMap<PsiParameter, PsiDocTag>();
for (PsiParameter parameter : parameters) {
boolean found = false;
for (PsiDocTag paramTag : paramTags) {
if (parameter.getName().equals(getNameOfReferencedParameter(paramTag))) {
tagForParam.put(parameter, paramTag);
found = true;
break;
}
}
if (!found) {
for (PsiDocTag paramTag : paramTags) {
final String paramName = getNameOfReferencedParameter(paramTag);
if (eqCondition.value(Pair.create(parameter, paramName))) {
tagForParam.put(parameter, paramTag);
found = true;
break;
}
}
}
if (!found && !newParameters.contains(parameter)) {
tagForParam.put(parameter, null);
}
}
List<PsiDocTag> newTags = new ArrayList<PsiDocTag>();
for (PsiDocTag paramTag : paramTags) {
final String paramName = getNameOfReferencedParameter(paramTag);
if (!tagForParam.containsValue(paramTag) && !matchedToOldParam.value(paramName)) {
newTags.add((PsiDocTag)paramTag.copy());
}
}
for (PsiParameter parameter : parameters) {
if (tagForParam.containsKey(parameter)) {
final PsiDocTag psiDocTag = tagForParam.get(parameter);
if (psiDocTag != null) {
final PsiDocTag copy = (PsiDocTag)psiDocTag.copy();
final PsiDocTagValue valueElement = copy.getValueElement();
if (valueElement != null) {
valueElement.replace(createParamTag(parameter).getValueElement());
}
newTags.add(copy);
}
}
else {
newTags.add(createParamTag(parameter));
}
}
PsiElement anchor = paramTags.length > 0 ? paramTags[0].getPrevSibling() : null;
for (PsiDocTag paramTag : paramTags) {
paramTag.delete();
}
for (PsiDocTag psiDocTag : newTags) {
anchor = anchor != null && anchor.isValid() ? docComment.addAfter(psiDocTag, anchor) : docComment.add(psiDocTag);
}
}
private static PsiDocTag createParamTag(PsiParameter parameter) {
return JavaPsiFacade.getInstance(parameter.getProject()).getElementFactory().createParamTag(parameter.getName(), "");
}
public static PsiDirectory createPackageDirectoryInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot)
throws IncorrectOperationException {
final PsiDirectory[] directories = aPackage.getDirectories();
for (PsiDirectory directory : directories) {
if (VfsUtil.isAncestor(sourceRoot, directory.getVirtualFile(), false)) {
return directory;
}
}
String qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
final String[] shortNames = qNameToCreate.split("\\.");
PsiDirectory current = aPackage.getManager().findDirectory(sourceRoot);
LOG.assertTrue(current != null);
for (String shortName : shortNames) {
PsiDirectory subdirectory = current.findSubdirectory(shortName);
if (subdirectory == null) {
subdirectory = current.createSubdirectory(shortName);
}
current = subdirectory;
}
return current;
}
public static String qNameToCreateInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot) throws IncorrectOperationException {
String targetQName = aPackage.getQualifiedName();
String sourceRootPackage =
ProjectRootManager.getInstance(aPackage.getManager().getProject()).getFileIndex().getPackageNameByDirectory(sourceRoot);
if (!canCreateInSourceRoot(sourceRootPackage, targetQName)) {
throw new IncorrectOperationException(
"Cannot create package '" + targetQName + "' in source folder " + sourceRoot.getPresentableUrl());
}
String result = targetQName.substring(sourceRootPackage.length());
if (StringUtil.startsWithChar(result, '.')) result = result.substring(1); // remove initial '.'
return result;
}
public static boolean canCreateInSourceRoot(final String sourceRootPackage, final String targetQName) {
if (sourceRootPackage == null || !targetQName.startsWith(sourceRootPackage)) return false;
if (sourceRootPackage.length() == 0 || targetQName.length() == sourceRootPackage.length()) return true;
return targetQName.charAt(sourceRootPackage.length()) == '.';
}
@Nullable
public static PsiDirectory findPackageDirectoryInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot) {
final PsiDirectory[] directories = aPackage.getDirectories();
for (PsiDirectory directory : directories) {
if (VfsUtil.isAncestor(sourceRoot, directory.getVirtualFile(), false)) {
return directory;
}
}
String qNameToCreate;
try {
qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
}
catch (IncorrectOperationException e) {
return null;
}
final String[] shortNames = qNameToCreate.split("\\.");
PsiDirectory current = aPackage.getManager().findDirectory(sourceRoot);
LOG.assertTrue(current != null);
for (String shortName : shortNames) {
PsiDirectory subdirectory = current.findSubdirectory(shortName);
if (subdirectory == null) {
return null;
}
current = subdirectory;
}
return current;
}
public static class ConditionCache<T> implements Condition<T> {
private final Condition<T> myCondition;
private final HashSet<T> myProcessedSet = new HashSet<T>();
private final HashSet<T> myTrueSet = new HashSet<T>();
public ConditionCache(Condition<T> condition) {
myCondition = condition;
}
public boolean value(T object) {
if (!myProcessedSet.contains(object)) {
myProcessedSet.add(object);
final boolean value = myCondition.value(object);
if (value) {
myTrueSet.add(object);
return true;
}
return false;
}
return myTrueSet.contains(object);
}
}
public static class IsDescendantOf implements Condition<PsiClass> {
private final PsiClass myClass;
private final ConditionCache<PsiClass> myConditionCache;
public IsDescendantOf(PsiClass aClass) {
myClass = aClass;
myConditionCache = new ConditionCache<PsiClass>(new Condition<PsiClass>() {
public boolean value(PsiClass aClass) {
return InheritanceUtil.isInheritorOrSelf(aClass, myClass, true);
}
});
}
public boolean value(PsiClass aClass) {
return myConditionCache.value(aClass);
}
}
@Nullable
public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@NotNull final PsiElement... elements) {
return createTypeParameterListWithUsedTypeParameters(null, elements);
}
@Nullable
public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@Nullable final PsiTypeParameterList fromList,
@NotNull final PsiElement... elements) {
return createTypeParameterListWithUsedTypeParameters(fromList, Conditions.<PsiTypeParameter>alwaysTrue(), elements);
}
@Nullable
public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@Nullable final PsiTypeParameterList fromList,
Condition<PsiTypeParameter> filter,
@NotNull final PsiElement... elements) {
if (elements.length == 0) return null;
final Set<PsiTypeParameter> used = new HashSet<PsiTypeParameter>();
for (final PsiElement element : elements) {
if (element == null) continue;
collectTypeParameters(used, element, filter); //pull up extends cls class with type params
}
if (fromList != null) {
used.retainAll(Arrays.asList(fromList.getTypeParameters()));
}
PsiTypeParameter[] typeParameters = used.toArray(new PsiTypeParameter[used.size()]);
Arrays.sort(typeParameters, new Comparator<PsiTypeParameter>() {
public int compare(final PsiTypeParameter tp1, final PsiTypeParameter tp2) {
return tp1.getTextRange().getStartOffset() - tp2.getTextRange().getStartOffset();
}
});
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(elements[0].getProject()).getElementFactory();
try {
final PsiClass aClass = elementFactory.createClassFromText("class A {}", null);
PsiTypeParameterList list = aClass.getTypeParameterList();
assert list != null;
for (final PsiTypeParameter typeParameter : typeParameters) {
list.add(typeParameter);
}
return list;
}
catch (IncorrectOperationException e) {
LOG.error(e);
assert false;
return null;
}
}
public static void collectTypeParameters(final Set<PsiTypeParameter> used, final PsiElement element) {
collectTypeParameters(used, element, Conditions.<PsiTypeParameter>alwaysTrue());
}
public static void collectTypeParameters(final Set<PsiTypeParameter> used, final PsiElement element,
final Condition<PsiTypeParameter> filter) {
element.accept(new JavaRecursiveElementVisitor() {
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
if (!reference.isQualified()) {
final PsiElement resolved = reference.resolve();
if (resolved instanceof PsiTypeParameter) {
final PsiTypeParameter typeParameter = (PsiTypeParameter)resolved;
if (PsiTreeUtil.isAncestor(typeParameter.getOwner(), element, false) && filter.value(typeParameter)) {
used.add(typeParameter);
}
}
}
}
@Override
public void visitExpression(final PsiExpression expression) {
super.visitExpression(expression);
final PsiType type = expression.getType();
if (type != null) {
final TypeParameterSearcher searcher = new TypeParameterSearcher();
type.accept(searcher);
for (PsiTypeParameter typeParam : searcher.myTypeParams) {
if (PsiTreeUtil.isAncestor(typeParam.getOwner(), element, false) && filter.value(typeParam)){
used.add(typeParam);
}
}
}
}
class TypeParameterSearcher extends PsiTypeVisitor<Boolean> {
private final Set<PsiTypeParameter> myTypeParams = new java.util.HashSet<PsiTypeParameter>();
public Boolean visitType(final PsiType type) {
return false;
}
public Boolean visitArrayType(final PsiArrayType arrayType) {
return arrayType.getComponentType().accept(this);
}
public Boolean visitClassType(final PsiClassType classType) {
final PsiClass aClass = classType.resolve();
if (aClass instanceof PsiTypeParameter) {
myTypeParams.add((PsiTypeParameter)aClass);
}
final PsiType[] types = classType.getParameters();
for (final PsiType psiType : types) {
psiType.accept(this);
}
return false;
}
public Boolean visitWildcardType(final PsiWildcardType wildcardType) {
final PsiType bound = wildcardType.getBound();
if (bound != null) {
bound.accept(this);
}
return false;
}
}
});
}
}