2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.refactoring
.util
;
18 import com
.intellij
.codeInsight
.ExpectedTypeInfo
;
19 import com
.intellij
.codeInsight
.ExpectedTypesProvider
;
20 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightControlFlowUtil
;
21 import com
.intellij
.codeInsight
.highlighting
.HighlightManager
;
22 import com
.intellij
.openapi
.diagnostic
.Logger
;
23 import com
.intellij
.openapi
.editor
.Editor
;
24 import com
.intellij
.openapi
.editor
.RangeMarker
;
25 import com
.intellij
.openapi
.editor
.colors
.EditorColors
;
26 import com
.intellij
.openapi
.editor
.colors
.EditorColorsManager
;
27 import com
.intellij
.openapi
.editor
.markup
.RangeHighlighter
;
28 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
29 import com
.intellij
.openapi
.project
.Project
;
30 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
31 import com
.intellij
.openapi
.util
.Comparing
;
32 import com
.intellij
.openapi
.util
.Condition
;
33 import com
.intellij
.openapi
.util
.TextRange
;
34 import com
.intellij
.openapi
.util
.text
.StringUtil
;
35 import com
.intellij
.openapi
.vfs
.VfsUtil
;
36 import com
.intellij
.openapi
.vfs
.VirtualFile
;
37 import com
.intellij
.psi
.*;
38 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
39 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
40 import com
.intellij
.psi
.codeStyle
.VariableKind
;
41 import com
.intellij
.psi
.controlFlow
.ControlFlowUtil
;
42 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
43 import com
.intellij
.psi
.javadoc
.PsiDocTag
;
44 import com
.intellij
.psi
.search
.LocalSearchScope
;
45 import com
.intellij
.psi
.search
.SearchScope
;
46 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
47 import com
.intellij
.psi
.tree
.IElementType
;
48 import com
.intellij
.psi
.util
.InheritanceUtil
;
49 import com
.intellij
.psi
.util
.PsiTreeUtil
;
50 import com
.intellij
.psi
.util
.PsiUtil
;
51 import com
.intellij
.refactoring
.PackageWrapper
;
52 import com
.intellij
.refactoring
.introduceField
.ElementToWorkOn
;
53 import com
.intellij
.refactoring
.introduceVariable
.IntroduceVariableBase
;
54 import com
.intellij
.usageView
.UsageInfo
;
55 import com
.intellij
.util
.IncorrectOperationException
;
56 import com
.intellij
.util
.containers
.HashMap
;
57 import com
.intellij
.util
.containers
.HashSet
;
58 import gnu
.trove
.THashMap
;
59 import org
.jetbrains
.annotations
.NotNull
;
60 import org
.jetbrains
.annotations
.Nullable
;
64 public class RefactoringUtil
{
65 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.util.RefactoringUtil");
66 public static final int EXPR_COPY_SAFE
= 0;
67 public static final int EXPR_COPY_UNSAFE
= 1;
68 public static final int EXPR_COPY_PROHIBITED
= 2;
70 private RefactoringUtil() {
73 public static boolean isSourceRoot(final PsiDirectory directory
) {
74 if (directory
.getManager() == null) return false;
75 final Project project
= directory
.getProject();
76 final VirtualFile virtualFile
= directory
.getVirtualFile();
77 final VirtualFile sourceRootForFile
= ProjectRootManager
.getInstance(project
).getFileIndex().getSourceRootForFile(virtualFile
);
78 return Comparing
.equal(virtualFile
, sourceRootForFile
);
81 public static boolean isInStaticContext(PsiElement element
, final PsiClass aClass
) {
82 return PsiUtil
.getEnclosingStaticElement(element
, aClass
) != null;
85 public static boolean isResolvableType(PsiType type
) {
86 return type
.accept(new PsiTypeVisitor
<Boolean
>() {
87 public Boolean
visitPrimitiveType(PsiPrimitiveType primitiveType
) {
91 public Boolean
visitArrayType(PsiArrayType arrayType
) {
92 return arrayType
.getComponentType().accept(this);
95 public Boolean
visitClassType(PsiClassType classType
) {
96 if (classType
.resolve() == null) return Boolean
.FALSE
;
97 PsiType
[] parameters
= classType
.getParameters();
98 for (PsiType parameter
: parameters
) {
99 if (parameter
!= null && !parameter
.accept(this).booleanValue()) return Boolean
.FALSE
;
105 public Boolean
visitWildcardType(PsiWildcardType wildcardType
) {
106 if (wildcardType
.getBound() != null) return wildcardType
.getBound().accept(this);
112 public static PsiElement
replaceOccurenceWithFieldRef(PsiExpression occurrence
, PsiField newField
, PsiClass destinationClass
)
113 throws IncorrectOperationException
{
114 final PsiManager manager
= destinationClass
.getManager();
115 final String fieldName
= newField
.getName();
116 final JavaPsiFacade facade
= JavaPsiFacade
.getInstance(manager
.getProject());
117 final PsiElement element
= occurrence
.getUserData(ElementToWorkOn
.PARENT
);
118 final PsiVariable psiVariable
= facade
.getResolveHelper().resolveReferencedVariable(fieldName
, element
!= null ? element
: occurrence
);
119 final PsiElementFactory factory
= facade
.getElementFactory();
120 if (psiVariable
!= null && psiVariable
.equals(newField
)) {
121 return IntroduceVariableBase
.replace(occurrence
, factory
.createExpressionFromText(fieldName
, null), manager
.getProject());
124 final PsiReferenceExpression ref
= (PsiReferenceExpression
)factory
.createExpressionFromText("this." + fieldName
, null);
125 if (!occurrence
.isValid()) return null;
126 if (newField
.hasModifierProperty(PsiModifier
.STATIC
)) {
127 ref
.setQualifierExpression(factory
.createReferenceExpression(destinationClass
));
129 return IntroduceVariableBase
.replace(occurrence
, ref
, manager
.getProject());
134 * @see com.intellij.psi.codeStyle.CodeStyleManager#suggestUniqueVariableName(String,com.intellij.psi.PsiElement,boolean)
135 * Cannot use method from code style manager: a collision with fieldToReplace is not a collision
137 public static String
suggestUniqueVariableName(String baseName
, PsiElement place
, PsiField fieldToReplace
) {
140 final String name
= index
> 0 ? baseName
+ index
: baseName
;
142 final PsiManager manager
= place
.getManager();
143 PsiResolveHelper helper
= JavaPsiFacade
.getInstance(manager
.getProject()).getResolveHelper();
144 PsiVariable refVar
= helper
.resolveReferencedVariable(name
, place
);
145 if (refVar
!= null && !manager
.areElementsEquivalent(refVar
, fieldToReplace
)) continue;
146 class CancelException
extends RuntimeException
{
150 place
.accept(new JavaRecursiveElementWalkingVisitor() {
151 @Override public void visitClass(PsiClass aClass
) {
155 @Override public void visitVariable(PsiVariable variable
) {
156 if (name
.equals(variable
.getName())) {
157 throw new CancelException();
162 catch (CancelException e
) {
170 //order of usages accross different files is irrelevant
171 public static void sortDepthFirstRightLeftOrder(final UsageInfo
[] usages
) {
172 Arrays
.sort(usages
, new Comparator
<UsageInfo
>() {
173 public int compare(final UsageInfo usage1
, final UsageInfo usage2
) {
174 final PsiElement element1
= usage1
.getElement();
175 final PsiElement element2
= usage2
.getElement();
176 if (element1
== null || element2
== null) return 0;
177 return element2
.getTextRange().getStartOffset() - element1
.getTextRange().getStartOffset();
183 public static String
suggestNewOverriderName(String oldOverriderName
, String oldBaseName
, String newBaseName
) {
184 if (oldOverriderName
.equals(oldBaseName
)) {
188 if (oldOverriderName
.startsWith(oldBaseName
)) {
192 i
= StringUtil
.indexOfIgnoreCase(oldOverriderName
, oldBaseName
, 0);
195 String newOverriderName
= oldOverriderName
.substring(0, i
);
196 if (Character
.isUpperCase(oldOverriderName
.charAt(i
))) {
197 newOverriderName
+= StringUtil
.capitalize(newBaseName
);
200 newOverriderName
+= newBaseName
;
202 final int j
= i
+ oldBaseName
.length();
203 if (j
< oldOverriderName
.length()) {
204 newOverriderName
+= oldOverriderName
.substring(j
);
207 return newOverriderName
;
212 public static boolean hasOnDemandStaticImport(final PsiElement element
, final PsiClass aClass
) {
213 if (element
.getContainingFile() instanceof PsiJavaFile
) {
214 final PsiImportList importList
= ((PsiJavaFile
)element
.getContainingFile()).getImportList();
215 if (importList
!= null) {
216 final PsiImportStaticStatement
[] importStaticStatements
= importList
.getImportStaticStatements();
217 for(PsiImportStaticStatement stmt
: importStaticStatements
) {
218 if (stmt
.isOnDemand() && stmt
.resolveTargetClass() == aClass
) {
227 public static PsiElement
replaceElementsWithMap(PsiElement replaceIn
, final Map
<PsiElement
, PsiElement
> elementsToReplace
) throws IncorrectOperationException
{
228 for(Map
.Entry
<PsiElement
, PsiElement
> e
: elementsToReplace
.entrySet()) {
229 if (e
.getKey() == replaceIn
) {
230 return e
.getKey().replace(e
.getValue());
232 e
.getKey().replace(e
.getValue());
237 public static PsiElement
getVariableScope(PsiLocalVariable localVar
) {
238 if (!(localVar
instanceof ImplicitVariable
)) {
239 return localVar
.getParent().getParent();
242 return ((ImplicitVariable
)localVar
).getDeclarationScope();
246 public static PsiReturnStatement
[] findReturnStatements(PsiMethod method
) {
247 ArrayList
<PsiReturnStatement
> vector
= new ArrayList
<PsiReturnStatement
>();
248 PsiCodeBlock body
= method
.getBody();
250 addReturnStatements(vector
, body
);
252 return vector
.toArray(new PsiReturnStatement
[vector
.size()]);
255 private static void addReturnStatements(ArrayList
<PsiReturnStatement
> vector
, PsiElement element
) {
256 if (element
instanceof PsiReturnStatement
) {
257 vector
.add((PsiReturnStatement
)element
);
259 else if (!(element
instanceof PsiClass
)) {
260 PsiElement
[] children
= element
.getChildren();
261 for (PsiElement child
: children
) {
262 addReturnStatements(vector
, child
);
268 public static PsiElement
getParentStatement(PsiElement place
, boolean skipScopingStatements
) {
269 PsiElement parent
= place
;
271 if (parent
instanceof PsiStatement
) break;
272 parent
= parent
.getParent();
273 if (parent
== null) return null;
275 PsiElement parentStatement
= parent
;
276 parent
= parentStatement
instanceof PsiStatement ? parentStatement
: parentStatement
.getParent();
277 while (parent
instanceof PsiStatement
) {
278 if (!skipScopingStatements
&& ((parent
instanceof PsiForStatement
&& parentStatement
== ((PsiForStatement
)parent
).getBody()) || (
279 parent
instanceof PsiForeachStatement
&& parentStatement
== ((PsiForeachStatement
)parent
).getBody()) || (
280 parent
instanceof PsiWhileStatement
&& parentStatement
== ((PsiWhileStatement
)parent
).getBody()) || (
281 parent
instanceof PsiIfStatement
&&
282 (parentStatement
== ((PsiIfStatement
)parent
).getThenBranch() || parentStatement
== ((PsiIfStatement
)parent
).getElseBranch())))) {
283 return parentStatement
;
285 parentStatement
= parent
;
286 parent
= parent
.getParent();
288 return parentStatement
;
292 public static PsiElement
getParentExpressionAnchorElement(PsiElement place
) {
293 PsiElement parent
= place
.getUserData(ElementToWorkOn
.PARENT
);
294 if (parent
== null) parent
= place
;
296 if (isExpressionAnchorElement(parent
)) return parent
;
297 parent
= parent
.getParent();
298 if (parent
== null) return null;
303 public static boolean isExpressionAnchorElement(PsiElement element
) {
304 return element
instanceof PsiStatement
|| element
instanceof PsiClassInitializer
|| element
instanceof PsiField
||
305 element
instanceof PsiMethod
;
310 * @return loop body if expression is part of some loop's condition or for loop's increment part
313 public static PsiElement
getLoopForLoopCondition(PsiExpression expression
) {
314 PsiExpression outermost
= expression
;
315 while (outermost
.getParent() instanceof PsiExpression
) {
316 outermost
= (PsiExpression
)outermost
.getParent();
318 if (outermost
.getParent() instanceof PsiForStatement
) {
319 final PsiForStatement forStatement
= (PsiForStatement
)outermost
.getParent();
320 if (forStatement
.getCondition() == outermost
) {
327 if (outermost
.getParent() instanceof PsiExpressionStatement
&& outermost
.getParent().getParent() instanceof PsiForStatement
) {
328 final PsiForStatement forStatement
= (PsiForStatement
)outermost
.getParent().getParent();
329 if (forStatement
.getUpdate() == outermost
.getParent()) {
336 if (outermost
.getParent() instanceof PsiWhileStatement
) {
337 return outermost
.getParent();
339 if (outermost
.getParent() instanceof PsiDoWhileStatement
) {
340 return outermost
.getParent();
345 public static PsiClass
getThisClass(PsiElement place
) {
346 PsiElement parent
= place
.getContext();
347 if (parent
== null) return null;
348 PsiElement prev
= null;
350 if (parent
instanceof PsiClass
) {
351 if (!(parent
instanceof PsiAnonymousClass
&& ((PsiAnonymousClass
)parent
).getArgumentList() == prev
)) {
352 return (PsiClass
)parent
;
356 parent
= parent
.getContext();
357 if (parent
== null) return null;
361 public static PsiClass
getThisResolveClass(final PsiReferenceExpression place
) {
362 final JavaResolveResult resolveResult
= place
.advancedResolve(false);
363 final PsiElement scope
= resolveResult
.getCurrentFileResolveScope();
364 if (scope
instanceof PsiClass
) {
365 return (PsiClass
)scope
;
370 public static PsiCall
getEnclosingConstructorCall(PsiJavaCodeReferenceElement ref
) {
371 PsiElement parent
= ref
.getParent();
372 if (ref
instanceof PsiReferenceExpression
&& parent
instanceof PsiMethodCallExpression
) return (PsiCall
)parent
;
374 if (parent
instanceof PsiAnonymousClass
) {
375 parent
= parent
.getParent();
378 return parent
instanceof PsiNewExpression ?
(PsiNewExpression
)parent
: null;
381 public static PsiMethod
getEnclosingMethod(PsiElement element
) {
382 final PsiElement container
= PsiTreeUtil
.getParentOfType(element
, PsiMethod
.class, PsiClass
.class);
383 return container
instanceof PsiMethod ?
(PsiMethod
)container
: null;
386 public static void renameVariableReferences(PsiVariable variable
, String newName
, SearchScope scope
) throws IncorrectOperationException
{
387 for (PsiReference reference
: ReferencesSearch
.search(variable
, scope
)) {
388 reference
.handleElementRename(newName
);
392 public static boolean canBeDeclaredFinal(PsiVariable variable
) {
393 LOG
.assertTrue(variable
instanceof PsiLocalVariable
|| variable
instanceof PsiParameter
);
394 final boolean isReassigned
= HighlightControlFlowUtil
395 .isReassigned(variable
, new THashMap
<PsiElement
, Collection
<ControlFlowUtil
.VariableInfo
>>(), new THashMap
<PsiParameter
, Boolean
>());
396 return !isReassigned
;
399 public static PsiThisExpression
createThisExpression(PsiManager manager
, PsiClass qualifierClass
) throws IncorrectOperationException
{
400 PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
401 if (qualifierClass
!= null) {
402 PsiThisExpression qualifiedThis
= (PsiThisExpression
)factory
.createExpressionFromText("q.this", null);
403 qualifiedThis
= (PsiThisExpression
)CodeStyleManager
.getInstance(manager
.getProject()).reformat(qualifiedThis
);
404 PsiJavaCodeReferenceElement thisQualifier
= qualifiedThis
.getQualifier();
405 LOG
.assertTrue(thisQualifier
!= null);
406 thisQualifier
.bindToElement(qualifierClass
);
407 return qualifiedThis
;
410 return (PsiThisExpression
)factory
.createExpressionFromText("this", null);
415 * removes a reference to the specified class from the reference list given
417 * @return if removed - a reference to the class or null if there were no references to this class in the reference list
419 public static PsiJavaCodeReferenceElement
removeFromReferenceList(PsiReferenceList refList
, PsiClass aClass
)
420 throws IncorrectOperationException
{
421 PsiJavaCodeReferenceElement
[] refs
= refList
.getReferenceElements();
422 for (PsiJavaCodeReferenceElement ref
: refs
) {
423 if (ref
.isReferenceTo(aClass
)) {
424 PsiJavaCodeReferenceElement refCopy
= (PsiJavaCodeReferenceElement
)ref
.copy();
432 public static PsiJavaCodeReferenceElement
findReferenceToClass(PsiReferenceList refList
, PsiClass aClass
) {
433 PsiJavaCodeReferenceElement
[] refs
= refList
.getReferenceElements();
434 for (PsiJavaCodeReferenceElement ref
: refs
) {
435 if (ref
.isReferenceTo(aClass
)) {
442 public static PsiType
getTypeByExpressionWithExpectedType(PsiExpression expr
) {
443 PsiType type
= getTypeByExpression(expr
);
444 if (type
!= null) return type
;
445 ExpectedTypeInfo
[] expectedTypes
= ExpectedTypesProvider
.getInstance(expr
.getProject()).getExpectedTypes(expr
, false);
446 if (expectedTypes
.length
== 1) {
447 type
= expectedTypes
[0].getType();
448 if (!type
.equalsToText("java.lang.Object")) return type
;
453 public static PsiType
getTypeByExpression(PsiExpression expr
) {
454 PsiElementFactory factory
= JavaPsiFacade
.getInstance(expr
.getProject()).getElementFactory();
455 return getTypeByExpression(expr
, factory
);
458 private static PsiType
getTypeByExpression(PsiExpression expr
, final PsiElementFactory factory
) {
459 PsiType type
= expr
.getType();
461 if (expr
instanceof PsiArrayInitializerExpression
) {
462 PsiExpression
[] initializers
= ((PsiArrayInitializerExpression
)expr
).getInitializers();
463 if (initializers
.length
> 0) {
464 PsiType initType
= getTypeByExpression(initializers
[0]);
465 if (initType
== null) return null;
466 return initType
.createArrayType();
471 PsiClass refClass
= PsiUtil
.resolveClassInType(type
);
472 if (refClass
instanceof PsiAnonymousClass
) {
473 type
= ((PsiAnonymousClass
)refClass
).getBaseClassType();
475 if (PsiType
.NULL
.equals(type
)) {
476 ExpectedTypeInfo
[] infos
= ExpectedTypesProvider
.getInstance(expr
.getProject()).getExpectedTypes(expr
, false);
477 if (infos
.length
== 1) {
478 type
= infos
[0].getType();
481 type
= factory
.createTypeByFQClassName("java.lang.Object", expr
.getResolveScope());
485 return GenericsUtil
.getVariableTypeByExpressionType(type
);
488 public static boolean isAssignmentLHS(PsiElement element
) {
489 PsiElement parent
= element
.getParent();
491 return parent
instanceof PsiAssignmentExpression
&& element
.equals(((PsiAssignmentExpression
)parent
).getLExpression()) ||
492 isPlusPlusOrMinusMinus(parent
);
495 public static boolean isPlusPlusOrMinusMinus(PsiElement element
) {
496 if (element
instanceof PsiPrefixExpression
) {
497 PsiJavaToken operandSign
= ((PsiPrefixExpression
)element
).getOperationSign();
498 return operandSign
.getTokenType() == JavaTokenType
.PLUSPLUS
|| operandSign
.getTokenType() == JavaTokenType
.MINUSMINUS
;
500 else if (element
instanceof PsiPostfixExpression
) {
501 IElementType operandTokenType
= ((PsiPostfixExpression
)element
).getOperationTokenType();
502 return operandTokenType
== JavaTokenType
.PLUSPLUS
|| operandTokenType
== JavaTokenType
.MINUSMINUS
;
509 private static void removeFinalParameters(PsiMethod method
) throws IncorrectOperationException
{
510 // Remove final parameters
511 PsiParameterList paramList
= method
.getParameterList();
512 PsiParameter
[] params
= paramList
.getParameters();
514 for (PsiParameter param
: params
) {
515 if (param
.hasModifierProperty(PsiModifier
.FINAL
)) {
516 PsiUtil
.setModifierProperty(param
, PsiModifier
.FINAL
, false);
521 public static PsiElement
getAnchorElementForMultipleExpressions(PsiExpression
[] occurrences
, PsiElement scope
) {
522 PsiElement anchor
= null;
523 for (PsiExpression occurrence
: occurrences
) {
524 if (scope
!= null && !PsiTreeUtil
.isAncestor(scope
, occurrence
, false)) {
527 PsiElement anchor1
= getParentExpressionAnchorElement(occurrence
);
528 if (anchor1
== null) return null;
530 if (anchor
== null) {
534 PsiElement commonParent
= PsiTreeUtil
.findCommonParent(anchor
, anchor1
);
535 if (commonParent
== null || anchor
.getTextRange() == null || anchor1
.getTextRange() == null) return null;
536 PsiElement firstAnchor
= anchor
.getTextRange().getStartOffset() < anchor1
.getTextRange().getStartOffset() ? anchor
: anchor1
;
537 if (commonParent
.equals(firstAnchor
)) {
538 anchor
= firstAnchor
;
541 if (commonParent
instanceof PsiStatement
) {
542 anchor
= commonParent
;
545 PsiElement parent
= firstAnchor
;
546 while (!parent
.getParent().equals(commonParent
)) {
547 parent
= parent
.getParent();
549 final PsiElement newAnchor
= getParentExpressionAnchorElement(parent
);
550 if (newAnchor
!= null) {
561 if (occurrences
.length
> 1 && anchor
.getParent().getParent() instanceof PsiSwitchStatement
) {
562 PsiSwitchStatement switchStatement
= (PsiSwitchStatement
)anchor
.getParent().getParent();
563 if (switchStatement
.getBody().equals(anchor
.getParent())) {
564 int startOffset
= occurrences
[0].getTextRange().getStartOffset();
565 int endOffset
= occurrences
[occurrences
.length
- 1].getTextRange().getEndOffset();
566 PsiStatement
[] statements
= switchStatement
.getBody().getStatements();
567 boolean isInDifferentCases
= false;
568 for (PsiStatement statement
: statements
) {
569 if (statement
instanceof PsiSwitchLabelStatement
) {
570 int caseOffset
= statement
.getTextRange().getStartOffset();
571 if (startOffset
< caseOffset
&& caseOffset
< endOffset
) {
572 isInDifferentCases
= true;
577 if (isInDifferentCases
) {
578 anchor
= switchStatement
;
586 public static boolean isMethodUsage(PsiElement element
) {
587 if (element
instanceof PsiEnumConstant
) return true;
588 if (!(element
instanceof PsiJavaCodeReferenceElement
)) return false;
589 PsiElement parent
= element
.getParent();
590 if (parent
instanceof PsiCall
) {
593 else if (parent
instanceof PsiAnonymousClass
) {
594 return element
.equals(((PsiAnonymousClass
)parent
).getBaseClassReference());
599 public static PsiExpressionList
getArgumentListByMethodReference(PsiElement ref
) {
600 if (ref
instanceof PsiEnumConstant
) return ((PsiEnumConstant
)ref
).getArgumentList();
601 PsiElement parent
= ref
.getParent();
602 if (parent
instanceof PsiCall
) {
603 return ((PsiCall
)parent
).getArgumentList();
605 else if (parent
instanceof PsiAnonymousClass
) {
606 return ((PsiNewExpression
)parent
.getParent()).getArgumentList();
608 LOG
.assertTrue(false);
612 public static PsiCall
getCallExpressionByMethodReference(PsiElement ref
) {
613 if (ref
instanceof PsiEnumConstant
) return (PsiCall
)ref
;
614 PsiElement parent
= ref
.getParent();
615 if (parent
instanceof PsiMethodCallExpression
) {
616 return (PsiMethodCallExpression
)parent
;
618 else if (parent
instanceof PsiNewExpression
) {
619 return (PsiNewExpression
)parent
;
621 else if (parent
instanceof PsiAnonymousClass
) {
622 return (PsiNewExpression
)parent
.getParent();
625 LOG
.assertTrue(false);
631 * @return List of highlighters
633 public static List
<RangeHighlighter
> highlightAllOccurences(Project project
, PsiElement
[] occurences
, Editor editor
) {
634 ArrayList
<RangeHighlighter
> highlighters
= new ArrayList
<RangeHighlighter
>();
635 HighlightManager highlightManager
= HighlightManager
.getInstance(project
);
636 EditorColorsManager colorsManager
= EditorColorsManager
.getInstance();
637 TextAttributes attributes
= colorsManager
.getGlobalScheme().getAttributes(EditorColors
.SEARCH_RESULT_ATTRIBUTES
);
638 if (occurences
.length
> 1) {
639 for (PsiElement occurrence
: occurences
) {
640 final RangeMarker rangeMarker
= occurrence
.getUserData(ElementToWorkOn
.TEXT_RANGE
);
641 if (rangeMarker
!= null) {
643 .addRangeHighlight(editor
, rangeMarker
.getStartOffset(), rangeMarker
.getEndOffset(), attributes
, true, highlighters
);
646 final TextRange textRange
= occurrence
.getTextRange();
647 highlightManager
.addRangeHighlight(editor
, textRange
.getStartOffset(), textRange
.getEndOffset(), attributes
, true, highlighters
);
654 public static String
createTempVar(PsiExpression expr
, PsiElement context
, boolean declareFinal
) throws IncorrectOperationException
{
655 PsiElement anchorStatement
= getParentStatement(context
, true);
656 LOG
.assertTrue(anchorStatement
!= null && anchorStatement
.getParent() != null);
658 Project project
= expr
.getProject();
659 String
[] suggestedNames
=
660 JavaCodeStyleManager
.getInstance(project
).suggestVariableName(VariableKind
.LOCAL_VARIABLE
, null, expr
, null).names
;
661 final String prefix
= suggestedNames
[0];
662 final String id
= JavaCodeStyleManager
.getInstance(project
).suggestUniqueVariableName(prefix
, context
, true);
664 PsiElementFactory factory
= JavaPsiFacade
.getInstance(expr
.getProject()).getElementFactory();
666 if (expr
instanceof PsiParenthesizedExpression
) {
667 PsiExpression expr1
= ((PsiParenthesizedExpression
)expr
).getExpression();
672 PsiDeclarationStatement decl
= factory
.createVariableDeclarationStatement(id
, expr
.getType(), expr
);
674 PsiUtil
.setModifierProperty(((PsiLocalVariable
)decl
.getDeclaredElements()[0]), PsiModifier
.FINAL
, true);
676 anchorStatement
.getParent().addBefore(decl
, anchorStatement
);
681 public static int verifySafeCopyExpression(PsiElement expr
) {
682 return verifySafeCopyExpressionSubElement(expr
);
686 private static int verifySafeCopyExpressionSubElement(PsiElement element
) {
687 int result
= EXPR_COPY_SAFE
;
688 if (element
== null) return result
;
690 if (element
instanceof PsiThisExpression
|| element
instanceof PsiSuperExpression
|| element
instanceof PsiIdentifier
) {
691 return EXPR_COPY_SAFE
;
694 if (element
instanceof PsiMethodCallExpression
) {
695 result
= EXPR_COPY_UNSAFE
;
698 if (element
instanceof PsiNewExpression
) {
699 return EXPR_COPY_PROHIBITED
;
702 if (element
instanceof PsiAssignmentExpression
) {
703 return EXPR_COPY_PROHIBITED
;
706 if (isPlusPlusOrMinusMinus(element
)) {
707 return EXPR_COPY_PROHIBITED
;
710 PsiElement
[] children
= element
.getChildren();
712 for (PsiElement child
: children
) {
713 int childResult
= verifySafeCopyExpressionSubElement(child
);
714 result
= Math
.max(result
, childResult
);
719 public static PsiExpression
convertInitializerToNormalExpression(PsiExpression expression
, PsiType forcedReturnType
)
720 throws IncorrectOperationException
{
721 if (expression
instanceof PsiArrayInitializerExpression
) {
722 return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression
)expression
, forcedReturnType
);
727 private static PsiExpression
createNewExpressionFromArrayInitializer(PsiArrayInitializerExpression initializer
, PsiType forcedType
)
728 throws IncorrectOperationException
{
729 PsiType initializerType
= null;
730 if (initializer
!= null) {
731 // initializerType = myExpresssion.getType();
732 if (forcedType
!= null) {
733 initializerType
= forcedType
;
736 initializerType
= getTypeByExpression(initializer
);
739 if (initializerType
== null) {
742 LOG
.assertTrue(initializerType
instanceof PsiArrayType
);
743 PsiElementFactory factory
= JavaPsiFacade
.getInstance(initializer
.getProject()).getElementFactory();
744 PsiNewExpression result
=
745 (PsiNewExpression
)factory
.createExpressionFromText("new " + initializerType
.getPresentableText() + "{}", null);
746 result
= (PsiNewExpression
)CodeStyleManager
.getInstance(initializer
.getProject()).reformat(result
);
747 PsiArrayInitializerExpression arrayInitializer
= result
.getArrayInitializer();
748 LOG
.assertTrue(arrayInitializer
!= null);
749 arrayInitializer
.replace(initializer
);
753 public static void abstractizeMethod(PsiClass targetClass
, PsiMethod method
) throws IncorrectOperationException
{
754 PsiCodeBlock body
= method
.getBody();
759 PsiUtil
.setModifierProperty(method
, PsiModifier
.ABSTRACT
, true);
760 PsiUtil
.setModifierProperty(method
, PsiModifier
.FINAL
, false);
761 PsiUtil
.setModifierProperty(method
, PsiModifier
.SYNCHRONIZED
, false);
762 PsiUtil
.setModifierProperty(method
, PsiModifier
.NATIVE
, false);
764 if (!targetClass
.isInterface()) {
765 PsiUtil
.setModifierProperty(targetClass
, PsiModifier
.ABSTRACT
, true);
768 removeFinalParameters(method
);
771 public static boolean isInsideAnonymous(PsiElement element
, PsiElement upTo
) {
772 for (PsiElement current
= element
; current
!= null && current
!= upTo
; current
= current
.getParent()) {
773 if (current
instanceof PsiAnonymousClass
) return true;
778 public static PsiExpression
unparenthesizeExpression(PsiExpression expression
) {
779 while (expression
instanceof PsiParenthesizedExpression
) {
780 final PsiExpression innerExpression
= ((PsiParenthesizedExpression
)expression
).getExpression();
781 if (innerExpression
== null) return expression
;
782 expression
= innerExpression
;
787 public static PsiExpression
outermostParenthesizedExpression(PsiExpression expression
) {
788 while (expression
.getParent() instanceof PsiParenthesizedExpression
) {
789 expression
= (PsiParenthesizedExpression
)expression
.getParent();
794 public static String
getNewInnerClassName(PsiClass aClass
, String oldInnerClassName
, String newName
) {
795 if (!oldInnerClassName
.endsWith(aClass
.getName())) return newName
;
796 StringBuilder buffer
= new StringBuilder(oldInnerClassName
);
797 buffer
.replace(buffer
.length() - aClass
.getName().length(), buffer
.length(), newName
);
798 return buffer
.toString();
801 public static boolean isSuperOrThisCall(PsiStatement statement
, boolean testForSuper
, boolean testForThis
) {
802 if (!(statement
instanceof PsiExpressionStatement
)) return false;
803 PsiExpression expression
= ((PsiExpressionStatement
)statement
).getExpression();
804 if (!(expression
instanceof PsiMethodCallExpression
)) return false;
805 final PsiReferenceExpression methodExpression
= ((PsiMethodCallExpression
)expression
).getMethodExpression();
807 if ("super".equals(methodExpression
.getText())) return true;
810 if ("this".equals(methodExpression
.getText())) return true;
816 public static void visitImplicitSuperConstructorUsages(PsiClass subClass
,
817 final ImplicitConstructorUsageVisitor implicitConstructorUsageVistor
,
818 PsiClass superClass
) {
819 final PsiMethod baseDefaultConstructor
= findDefaultConstructor(superClass
);
820 final PsiMethod
[] constructors
= subClass
.getConstructors();
821 if (constructors
.length
> 0) {
822 for (PsiMethod constructor
: constructors
) {
823 final PsiStatement
[] statements
= constructor
.getBody().getStatements();
824 if (statements
.length
< 1 || !isSuperOrThisCall(statements
[0], true, true)) {
825 implicitConstructorUsageVistor
.visitConstructor(constructor
, baseDefaultConstructor
);
830 implicitConstructorUsageVistor
.visitClassWithoutConstructors(subClass
);
834 private static PsiMethod
findDefaultConstructor(final PsiClass aClass
) {
835 final PsiMethod
[] constructors
= aClass
.getConstructors();
836 for (PsiMethod constructor
: constructors
) {
837 if (constructor
.getParameterList().getParametersCount() == 0) return constructor
;
843 public static interface ImplicitConstructorUsageVisitor
{
844 void visitConstructor(PsiMethod constructor
, PsiMethod baseConstructor
);
846 void visitClassWithoutConstructors(PsiClass aClass
);
849 public interface Graph
<T
> {
850 Set
<T
> getVertices();
852 Set
<T
> getTargets(T source
);
856 * Returns subset of <code>graph.getVertices()</code> that is a tranistive closure (by <code>graph.getTargets()<code>)
857 * of the following property: initialRelation.value() of vertex or <code>graph.getTargets(vertex)</code> is true.
859 * Note that <code>graph.getTargets()</code> is not neccesrily a subset of <code>graph.getVertex()</code>
862 * @param initialRelation
863 * @return subset of graph.getVertices()
865 public static <T
> Set
<T
> transitiveClosure(Graph
<T
> graph
, Condition
<T
> initialRelation
) {
866 Set
<T
> result
= new HashSet
<T
>();
868 final Set
<T
> vertices
= graph
.getVertices();
872 for (T currentVertex
: vertices
) {
873 if (!result
.contains(currentVertex
)) {
874 if (!initialRelation
.value(currentVertex
)) {
875 Set
<T
> targets
= graph
.getTargets(currentVertex
);
876 for (T currentTarget
: targets
) {
877 if (result
.contains(currentTarget
) || initialRelation
.value(currentTarget
)) {
878 result
.add(currentVertex
);
885 result
.add(currentVertex
);
894 public static boolean equivalentTypes(PsiType t1
, PsiType t2
, PsiManager manager
) {
895 while (t1
instanceof PsiArrayType
) {
896 if (!(t2
instanceof PsiArrayType
)) return false;
897 t1
= ((PsiArrayType
)t1
).getComponentType();
898 t2
= ((PsiArrayType
)t2
).getComponentType();
901 if (t1
instanceof PsiPrimitiveType
) {
902 return t2
instanceof PsiPrimitiveType
&& t1
.equals(t2
);
905 return manager
.areElementsEquivalent(PsiUtil
.resolveClassInType(t1
), PsiUtil
.resolveClassInType(t2
));
908 public static List
<PsiVariable
> collectReferencedVariables(PsiElement scope
) {
909 final List
<PsiVariable
> result
= new ArrayList
<PsiVariable
>();
910 scope
.accept(new JavaRecursiveElementWalkingVisitor() {
911 @Override public void visitReferenceExpression(PsiReferenceExpression expression
) {
912 final PsiElement element
= expression
.resolve();
913 if (element
instanceof PsiVariable
) {
914 result
.add((PsiVariable
)element
);
916 final PsiExpression qualifier
= expression
.getQualifierExpression();
917 if (qualifier
!= null) {
918 qualifier
.accept(this);
925 public static boolean isModifiedInScope(PsiVariable variable
, PsiElement scope
) {
926 for (PsiReference reference
: ReferencesSearch
.search(variable
, new LocalSearchScope(scope
), false)) {
927 if (isAssignmentLHS(reference
.getElement())) return true;
932 private static String
getNameOfReferencedParameter(PsiDocTag tag
) {
933 LOG
.assertTrue("param".equals(tag
.getName()));
934 final PsiElement
[] dataElements
= tag
.getDataElements();
935 if (dataElements
.length
< 1) return null;
936 return dataElements
[0].getText();
939 public static void fixJavadocsForParams(PsiMethod method
, Set
<PsiParameter
> newParameters
) throws IncorrectOperationException
{
940 final PsiDocComment docComment
= method
.getDocComment();
941 if (docComment
== null) return;
942 final PsiParameter
[] parameters
= method
.getParameterList().getParameters();
943 final PsiDocTag
[] paramTags
= docComment
.findTagsByName("param");
944 if (parameters
.length
> 0 && newParameters
.size() < parameters
.length
&& paramTags
.length
== 0) return;
945 Map
<PsiParameter
, PsiDocTag
> tagForParam
= new HashMap
<PsiParameter
, PsiDocTag
>();
946 for (PsiParameter parameter
: parameters
) {
947 boolean found
= false;
948 for (PsiDocTag paramTag
: paramTags
) {
949 if (parameter
.getName().equals(getNameOfReferencedParameter(paramTag
))) {
950 tagForParam
.put(parameter
, paramTag
);
955 if (!found
&& !newParameters
.contains(parameter
)) {
956 tagForParam
.put(parameter
, null);
960 List
<PsiDocTag
> newTags
= new ArrayList
<PsiDocTag
>();
961 for (PsiParameter parameter
: parameters
) {
962 if (tagForParam
.containsKey(parameter
)) {
963 final PsiDocTag psiDocTag
= tagForParam
.get(parameter
);
964 if (psiDocTag
!= null) {
965 newTags
.add((PsiDocTag
)psiDocTag
.copy());
969 newTags
.add(JavaPsiFacade
.getInstance(method
.getProject()).getElementFactory().createParamTag(parameter
.getName(), ""));
972 PsiDocTag anchor
= paramTags
.length
> 0 ? paramTags
[paramTags
.length
- 1] : null;
973 for (PsiDocTag psiDocTag
: newTags
) {
974 anchor
= (PsiDocTag
)docComment
.addAfter(psiDocTag
, anchor
);
976 for (PsiDocTag paramTag
: paramTags
) {
981 public static PsiDirectory
createPackageDirectoryInSourceRoot(PackageWrapper aPackage
, final VirtualFile sourceRoot
)
982 throws IncorrectOperationException
{
983 final PsiDirectory
[] directories
= aPackage
.getDirectories();
984 for (PsiDirectory directory
: directories
) {
985 if (VfsUtil
.isAncestor(sourceRoot
, directory
.getVirtualFile(), false)) {
989 String qNameToCreate
= qNameToCreateInSourceRoot(aPackage
, sourceRoot
);
990 final String
[] shortNames
= qNameToCreate
.split("\\.");
991 PsiDirectory current
= aPackage
.getManager().findDirectory(sourceRoot
);
992 LOG
.assertTrue(current
!= null);
993 for (String shortName
: shortNames
) {
994 PsiDirectory subdirectory
= current
.findSubdirectory(shortName
);
995 if (subdirectory
== null) {
996 subdirectory
= current
.createSubdirectory(shortName
);
998 current
= subdirectory
;
1003 public static String
qNameToCreateInSourceRoot(PackageWrapper aPackage
, final VirtualFile sourceRoot
) throws IncorrectOperationException
{
1004 String targetQName
= aPackage
.getQualifiedName();
1005 String sourceRootPackage
=
1006 ProjectRootManager
.getInstance(aPackage
.getManager().getProject()).getFileIndex().getPackageNameByDirectory(sourceRoot
);
1007 if (!canCreateInSourceRoot(sourceRootPackage
, targetQName
)) {
1008 throw new IncorrectOperationException(
1009 "Cannot create package '" + targetQName
+ "' in source folder " + sourceRoot
.getPresentableUrl());
1011 String result
= targetQName
.substring(sourceRootPackage
.length());
1012 if (StringUtil
.startsWithChar(result
, '.')) result
= result
.substring(1); // remove initial '.'
1016 public static boolean canCreateInSourceRoot(final String sourceRootPackage
, final String targetQName
) {
1017 if (sourceRootPackage
== null || !targetQName
.startsWith(sourceRootPackage
)) return false;
1018 if (sourceRootPackage
.length() == 0 || targetQName
.length() == sourceRootPackage
.length()) return true;
1019 return targetQName
.charAt(sourceRootPackage
.length()) == '.';
1024 public static PsiDirectory
findPackageDirectoryInSourceRoot(PackageWrapper aPackage
, final VirtualFile sourceRoot
) {
1025 final PsiDirectory
[] directories
= aPackage
.getDirectories();
1026 for (PsiDirectory directory
: directories
) {
1027 if (VfsUtil
.isAncestor(sourceRoot
, directory
.getVirtualFile(), false)) {
1031 String qNameToCreate
;
1033 qNameToCreate
= qNameToCreateInSourceRoot(aPackage
, sourceRoot
);
1035 catch (IncorrectOperationException e
) {
1038 final String
[] shortNames
= qNameToCreate
.split("\\.");
1039 PsiDirectory current
= aPackage
.getManager().findDirectory(sourceRoot
);
1040 LOG
.assertTrue(current
!= null);
1041 for (String shortName
: shortNames
) {
1042 PsiDirectory subdirectory
= current
.findSubdirectory(shortName
);
1043 if (subdirectory
== null) {
1046 current
= subdirectory
;
1051 public static class ConditionCache
<T
> implements Condition
<T
> {
1052 private final Condition
<T
> myCondition
;
1053 private final HashSet
<T
> myProcessedSet
= new HashSet
<T
>();
1054 private final HashSet
<T
> myTrueSet
= new HashSet
<T
>();
1056 public ConditionCache(Condition
<T
> condition
) {
1057 myCondition
= condition
;
1060 public boolean value(T object
) {
1061 if (!myProcessedSet
.contains(object
)) {
1062 myProcessedSet
.add(object
);
1063 final boolean value
= myCondition
.value(object
);
1065 myTrueSet
.add(object
);
1070 return myTrueSet
.contains(object
);
1074 public static class IsDescendantOf
implements Condition
<PsiClass
> {
1075 private final PsiClass myClass
;
1076 private final ConditionCache
<PsiClass
> myConditionCache
;
1078 public IsDescendantOf(PsiClass aClass
) {
1080 myConditionCache
= new ConditionCache
<PsiClass
>(new Condition
<PsiClass
>() {
1081 public boolean value(PsiClass aClass
) {
1082 return InheritanceUtil
.isInheritorOrSelf(aClass
, myClass
, true);
1087 public boolean value(PsiClass aClass
) {
1088 return myConditionCache
.value(aClass
);
1093 public static PsiTypeParameterList
createTypeParameterListWithUsedTypeParameters(@NotNull final PsiElement
... elements
) {
1094 return createTypeParameterListWithUsedTypeParameters(null, elements
);
1098 public static PsiTypeParameterList
createTypeParameterListWithUsedTypeParameters(final PsiTypeParameterList fromList
, @NotNull final PsiElement
... elements
) {
1099 if (elements
.length
== 0) return null;
1100 final Set
<PsiTypeParameter
> used
= new HashSet
<PsiTypeParameter
>();
1101 for (final PsiElement element
: elements
) {
1102 if (element
== null) continue;
1103 element
.accept(new JavaRecursiveElementWalkingVisitor() {
1105 @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference
) {
1106 super.visitReferenceElement(reference
);
1107 if (!reference
.isQualified()) {
1108 final PsiElement resolved
= reference
.resolve();
1109 if (resolved
instanceof PsiTypeParameter
) {
1110 final PsiTypeParameter typeParameter
= (PsiTypeParameter
)resolved
;
1111 if (PsiTreeUtil
.isAncestor(typeParameter
.getOwner(), element
, true)) {
1112 used
.add(typeParameter
);
1119 public void visitExpression(final PsiExpression expression
) {
1120 super.visitExpression(expression
);
1121 final PsiType type
= expression
.getType();
1122 final PsiClass resolved
= PsiUtil
.resolveClassInType(type
);
1123 if (resolved
instanceof PsiTypeParameter
&& PsiTreeUtil
.isAncestor(((PsiTypeParameter
)resolved
).getOwner(), element
, true)){
1124 used
.add((PsiTypeParameter
)resolved
);
1130 if (fromList
!= null) {
1131 used
.retainAll(Arrays
.asList(fromList
.getTypeParameters()));
1134 PsiTypeParameter
[] typeParameters
= used
.toArray(new PsiTypeParameter
[used
.size()]);
1136 Arrays
.sort(typeParameters
, new Comparator
<PsiTypeParameter
>() {
1137 public int compare(final PsiTypeParameter tp1
, final PsiTypeParameter tp2
) {
1138 return tp1
.getTextRange().getStartOffset() - tp2
.getTextRange().getStartOffset();
1142 final PsiElementFactory elementFactory
= JavaPsiFacade
.getInstance(elements
[0].getProject()).getElementFactory();
1144 final PsiClass aClass
= elementFactory
.createClassFromText("class A {}", null);
1145 PsiTypeParameterList list
= aClass
.getTypeParameterList();
1146 assert list
!= null;
1147 for (final PsiTypeParameter typeParameter
: typeParameters
) {
1148 list
.add(typeParameter
);
1152 catch (IncorrectOperationException e
) {