i18n: java-impl
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / util / RefactoringUtil.java
1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.refactoring.util;
3
4 import com.intellij.codeInsight.ExpectedTypeInfo;
5 import com.intellij.codeInsight.ExpectedTypesProvider;
6 import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
7 import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
8 import com.intellij.codeInsight.highlighting.HighlightManager;
9 import com.intellij.java.refactoring.JavaRefactoringBundle;
10 import com.intellij.lang.java.JavaLanguage;
11 import com.intellij.openapi.diagnostic.Logger;
12 import com.intellij.openapi.editor.Editor;
13 import com.intellij.openapi.editor.RangeMarker;
14 import com.intellij.openapi.editor.colors.EditorColors;
15 import com.intellij.openapi.editor.markup.RangeHighlighter;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.openapi.roots.ProjectRootManager;
18 import com.intellij.openapi.util.*;
19 import com.intellij.openapi.util.text.StringUtil;
20 import com.intellij.openapi.vfs.VfsUtilCore;
21 import com.intellij.openapi.vfs.VirtualFile;
22 import com.intellij.psi.*;
23 import com.intellij.psi.codeStyle.CodeStyleManager;
24 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
25 import com.intellij.psi.codeStyle.VariableKind;
26 import com.intellij.psi.impl.PsiImplUtil;
27 import com.intellij.psi.javadoc.PsiDocComment;
28 import com.intellij.psi.javadoc.PsiDocTag;
29 import com.intellij.psi.javadoc.PsiDocTagValue;
30 import com.intellij.psi.search.GlobalSearchScopes;
31 import com.intellij.psi.search.LocalSearchScope;
32 import com.intellij.psi.search.SearchScope;
33 import com.intellij.psi.search.searches.ReferencesSearch;
34 import com.intellij.psi.util.*;
35 import com.intellij.refactoring.PackageWrapper;
36 import com.intellij.refactoring.RefactoringBundle;
37 import com.intellij.refactoring.introduceField.ElementToWorkOn;
38 import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
39 import com.intellij.util.IncorrectOperationException;
40 import com.intellij.util.ObjectUtils;
41 import com.intellij.util.containers.Stack;
42 import com.intellij.util.text.UniqueNameGenerator;
43 import gnu.trove.THashMap;
44 import org.jetbrains.annotations.Contract;
45 import org.jetbrains.annotations.NonNls;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48
49 import java.util.*;
50
51 public final class RefactoringUtil {
52   private static final Logger LOG = Logger.getInstance(RefactoringUtil.class);
53   public static final int EXPR_COPY_SAFE = 0;
54   public static final int EXPR_COPY_UNSAFE = 1;
55   public static final int EXPR_COPY_PROHIBITED = 2;
56
57   private RefactoringUtil() {
58   }
59
60   public static boolean isSourceRoot(final PsiDirectory directory) {
61     if (directory.getManager() == null) return false;
62     final Project project = directory.getProject();
63     final VirtualFile virtualFile = directory.getVirtualFile();
64     final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(project).getFileIndex().getSourceRootForFile(virtualFile);
65     return Comparing.equal(virtualFile, sourceRootForFile);
66   }
67
68   public static boolean isInStaticContext(PsiElement element, @Nullable final PsiClass aClass) {
69     return PsiUtil.getEnclosingStaticElement(element, aClass) != null;
70   }
71
72   /**
73    * @deprecated use {@link PsiTypesUtil#hasUnresolvedComponents(PsiType)}
74    */
75   @Deprecated
76   public static boolean isResolvableType(PsiType type) {
77     return !PsiTypesUtil.hasUnresolvedComponents(type);
78   }
79
80   public static PsiElement replaceOccurenceWithFieldRef(PsiExpression occurrence, PsiField newField, PsiClass destinationClass)
81     throws IncorrectOperationException {
82     final PsiManager manager = destinationClass.getManager();
83     final String fieldName = newField.getName();
84     final JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
85     final PsiElement element = occurrence.getUserData(ElementToWorkOn.PARENT);
86     final PsiVariable psiVariable = facade.getResolveHelper().resolveAccessibleReferencedVariable(fieldName, element != null ? element : occurrence);
87     final PsiElementFactory factory = facade.getElementFactory();
88     if (psiVariable != null && psiVariable.equals(newField)) {
89       return IntroduceVariableBase.replace(occurrence, factory.createExpressionFromText(fieldName, null), manager.getProject());
90     }
91     else {
92       final PsiReferenceExpression ref = (PsiReferenceExpression)factory.createExpressionFromText("this." + fieldName, null);
93       if (!occurrence.isValid()) return null;
94       if (newField.hasModifierProperty(PsiModifier.STATIC)) {
95         ref.setQualifierExpression(factory.createReferenceExpression(destinationClass));
96       }
97       return IntroduceVariableBase.replace(occurrence, ref, manager.getProject());
98     }
99   }
100
101   /**
102    * @see JavaCodeStyleManager#suggestUniqueVariableName(String, PsiElement, boolean)
103    * Cannot use method from code style manager: a collision with fieldToReplace is not a collision
104    */
105   public static String suggestUniqueVariableName(String baseName, PsiElement place, PsiField fieldToReplace) {
106     for(int index = 0;;index++) {
107       final String name = index > 0 ? baseName + index : baseName;
108       final PsiManager manager = place.getManager();
109       PsiResolveHelper helper = JavaPsiFacade.getInstance(manager.getProject()).getResolveHelper();
110       PsiVariable refVar = helper.resolveAccessibleReferencedVariable(name, place);
111       if (refVar != null && !manager.areElementsEquivalent(refVar, fieldToReplace)) continue;
112       final boolean[] found = {false};
113       place.accept(new JavaRecursiveElementWalkingVisitor() {
114         @Override
115         public void visitClass(PsiClass aClass) {
116
117         }
118
119         @Override
120         public void visitVariable(PsiVariable variable) {
121           if (name.equals(variable.getName())) {
122             found[0] = true;
123             stopWalking();
124           }
125         }
126       });
127       if (found[0]) {
128         continue;
129       }
130
131       return name;
132     }
133   }
134
135   @Nullable
136   public static String suggestNewOverriderName(String oldOverriderName, String oldBaseName, String newBaseName) {
137     if (oldOverriderName.equals(oldBaseName)) {
138       return newBaseName;
139     }
140     int i;
141     if (oldOverriderName.startsWith(oldBaseName)) {
142       i = 0;
143     }
144     else {
145       i = StringUtil.indexOfIgnoreCase(oldOverriderName, oldBaseName, 0);
146     }
147     if (i >= 0) {
148       String newOverriderName = oldOverriderName.substring(0, i);
149       if (Character.isUpperCase(oldOverriderName.charAt(i))) {
150         newOverriderName += StringUtil.capitalize(newBaseName);
151       }
152       else {
153         newOverriderName += newBaseName;
154       }
155       final int j = i + oldBaseName.length();
156       if (j < oldOverriderName.length()) {
157         newOverriderName += oldOverriderName.substring(j);
158       }
159
160       return newOverriderName;
161     }
162     return null;
163   }
164
165   public static boolean hasOnDemandStaticImport(final PsiElement element, final PsiClass aClass) {
166     if (element.getContainingFile() instanceof PsiJavaFile) {
167       final PsiImportList importList = ((PsiJavaFile)element.getContainingFile()).getImportList();
168       if (importList != null) {
169         final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements();
170         return Arrays.stream(importStaticStatements).anyMatch(stmt -> stmt.isOnDemand() && stmt.resolveTargetClass() == aClass);
171       }
172     }
173     return false;
174   }
175
176   public static PsiElement replaceElementsWithMap(PsiElement replaceIn, final Map<PsiElement, PsiElement> elementsToReplace) throws IncorrectOperationException {
177     for(Map.Entry<PsiElement, PsiElement> e: elementsToReplace.entrySet()) {
178       if (e.getKey() == replaceIn) {
179         return e.getKey().replace(e.getValue());
180       }
181       e.getKey().replace(e.getValue());
182     }
183     return replaceIn;
184   }
185
186   public static PsiElement getVariableScope(PsiLocalVariable localVar) {
187     if (!(localVar instanceof ImplicitVariable)) {
188       return localVar.getParent().getParent();
189     }
190     else {
191       return ((ImplicitVariable)localVar).getDeclarationScope();
192     }
193   }
194
195
196   @Nullable
197   public static PsiElement getParentStatement(@Nullable PsiElement place, boolean skipScopingStatements) {
198     PsiElement parent = place;
199     while (true) {
200       if (parent == null) return null;
201       if (parent instanceof PsiStatement) break;
202       if (parent instanceof PsiExpression && parent.getParent() instanceof PsiLambdaExpression) return parent;
203       parent = parent.getParent();
204     }
205     PsiElement parentStatement = parent;
206     while (parent instanceof PsiStatement && !(parent instanceof PsiSwitchLabeledRuleStatement)) {
207       if (!skipScopingStatements && ((parent instanceof PsiForStatement && parentStatement == ((PsiForStatement)parent).getBody()) || (
208         parent instanceof PsiForeachStatement && parentStatement == ((PsiForeachStatement)parent).getBody()) || (
209         parent instanceof PsiWhileStatement && parentStatement == ((PsiWhileStatement)parent).getBody()) || (
210         parent instanceof PsiIfStatement &&
211         (parentStatement == ((PsiIfStatement)parent).getThenBranch() || parentStatement == ((PsiIfStatement)parent).getElseBranch())))) {
212         return parentStatement;
213       }
214       parentStatement = parent;
215       parent = parent.getParent();
216     }
217     return parentStatement;
218   }
219
220
221   public static PsiElement getParentExpressionAnchorElement(PsiElement place) {
222     PsiElement parent = place.getUserData(ElementToWorkOn.PARENT);
223     if (place.getUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK) != null) return parent;
224     if (parent == null) parent = place;
225     while (true) {
226       if (isExpressionAnchorElement(parent)) return parent;
227       if (parent instanceof PsiExpression && parent.getParent() instanceof PsiLambdaExpression) return parent;
228       parent = parent.getParent();
229       if (parent == null) return null;
230     }
231   }
232
233
234   public static boolean isExpressionAnchorElement(PsiElement element) {
235     if (element instanceof PsiDeclarationStatement && element.getParent() instanceof PsiForStatement) return false;
236     return element instanceof PsiStatement || element instanceof PsiClassInitializer || element instanceof PsiField ||
237            element instanceof PsiMethod;
238   }
239
240   /**
241    * @param expression
242    * @return loop body if expression is part of some loop's condition or for loop's increment part
243    *         null otherwise
244    */
245   public static PsiElement getLoopForLoopCondition(PsiExpression expression) {
246     PsiExpression outermost = expression;
247     while (outermost.getParent() instanceof PsiExpression) {
248       outermost = (PsiExpression)outermost.getParent();
249     }
250     if (outermost.getParent() instanceof PsiForStatement) {
251       final PsiForStatement forStatement = (PsiForStatement)outermost.getParent();
252       if (forStatement.getCondition() == outermost) {
253         return forStatement;
254       }
255       else {
256         return null;
257       }
258     }
259     if (outermost.getParent() instanceof PsiExpressionStatement && outermost.getParent().getParent() instanceof PsiForStatement) {
260       final PsiForStatement forStatement = (PsiForStatement)outermost.getParent().getParent();
261       if (forStatement.getUpdate() == outermost.getParent()) {
262         return forStatement;
263       }
264       else {
265         return null;
266       }
267     }
268     if (outermost.getParent() instanceof PsiWhileStatement) {
269       return outermost.getParent();
270     }
271     if (outermost.getParent() instanceof PsiDoWhileStatement) {
272       return outermost.getParent();
273     }
274     return null;
275   }
276
277   public static PsiClass getThisResolveClass(final PsiReferenceExpression place) {
278     final JavaResolveResult resolveResult = place.advancedResolve(false);
279     final PsiElement scope = resolveResult.getCurrentFileResolveScope();
280     if (scope instanceof PsiClass) {
281       return (PsiClass)scope;
282     }
283     return null;
284   }
285
286   public static PsiCall getEnclosingConstructorCall(PsiJavaCodeReferenceElement ref) {
287     PsiElement parent = ref.getParent();
288     if (ref instanceof PsiReferenceExpression && parent instanceof PsiMethodCallExpression) return (PsiCall)parent;
289
290     if (parent instanceof PsiAnonymousClass) {
291       parent = parent.getParent();
292     }
293
294     return parent instanceof PsiNewExpression ? (PsiNewExpression)parent : null;
295   }
296
297   public static PsiMethod getEnclosingMethod(PsiElement element) {
298     final PsiElement container = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class, PsiLambdaExpression.class);
299     return container instanceof PsiMethod ? (PsiMethod)container : null;
300   }
301
302   public static void renameVariableReferences(PsiVariable variable, String newName, SearchScope scope) throws IncorrectOperationException {
303     renameVariableReferences(variable, newName, scope, false);
304   }
305
306   public static void renameVariableReferences(PsiVariable variable,
307                                               String newName,
308                                               SearchScope scope,
309                                               final boolean ignoreAccessScope) throws IncorrectOperationException {
310     for (PsiReference reference : ReferencesSearch.search(variable, scope, ignoreAccessScope)) {
311       reference.handleElementRename(newName);
312     }
313   }
314
315   public static boolean canBeDeclaredFinal(@NotNull PsiVariable variable) {
316     LOG.assertTrue(variable instanceof PsiLocalVariable || variable instanceof PsiParameter);
317     final boolean isReassigned = HighlightControlFlowUtil
318       .isReassigned(variable, new THashMap<>());
319     return !isReassigned;
320   }
321
322   /**
323    * removes a reference to the specified class from the reference list given
324    *
325    * @return if removed  - a reference to the class or null if there were no references to this class in the reference list
326    */
327   public static PsiJavaCodeReferenceElement removeFromReferenceList(PsiReferenceList refList, PsiClass aClass)
328     throws IncorrectOperationException {
329     PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
330     for (PsiJavaCodeReferenceElement ref : refs) {
331       if (ref.isReferenceTo(aClass)) {
332         PsiJavaCodeReferenceElement refCopy = (PsiJavaCodeReferenceElement)ref.copy();
333         ref.delete();
334         return refCopy;
335       }
336     }
337     return null;
338   }
339
340   public static PsiJavaCodeReferenceElement findReferenceToClass(PsiReferenceList refList, PsiClass aClass) {
341     PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
342     for (PsiJavaCodeReferenceElement ref : refs) {
343       if (ref.isReferenceTo(aClass)) {
344         return ref;
345       }
346     }
347     return null;
348   }
349
350   public static PsiType getTypeByExpressionWithExpectedType(PsiExpression expr) {
351     PsiElementFactory factory = JavaPsiFacade.getElementFactory(expr.getProject());
352     PsiType typeByExpression = getTypeByExpression(expr, factory);
353     PsiType type = typeByExpression;
354     final boolean isFunctionalType = LambdaUtil.notInferredType(type);
355     PsiType exprType = expr.getType();
356     final boolean detectConjunct = exprType instanceof PsiIntersectionType ||
357                                    exprType instanceof PsiWildcardType && ((PsiWildcardType)exprType).getBound() instanceof PsiIntersectionType ||
358                                    exprType instanceof PsiCapturedWildcardType && ((PsiCapturedWildcardType)exprType).getUpperBound() instanceof PsiIntersectionType;
359     if (type != null && !isFunctionalType && !detectConjunct) {
360       return type;
361     }
362     ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(expr, false);
363     if (expectedTypes.length == 1 || (isFunctionalType || detectConjunct) && expectedTypes.length > 0 ) {
364       if (typeByExpression != null && Arrays.stream(expectedTypes).anyMatch(typeInfo -> typeByExpression.isAssignableFrom(typeInfo.getType()))) {
365         return type;
366       }
367       type = expectedTypes[0].getType();
368       if (!type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return type;
369     }
370     return detectConjunct ? type : null;
371   }
372
373   public static PsiType getTypeByExpression(PsiExpression expr) {
374     PsiElementFactory factory = JavaPsiFacade.getElementFactory(expr.getProject());
375     PsiType type = getTypeByExpression(expr, factory);
376     if (LambdaUtil.notInferredType(type)) {
377       type = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, expr.getResolveScope());
378     }
379     return type;
380   }
381
382   private static PsiType getTypeByExpression(PsiExpression expr, final PsiElementFactory factory) {
383     PsiType type = RefactoringChangeUtil.getTypeByExpression(expr);
384     if (PsiType.NULL.equals(type)) {
385       ExpectedTypeInfo[] infos = ExpectedTypesProvider.getExpectedTypes(expr, false);
386       if (infos.length > 0) {
387         type = infos[0].getType();
388         if (type instanceof PsiPrimitiveType) {
389           type = infos.length > 1 && !(infos[1].getType() instanceof PsiPrimitiveType) ? infos[1].getType()
390                                                                                        : ((PsiPrimitiveType)type).getBoxedType(expr);
391         }
392       }
393       else {
394         type = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, expr.getResolveScope());
395       }
396     }
397
398     return type;
399   }
400
401   public static boolean isAssignmentLHS(@NotNull PsiElement element) {
402     return element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element);
403   }
404
405   private static void removeFinalParameters(PsiMethod method) throws IncorrectOperationException {
406     PsiParameterList paramList = method.getParameterList();
407     PsiParameter[] params = paramList.getParameters();
408
409     for (PsiParameter param : params) {
410       if (param.hasModifierProperty(PsiModifier.FINAL)) {
411         PsiUtil.setModifierProperty(param, PsiModifier.FINAL, false);
412       }
413     }
414   }
415
416   public static PsiElement getAnchorElementForMultipleExpressions(PsiExpression @NotNull [] occurrences, PsiElement scope) {
417     PsiElement anchor = null;
418     for (PsiExpression occurrence : occurrences) {
419       if (scope != null && !PsiTreeUtil.isAncestor(scope, occurrence, false)) {
420         continue;
421       }
422       PsiElement anchor1 = getParentExpressionAnchorElement(occurrence);
423
424       if (anchor1 == null) {
425         if (occurrence.isPhysical()) return null;
426         continue;
427       }
428
429       if (anchor == null) {
430         anchor = anchor1;
431       }
432       else {
433         PsiElement commonParent = PsiTreeUtil.findCommonParent(anchor, anchor1);
434         if (commonParent == null || anchor.getTextRange() == null || anchor1.getTextRange() == null) return null;
435         PsiElement firstAnchor = anchor.getTextRange().getStartOffset() < anchor1.getTextRange().getStartOffset() ? anchor : anchor1;
436         if (commonParent.equals(firstAnchor)) {
437           anchor = firstAnchor;
438         }
439         else {
440           if (commonParent instanceof PsiStatement) {
441             anchor = commonParent;
442           }
443           else {
444             PsiElement parent = firstAnchor;
445             while (!parent.getParent().equals(commonParent)) {
446               parent = parent.getParent();
447             }
448             final PsiElement newAnchor = getParentExpressionAnchorElement(parent);
449             if (newAnchor != null) {
450               anchor = newAnchor;
451             }
452             else {
453               anchor = parent;
454             }
455           }
456         }
457       }
458     }
459
460     if (anchor == null) return null;
461     if (occurrences.length > 1 && anchor.getParent().getParent() instanceof PsiSwitchStatement) {
462       PsiSwitchStatement switchStatement = (PsiSwitchStatement)anchor.getParent().getParent();
463       if (switchStatement.getBody().equals(anchor.getParent())) {
464         int startOffset = occurrences[0].getTextRange().getStartOffset();
465         int endOffset = occurrences[occurrences.length - 1].getTextRange().getEndOffset();
466         PsiStatement[] statements = switchStatement.getBody().getStatements();
467         boolean isInDifferentCases = false;
468         for (PsiStatement statement : statements) {
469           if (statement instanceof PsiSwitchLabelStatement) {
470             int caseOffset = statement.getTextRange().getStartOffset();
471             if (startOffset < caseOffset && caseOffset < endOffset) {
472               isInDifferentCases = true;
473               break;
474             }
475           }
476         }
477         if (isInDifferentCases) {
478           anchor = switchStatement;
479         }
480       }
481     }
482
483     return anchor;
484   }
485
486   public static boolean isMethodUsage(PsiElement element) {
487     if (element instanceof PsiEnumConstant) {
488       return JavaLanguage.INSTANCE.equals(element.getLanguage());
489     }
490     if (!(element instanceof PsiJavaCodeReferenceElement)) return false;
491     PsiElement parent = element.getParent();
492     if (parent instanceof PsiCall) {
493       return true;
494     }
495     else if (parent instanceof PsiAnonymousClass) {
496       return element.equals(((PsiAnonymousClass)parent).getBaseClassReference());
497     }
498     return false;
499   }
500
501   @Nullable
502   public static PsiExpressionList getArgumentListByMethodReference(PsiElement ref) {
503     if (ref instanceof PsiCall) return ((PsiCall)ref).getArgumentList();
504     PsiElement parent = ref.getParent();
505     if (parent instanceof PsiCall) {
506       return ((PsiCall)parent).getArgumentList();
507     }
508     else if (parent instanceof PsiAnonymousClass) {
509       return ((PsiNewExpression)parent.getParent()).getArgumentList();
510     }
511     LOG.assertTrue(false);
512     return null;
513   }
514
515   public static PsiCall getCallExpressionByMethodReference(PsiElement ref) {
516     if (ref instanceof PsiCall) return (PsiCall)ref;
517     PsiElement parent = ref.getParent();
518     if (parent instanceof PsiMethodCallExpression) {
519       return (PsiMethodCallExpression)parent;
520     }
521     else if (parent instanceof PsiNewExpression) {
522       return (PsiNewExpression)parent;
523     }
524     else if (parent instanceof PsiAnonymousClass) {
525       return (PsiNewExpression)parent.getParent();
526     }
527     else {
528       LOG.assertTrue(false);
529       return null;
530     }
531   }
532
533   /**
534    * @return List of highlighters
535    */
536   public static List<RangeHighlighter> highlightAllOccurrences(Project project, PsiElement[] occurrences, Editor editor) {
537     ArrayList<RangeHighlighter> highlighters = new ArrayList<>();
538     HighlightManager highlightManager = HighlightManager.getInstance(project);
539     if (occurrences.length > 1) {
540       for (PsiElement occurrence : occurrences) {
541         final RangeMarker rangeMarker = occurrence.getUserData(ElementToWorkOn.TEXT_RANGE);
542         if (rangeMarker != null && rangeMarker.isValid()) {
543           highlightManager.addRangeHighlight(editor, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(),
544                                              EditorColors.SEARCH_RESULT_ATTRIBUTES, true, highlighters);
545         }
546         else {
547           final TextRange textRange = occurrence.getTextRange();
548           highlightManager.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(),
549                                              EditorColors.SEARCH_RESULT_ATTRIBUTES, true, highlighters);
550         }
551       }
552     }
553     return highlighters;
554   }
555
556   public static String createTempVar(PsiExpression expr, PsiElement context, boolean declareFinal) throws IncorrectOperationException {
557     PsiElement anchorStatement = getParentStatement(context, true);
558     LOG.assertTrue(anchorStatement != null && anchorStatement.getParent() != null);
559
560     Project project = expr.getProject();
561     String[] suggestedNames =
562       JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null).names;
563     final String prefix = suggestedNames.length > 0 ? suggestedNames[0] : "var";
564     final String id = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(prefix, context, true);
565
566     PsiElementFactory factory = JavaPsiFacade.getElementFactory(expr.getProject());
567
568     if (expr instanceof PsiParenthesizedExpression) {
569       PsiExpression expr1 = ((PsiParenthesizedExpression)expr).getExpression();
570       if (expr1 != null) {
571         expr = expr1;
572       }
573     }
574     PsiDeclarationStatement decl = factory.createVariableDeclarationStatement(id, expr.getType(), expr);
575     if (declareFinal) {
576       PsiUtil.setModifierProperty(((PsiLocalVariable)decl.getDeclaredElements()[0]), PsiModifier.FINAL, true);
577     }
578     anchorStatement.getParent().addBefore(decl, anchorStatement);
579
580     return id;
581   }
582
583   public static int verifySafeCopyExpression(PsiElement expr) {
584     return verifySafeCopyExpressionSubElement(expr);
585
586   }
587
588   private static int verifySafeCopyExpressionSubElement(PsiElement element) {
589     int result = EXPR_COPY_SAFE;
590     if (element == null) return result;
591
592     if (element instanceof PsiThisExpression || element instanceof PsiSuperExpression || element instanceof PsiIdentifier) {
593       return EXPR_COPY_SAFE;
594     }
595
596     if (element instanceof PsiMethodCallExpression) {
597       result = EXPR_COPY_UNSAFE;
598     }
599
600     if (element instanceof PsiNewExpression) {
601       return EXPR_COPY_PROHIBITED;
602     }
603
604     if (element instanceof PsiAssignmentExpression) {
605       return EXPR_COPY_PROHIBITED;
606     }
607
608     if (PsiUtil.isIncrementDecrementOperation(element)) {
609       return EXPR_COPY_PROHIBITED;
610     }
611
612     PsiElement[] children = element.getChildren();
613
614     for (PsiElement child : children) {
615       int childResult = verifySafeCopyExpressionSubElement(child);
616       result = Math.max(result, childResult);
617     }
618     return result;
619   }
620
621   @Contract("null, _ -> null")
622   public static PsiExpression convertInitializerToNormalExpression(PsiExpression expression, PsiType forcedReturnType)
623     throws IncorrectOperationException {
624     if (expression instanceof PsiArrayInitializerExpression && (forcedReturnType == null || forcedReturnType instanceof PsiArrayType)) {
625       return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)expression, forcedReturnType);
626     }
627     return expression;
628   }
629
630   public static PsiExpression createNewExpressionFromArrayInitializer(PsiArrayInitializerExpression initializer, PsiType forcedType)
631     throws IncorrectOperationException {
632     PsiType initializerType = null;
633     if (initializer != null) {
634       if (forcedType != null) {
635         initializerType = forcedType;
636       }
637       else {
638         initializerType = getTypeByExpression(initializer);
639       }
640     }
641     if (initializerType == null) {
642       return initializer;
643     }
644     LOG.assertTrue(initializerType instanceof PsiArrayType);
645     PsiElementFactory factory = JavaPsiFacade.getElementFactory(initializer.getProject());
646     PsiNewExpression result =
647       (PsiNewExpression)factory.createExpressionFromText("new " + initializerType.getPresentableText() + "{}", null);
648     result = (PsiNewExpression)CodeStyleManager.getInstance(initializer.getProject()).reformat(result);
649     PsiArrayInitializerExpression arrayInitializer = result.getArrayInitializer();
650     LOG.assertTrue(arrayInitializer != null);
651     arrayInitializer.replace(initializer);
652     return result;
653   }
654
655   public static void makeMethodAbstract(@NotNull PsiClass targetClass, @NotNull PsiMethod method) throws IncorrectOperationException {
656     if (!method.hasModifierProperty(PsiModifier.DEFAULT)) {
657       PsiCodeBlock body = method.getBody();
658       if (body != null) {
659         body.delete();
660       }
661
662       PsiUtil.setModifierProperty(method, PsiModifier.ABSTRACT, true);
663     }
664
665     if (!targetClass.isInterface()) {
666       PsiUtil.setModifierProperty(targetClass, PsiModifier.ABSTRACT, true);
667       prepareForAbstract(method);
668     }
669     else {
670       prepareForInterface(method);
671     }
672
673   }
674
675   public static void makeMethodDefault(@NotNull PsiMethod method) throws IncorrectOperationException {
676     PsiUtil.setModifierProperty(method, PsiModifier.DEFAULT, !method.hasModifierProperty(PsiModifier.STATIC));
677     PsiUtil.setModifierProperty(method, PsiModifier.ABSTRACT, false);
678
679     prepareForInterface(method);
680   }
681
682   private static void prepareForInterface(PsiMethod method) {
683     PsiUtil.setModifierProperty(method, PsiModifier.PUBLIC, false);
684     PsiUtil.setModifierProperty(method, PsiModifier.PRIVATE, false);
685     PsiUtil.setModifierProperty(method, PsiModifier.PROTECTED, false);
686     prepareForAbstract(method);
687   }
688
689   private static void prepareForAbstract(PsiMethod method) {
690     PsiUtil.setModifierProperty(method, PsiModifier.FINAL, false);
691     PsiUtil.setModifierProperty(method, PsiModifier.SYNCHRONIZED, false);
692     PsiUtil.setModifierProperty(method, PsiModifier.NATIVE, false);
693     removeFinalParameters(method);
694   }
695
696   public static boolean isInsideAnonymousOrLocal(PsiElement element, PsiElement upTo) {
697     for (PsiElement current = element; current != null && current != upTo; current = current.getParent()) {
698       if (current instanceof PsiAnonymousClass) return true;
699       if (current instanceof PsiClass && current.getParent() instanceof PsiDeclarationStatement) {
700         return true;
701       }
702     }
703     return false;
704   }
705
706   public static PsiExpression unparenthesizeExpression(PsiExpression expression) {
707     while (expression instanceof PsiParenthesizedExpression) {
708       final PsiExpression innerExpression = ((PsiParenthesizedExpression)expression).getExpression();
709       if (innerExpression == null) return expression;
710       expression = innerExpression;
711     }
712     return expression;
713   }
714
715   public static PsiExpression outermostParenthesizedExpression(PsiExpression expression) {
716     while (expression.getParent() instanceof PsiParenthesizedExpression) {
717       expression = (PsiParenthesizedExpression)expression.getParent();
718     }
719     return expression;
720   }
721
722   public static String getNewInnerClassName(PsiClass aClass, String oldInnerClassName, String newName) {
723     String className = aClass.getName();
724     if (className == null || !oldInnerClassName.endsWith(className)) return newName;
725     StringBuilder buffer = new StringBuilder(oldInnerClassName);
726     buffer.replace(buffer.length() - className.length(), buffer.length(), newName);
727     return buffer.toString();
728   }
729
730   public static void visitImplicitSuperConstructorUsages(PsiClass subClass,
731                                                          final ImplicitConstructorUsageVisitor implicitConstructorUsageVisitor,
732                                                          PsiClass superClass) {
733     final PsiMethod baseDefaultConstructor = findDefaultConstructor(superClass);
734     final PsiMethod[] constructors = subClass.getConstructors();
735     if (constructors.length > 0) {
736       for (PsiMethod constructor : constructors) {
737         PsiCodeBlock body = constructor.getBody();
738         if (body == null) continue;
739         final PsiStatement[] statements = body.getStatements();
740         if (statements.length < 1 || !JavaHighlightUtil.isSuperOrThisCall(statements[0], true, true)) {
741           implicitConstructorUsageVisitor.visitConstructor(constructor, baseDefaultConstructor);
742         }
743       }
744     }
745     else {
746       implicitConstructorUsageVisitor.visitClassWithoutConstructors(subClass);
747     }
748   }
749
750   private static PsiMethod findDefaultConstructor(final PsiClass aClass) {
751     final PsiMethod[] constructors = aClass.getConstructors();
752     for (PsiMethod constructor : constructors) {
753       if (constructor.getParameterList().isEmpty()) return constructor;
754     }
755
756     return null;
757   }
758
759   public static void replaceMovedMemberTypeParameters(final PsiElement member,
760                                                       final Iterable<? extends PsiTypeParameter> parametersIterable,
761                                                       final PsiSubstitutor substitutor,
762                                                       final PsiElementFactory factory) {
763     final Map<PsiElement, PsiElement> replacement = new LinkedHashMap<>();
764     for (PsiTypeParameter parameter : parametersIterable) {
765       final PsiType substitutedType = substitutor.substitute(parameter);
766       final PsiType erasedType = substitutedType == null ? TypeConversionUtil.erasure(factory.createType(parameter))
767                                                          : substitutedType;
768       for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(member))) {
769         final PsiElement element = reference.getElement();
770         final PsiElement parent = element.getParent();
771         if (parent instanceof PsiTypeElement) {
772           if (substitutedType == null) {
773             //extends/implements list of type parameters: S extends List<T>
774             final PsiJavaCodeReferenceElement codeReferenceElement = PsiTreeUtil.getTopmostParentOfType(parent, PsiJavaCodeReferenceElement.class);
775             if (codeReferenceElement != null) {
776               final PsiJavaCodeReferenceElement copy = (PsiJavaCodeReferenceElement)codeReferenceElement.copy();
777               final PsiReferenceParameterList parameterList = copy.getParameterList();
778               if (parameterList != null) {
779                 parameterList.delete();
780               }
781               replacement.put(codeReferenceElement, copy);
782             }
783             else {
784               //nested types List<List<T> listOfLists;
785               PsiTypeElement topPsiTypeElement = PsiTreeUtil.getTopmostParentOfType(parent, PsiTypeElement.class);
786               if (topPsiTypeElement == null) {
787                 topPsiTypeElement = (PsiTypeElement)parent;
788               }
789               replacement.put(topPsiTypeElement, factory.createTypeElement(TypeConversionUtil.erasure(topPsiTypeElement.getType())));
790             }
791           }
792           else {
793             replacement.put(parent, factory.createTypeElement(substitutedType));
794           }
795         }
796         else if (element instanceof PsiJavaCodeReferenceElement && erasedType instanceof PsiClassType) {
797           replacement.put(element, factory.createReferenceElementByType((PsiClassType)erasedType));
798         }
799       }
800     }
801     for (PsiElement element : replacement.keySet()) {
802       if (element.isValid()) {
803         element.replace(replacement.get(element));
804       }
805     }
806   }
807
808   public static void renameConflictingTypeParameters(PsiMember memberCopy, PsiClass targetClass) {
809     if (memberCopy instanceof PsiTypeParameterListOwner && !memberCopy.hasModifierProperty(PsiModifier.STATIC)) {
810       UniqueNameGenerator nameGenerator = new UniqueNameGenerator();
811       PsiUtil.typeParametersIterable(targetClass).forEach(param -> {
812         String paramName = param.getName();
813         if (paramName != null) {
814           nameGenerator.addExistingName(paramName);
815         }
816       });
817
818       PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
819       PsiElementFactory factory = JavaPsiFacade.getElementFactory(memberCopy.getProject());
820       for (PsiTypeParameter parameter : ((PsiTypeParameterListOwner)memberCopy).getTypeParameters()) {
821         String parameterName = parameter.getName();
822         if (parameterName == null) continue;
823         if (!nameGenerator.isUnique(parameterName)) {
824           substitutor = substitutor.put(parameter, PsiSubstitutor.EMPTY.substitute(factory.createTypeParameter(nameGenerator.generateUniqueName(parameterName), PsiClassType.EMPTY_ARRAY)));
825         }
826       }
827       if (!PsiSubstitutor.EMPTY.equals(substitutor)) {
828         replaceMovedMemberTypeParameters(memberCopy, PsiUtil.typeParametersIterable((PsiTypeParameterListOwner)memberCopy), substitutor, factory);//rename usages in the method
829         for (Map.Entry<PsiTypeParameter, PsiType> entry : substitutor.getSubstitutionMap().entrySet()) {
830           entry.getKey().setName(entry.getValue().getCanonicalText()); //rename declaration after all usages renamed
831         }
832       }
833     }
834   }
835
836   @Nullable
837   public static PsiMethod getChainedConstructor(PsiMethod constructor) {
838     final PsiCodeBlock constructorBody = constructor.getBody();
839     if (constructorBody == null) return null;
840     final PsiStatement[] statements = constructorBody.getStatements();
841     if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) {
842       final PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
843       if (expression instanceof PsiMethodCallExpression) {
844         final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression;
845         final PsiReferenceExpression methodExpr = methodCallExpression.getMethodExpression();
846         if ("this".equals(methodExpr.getReferenceName())) {
847           return (PsiMethod)methodExpr.resolve();
848         }
849       }
850     }
851     return null;
852   }
853
854   public static boolean isInMovedElement(PsiElement element, Set<? extends PsiMember> membersToMove) {
855     for (PsiMember member : membersToMove) {
856       if (PsiTreeUtil.isAncestor(member, element, false)) return true;
857     }
858     return false;
859   }
860
861   public static boolean inImportStatement(PsiReference ref, PsiElement element) {
862     if (PsiTreeUtil.getParentOfType(element, PsiImportStatement.class) != null) return true;
863     final PsiFile containingFile = element.getContainingFile();
864     if (containingFile instanceof PsiJavaFile) {
865       final PsiImportList importList = ((PsiJavaFile)containingFile).getImportList();
866       if (importList != null) {
867         final TextRange refRange = ref.getRangeInElement().shiftRight(element.getTextRange().getStartOffset());
868         for (PsiImportStatementBase importStatementBase : importList.getAllImportStatements()) {
869           final TextRange textRange = importStatementBase.getTextRange();
870           if (textRange.contains(refRange)) {
871             return true;
872           }
873         }
874       }
875     }
876     return false;
877   }
878
879   public static PsiStatement putStatementInLoopBody(PsiStatement declaration,
880                                                   PsiElement container,
881                                                   PsiElement finalAnchorStatement) throws IncorrectOperationException {
882     return putStatementInLoopBody(declaration, container, finalAnchorStatement, false);
883   }
884
885   public static PsiStatement putStatementInLoopBody(PsiStatement declaration,
886                                                     PsiElement container,
887                                                     PsiElement finalAnchorStatement,
888                                                     boolean replaceBody)
889     throws IncorrectOperationException {
890     final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(container.getProject());
891     if(isLoopOrIf(container)) {
892       PsiStatement loopBody = getLoopBody(container, finalAnchorStatement);
893       PsiStatement loopBodyCopy = loopBody != null ? (PsiStatement) loopBody.copy() : null;
894       PsiBlockStatement blockStatement = (PsiBlockStatement)elementFactory
895         .createStatementFromText("{}", null);
896       blockStatement = (PsiBlockStatement) CodeStyleManager.getInstance(container.getProject()).reformat(blockStatement);
897       final PsiElement prevSibling = loopBody.getPrevSibling();
898       if(prevSibling instanceof PsiWhiteSpace) {
899         final PsiElement pprev = prevSibling.getPrevSibling();
900         if (!(pprev instanceof PsiComment) || !((PsiComment)pprev).getTokenType().equals(JavaTokenType.END_OF_LINE_COMMENT)) {
901           prevSibling.delete();
902         }
903       }
904       blockStatement = (PsiBlockStatement) loopBody.replace(blockStatement);
905       final PsiCodeBlock codeBlock = blockStatement.getCodeBlock();
906       declaration = (PsiStatement) codeBlock.add(declaration);
907       JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
908       if (loopBodyCopy != null && !replaceBody) codeBlock.add(loopBodyCopy);
909     } else if (container instanceof PsiLambdaExpression) {
910       PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)container;
911       final PsiElement invalidBody = lambdaExpression.getBody();
912       if (invalidBody == null) return declaration;
913
914       String lambdaParamListWithArrowAndComments = lambdaExpression.getText()
915         .substring(0, (declaration.isPhysical() ? declaration : invalidBody).getStartOffsetInParent());
916       final PsiLambdaExpression expressionFromText = (PsiLambdaExpression)elementFactory.createExpressionFromText(lambdaParamListWithArrowAndComments + "{}", lambdaExpression.getParent());
917       PsiCodeBlock newBody = (PsiCodeBlock)expressionFromText.getBody();
918       LOG.assertTrue(newBody != null);
919       newBody.add(declaration);
920
921       final PsiElement lambdaExpressionBody = lambdaExpression.getBody();
922       LOG.assertTrue(lambdaExpressionBody != null);
923       final PsiStatement lastBodyStatement;
924       if (PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression))) {
925         if (replaceBody) {
926           lastBodyStatement = null;
927         } else {
928           lastBodyStatement = elementFactory.createStatementFromText("a;", lambdaExpression);
929           ((PsiExpressionStatement)lastBodyStatement).getExpression().replace(lambdaExpressionBody);
930         }
931       }
932       else {
933         lastBodyStatement = elementFactory.createStatementFromText("return a;", lambdaExpression);
934         final PsiExpression returnValue = ((PsiReturnStatement)lastBodyStatement).getReturnValue();
935         LOG.assertTrue(returnValue != null);
936         returnValue.replace(lambdaExpressionBody);
937       }
938       if (lastBodyStatement != null) {
939         newBody.add(lastBodyStatement);
940       }
941
942       final PsiLambdaExpression copy = (PsiLambdaExpression)lambdaExpression.replace(expressionFromText);
943       newBody = (PsiCodeBlock)copy.getBody();
944       LOG.assertTrue(newBody != null);
945       declaration = newBody.getStatements()[0];
946       declaration = (PsiStatement)JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration);
947     }
948     return declaration;
949   }
950
951   @Nullable
952   private static PsiStatement getLoopBody(PsiElement container, PsiElement anchorStatement) {
953     if(container instanceof PsiLoopStatement) {
954       return ((PsiLoopStatement) container).getBody();
955     }
956     else if (container instanceof PsiIfStatement) {
957       final PsiStatement thenBranch = ((PsiIfStatement)container).getThenBranch();
958       if (thenBranch != null && PsiTreeUtil.isAncestor(thenBranch, anchorStatement, false)) {
959         return thenBranch;
960       }
961       final PsiStatement elseBranch = ((PsiIfStatement)container).getElseBranch();
962       if (elseBranch != null && PsiTreeUtil.isAncestor(elseBranch, anchorStatement, false)) {
963         return elseBranch;
964       }
965       LOG.assertTrue(false);
966     }
967     LOG.assertTrue(false);
968     return null;
969   }
970
971   public static boolean isLoopOrIf(PsiElement element) {
972     return element instanceof PsiLoopStatement || element instanceof PsiIfStatement;
973   }
974
975   public static PsiCodeBlock expandExpressionLambdaToCodeBlock(@NotNull PsiLambdaExpression lambdaExpression) {
976     final PsiElement body = lambdaExpression.getBody();
977     if (!(body instanceof PsiExpression)) return (PsiCodeBlock)body;
978
979     @NonNls String newLambdaText = "{";
980     if (!PsiType.VOID.equals(LambdaUtil.getFunctionalInterfaceReturnType(lambdaExpression))) newLambdaText += "return ";
981     newLambdaText += "a;}";
982
983     final Project project = lambdaExpression.getProject();
984     final PsiCodeBlock codeBlock = JavaPsiFacade.getElementFactory(project).createCodeBlockFromText(newLambdaText, lambdaExpression);
985     PsiStatement statement = codeBlock.getStatements()[0];
986     if (statement instanceof PsiReturnStatement) {
987       PsiExpression value = ((PsiReturnStatement)statement).getReturnValue();
988       LOG.assertTrue(value != null);
989       value.replace(body);
990     }
991     else if (statement instanceof PsiExpressionStatement){
992       ((PsiExpressionStatement)statement).getExpression().replace(body);
993     }
994     PsiElement arrow = PsiTreeUtil.skipWhitespacesBackward(body);
995     if (arrow != null && arrow.getNextSibling() != body) {
996       lambdaExpression.deleteChildRange(arrow.getNextSibling(), body.getPrevSibling());
997     }
998     return (PsiCodeBlock)CodeStyleManager.getInstance(project).reformat(body.replace(codeBlock));
999   }
1000
1001   @NlsContexts.DialogMessage
1002   public static String checkEnumConstantInSwitchLabel(PsiExpression expr) {
1003     if (PsiImplUtil.getSwitchLabel(expr) != null) {
1004       PsiReferenceExpression ref = ObjectUtils.tryCast(PsiUtil.skipParenthesizedExprDown(expr), PsiReferenceExpression.class);
1005       if (ref != null && ref.resolve() instanceof PsiEnumConstant) {
1006         return RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("refactoring.introduce.variable.enum.in.label.message"));
1007       }
1008     }
1009     return null;
1010   }
1011
1012   public interface ImplicitConstructorUsageVisitor {
1013     void visitConstructor(PsiMethod constructor, PsiMethod baseConstructor);
1014
1015     void visitClassWithoutConstructors(PsiClass aClass);
1016   }
1017
1018   public interface Graph<T> {
1019     Set<T> getVertices();
1020
1021     Set<T> getTargets(T source);
1022   }
1023
1024   /**
1025    * Returns subset of {@code graph.getVertices()} that is a transitive closure (by <code>graph.getTargets()<code>)
1026    * of the following property: initialRelation.value() of vertex or {@code graph.getTargets(vertex)} is true.
1027    * <p/>
1028    * Note that {@code graph.getTargets()} is not necessarily a subset of {@code graph.getVertex()}
1029    *
1030    * @param graph
1031    * @param initialRelation
1032    * @return subset of graph.getVertices()
1033    */
1034   public static <T> Set<T> transitiveClosure(Graph<T> graph, Condition<? super T> initialRelation) {
1035     Set<T> result = new HashSet<>();
1036
1037     final Set<T> vertices = graph.getVertices();
1038     boolean anyChanged;
1039     do {
1040       anyChanged = false;
1041       for (T currentVertex : vertices) {
1042         if (!result.contains(currentVertex)) {
1043           if (!initialRelation.value(currentVertex)) {
1044             Set<T> targets = graph.getTargets(currentVertex);
1045             for (T currentTarget : targets) {
1046               if (result.contains(currentTarget) || initialRelation.value(currentTarget)) {
1047                 result.add(currentVertex);
1048                 anyChanged = true;
1049                 break;
1050               }
1051             }
1052           }
1053           else {
1054             result.add(currentVertex);
1055           }
1056         }
1057       }
1058     }
1059     while (anyChanged);
1060     return result;
1061   }
1062
1063   public static boolean equivalentTypes(PsiType t1, PsiType t2, PsiManager manager) {
1064     while (t1 instanceof PsiArrayType) {
1065       if (!(t2 instanceof PsiArrayType)) return false;
1066       t1 = ((PsiArrayType)t1).getComponentType();
1067       t2 = ((PsiArrayType)t2).getComponentType();
1068     }
1069
1070     if (t1 instanceof PsiPrimitiveType) {
1071       return t2 instanceof PsiPrimitiveType && t1.equals(t2);
1072     }
1073
1074     return manager.areElementsEquivalent(PsiUtil.resolveClassInType(t1), PsiUtil.resolveClassInType(t2));
1075   }
1076
1077   public static List<PsiVariable> collectReferencedVariables(PsiElement scope) {
1078     final List<PsiVariable> result = new ArrayList<>();
1079     scope.accept(new JavaRecursiveElementWalkingVisitor() {
1080       @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
1081         final PsiElement element = expression.resolve();
1082         if (element instanceof PsiVariable) {
1083           result.add((PsiVariable)element);
1084         }
1085         final PsiExpression qualifier = expression.getQualifierExpression();
1086         if (qualifier != null) {
1087           qualifier.accept(this);
1088         }
1089       }
1090     });
1091     return result;
1092   }
1093
1094   public static boolean isModifiedInScope(PsiVariable variable, PsiElement scope) {
1095     for (PsiReference reference : ReferencesSearch.search(variable, new LocalSearchScope(scope), false)) {
1096       if (isAssignmentLHS(reference.getElement())) return true;
1097     }
1098     return false;
1099   }
1100
1101   private static String getNameOfReferencedParameter(PsiDocTag tag) {
1102     LOG.assertTrue("param".equals(tag.getName()));
1103     final PsiElement[] dataElements = tag.getDataElements();
1104     if (dataElements.length < 1) return null;
1105     return dataElements[0].getText();
1106   }
1107
1108   public static void fixJavadocsForParams(PsiMethod method, Set<? extends PsiParameter> newParameters) throws IncorrectOperationException {
1109     fixJavadocsForParams(method, newParameters, Conditions.alwaysFalse());
1110   }
1111
1112   public static void fixJavadocsForParams(PsiMethod method,
1113                                           Set<? extends PsiParameter> newParameters,
1114                                           Condition<? super Pair<PsiParameter, String>> eqCondition) throws IncorrectOperationException {
1115     fixJavadocsForParams(method, newParameters, eqCondition, Conditions.alwaysTrue());
1116   }
1117
1118   public static void fixJavadocsForParams(@NotNull PsiMethod method,
1119                                           @NotNull Set<? extends PsiParameter> newParameters,
1120                                           @NotNull Condition<? super Pair<PsiParameter, String>> eqCondition,
1121                                           @NotNull Condition<? super String> matchedToOldParam) throws IncorrectOperationException {
1122     fixJavadocsForParams(method, method.getDocComment(), newParameters, eqCondition, matchedToOldParam);
1123   }
1124
1125   public static void fixJavadocsForParams(@NotNull PsiMethod method,
1126                                           @Nullable PsiDocComment docComment,
1127                                           @NotNull Set<? extends PsiParameter> newParameters,
1128                                           @NotNull Condition<? super Pair<PsiParameter, String>> eqCondition,
1129                                           @NotNull Condition<? super String> matchedToOldParam) throws IncorrectOperationException {
1130     if (docComment == null) return;
1131     final PsiParameter[] parameters = method.getParameterList().getParameters();
1132     final PsiDocTag[] paramTags = docComment.findTagsByName("param");
1133     if (parameters.length > 0 && newParameters.size() < parameters.length && paramTags.length == 0) return;
1134     Map<PsiParameter, PsiDocTag> tagForParam = new HashMap<>();
1135     for (PsiParameter parameter : parameters) {
1136       boolean found = false;
1137       for (PsiDocTag paramTag : paramTags) {
1138         if (parameter.getName().equals(getNameOfReferencedParameter(paramTag))) {
1139           tagForParam.put(parameter, paramTag);
1140           found = true;
1141           break;
1142         }
1143       }
1144       if (!found) {
1145         for (PsiDocTag paramTag : paramTags) {
1146           final String paramName = getNameOfReferencedParameter(paramTag);
1147           if (eqCondition.value(Pair.create(parameter, paramName))) {
1148             tagForParam.put(parameter, paramTag);
1149             found = true;
1150             break;
1151           }
1152         }
1153       }
1154       if (!found && !newParameters.contains(parameter)) {
1155         tagForParam.put(parameter, null);
1156       }
1157     }
1158
1159     List<PsiDocTag> newTags = new ArrayList<>();
1160
1161     for (PsiDocTag paramTag : paramTags) {
1162       final String paramName = getNameOfReferencedParameter(paramTag);
1163       if (!tagForParam.containsValue(paramTag) && !matchedToOldParam.value(paramName)) {
1164         newTags.add((PsiDocTag)paramTag.copy());
1165       }
1166     }
1167
1168     for (PsiParameter parameter : parameters) {
1169       if (tagForParam.containsKey(parameter)) {
1170         final PsiDocTag psiDocTag = tagForParam.get(parameter);
1171         if (psiDocTag != null) {
1172           final PsiDocTag copy = (PsiDocTag)psiDocTag.copy();
1173           final PsiDocTagValue valueElement = copy.getValueElement();
1174           if (valueElement != null) {
1175             valueElement.replace(createParamTag(parameter).getValueElement());
1176           }
1177           newTags.add(copy);
1178         }
1179       }
1180       else {
1181         newTags.add(createParamTag(parameter));
1182       }
1183     }
1184     PsiElement anchor = paramTags.length > 0 ? paramTags[0].getPrevSibling() : null;
1185     for (PsiDocTag paramTag : paramTags) {
1186       paramTag.delete();
1187     }
1188     for (PsiDocTag psiDocTag : newTags) {
1189       anchor = anchor != null && anchor.isValid() ? docComment.addAfter(psiDocTag, anchor) : docComment.add(psiDocTag);
1190     }
1191   }
1192
1193   @NotNull
1194   private static PsiDocTag createParamTag(@NotNull PsiParameter parameter) {
1195     return JavaPsiFacade.getElementFactory(parameter.getProject()).createParamTag(parameter.getName(), "");
1196   }
1197
1198   @NotNull
1199   public static PsiDirectory createPackageDirectoryInSourceRoot(@NotNull PackageWrapper aPackage, @NotNull final VirtualFile sourceRoot)
1200     throws IncorrectOperationException {
1201     PsiDirectory[] existing = aPackage.getDirectories(
1202         GlobalSearchScopes.directoryScope(aPackage.getManager().getProject(), sourceRoot, true));
1203     if (existing.length > 0) {
1204       return existing[0];
1205     }
1206     String qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
1207     PsiDirectory current = aPackage.getManager().findDirectory(sourceRoot);
1208     LOG.assertTrue(current != null);
1209     if (qNameToCreate.isEmpty()) {
1210       return current;
1211     }
1212     final String[] shortNames = qNameToCreate.split("\\.");
1213     for (String shortName : shortNames) {
1214       PsiDirectory subdirectory = current.findSubdirectory(shortName);
1215       if (subdirectory == null) {
1216         subdirectory = current.createSubdirectory(shortName);
1217       }
1218       current = subdirectory;
1219     }
1220     return current;
1221   }
1222
1223   public static String qNameToCreateInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot) throws IncorrectOperationException {
1224     String targetQName = aPackage.getQualifiedName();
1225     String sourceRootPackage =
1226       ProjectRootManager.getInstance(aPackage.getManager().getProject()).getFileIndex().getPackageNameByDirectory(sourceRoot);
1227     if (!canCreateInSourceRoot(sourceRootPackage, targetQName)) {
1228       throw new IncorrectOperationException(
1229         "Cannot create package '" + targetQName + "' in source folder " + sourceRoot.getPresentableUrl());
1230     }
1231     String result = targetQName.substring(sourceRootPackage.length());
1232     if (StringUtil.startsWithChar(result, '.')) result = result.substring(1);  // remove initial '.'
1233     return result;
1234   }
1235
1236   public static boolean canCreateInSourceRoot(final String sourceRootPackage, final String targetQName) {
1237     if (sourceRootPackage == null || !targetQName.startsWith(sourceRootPackage)) return false;
1238     if (sourceRootPackage.isEmpty() || targetQName.length() == sourceRootPackage.length()) return true;
1239     return targetQName.charAt(sourceRootPackage.length()) == '.';
1240   }
1241
1242
1243   @Nullable
1244   public static PsiDirectory findPackageDirectoryInSourceRoot(PackageWrapper aPackage, final VirtualFile sourceRoot) {
1245     final PsiDirectory[] directories = aPackage.getDirectories();
1246     for (PsiDirectory directory : directories) {
1247       if (VfsUtilCore.isAncestor(sourceRoot, directory.getVirtualFile(), false)) {
1248         return directory;
1249       }
1250     }
1251     String qNameToCreate;
1252     try {
1253       qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
1254     }
1255     catch (IncorrectOperationException e) {
1256       return null;
1257     }
1258     final String[] shortNames = qNameToCreate.split("\\.");
1259     PsiDirectory current = aPackage.getManager().findDirectory(sourceRoot);
1260     LOG.assertTrue(current != null);
1261     for (String shortName : shortNames) {
1262       PsiDirectory subdirectory = current.findSubdirectory(shortName);
1263       if (subdirectory == null) {
1264         return null;
1265       }
1266       current = subdirectory;
1267     }
1268     return current;
1269   }
1270
1271   public static class ConditionCache<T> implements Condition<T> {
1272     private final Condition<? super T> myCondition;
1273     private final HashSet<T> myProcessedSet = new HashSet<>();
1274     private final HashSet<T> myTrueSet = new HashSet<>();
1275
1276     public ConditionCache(Condition<? super T> condition) {
1277       myCondition = condition;
1278     }
1279
1280     @Override
1281     public boolean value(T object) {
1282       if (!myProcessedSet.contains(object)) {
1283         myProcessedSet.add(object);
1284         final boolean value = myCondition.value(object);
1285         if (value) {
1286           myTrueSet.add(object);
1287           return true;
1288         }
1289         return false;
1290       }
1291       return myTrueSet.contains(object);
1292     }
1293   }
1294
1295   public static class IsDescendantOf implements Condition<PsiClass> {
1296     private final PsiClass myClass;
1297     private final ConditionCache<PsiClass> myConditionCache;
1298
1299     public IsDescendantOf(PsiClass aClass) {
1300       myClass = aClass;
1301       myConditionCache = new ConditionCache<>(aClass1 -> InheritanceUtil.isInheritorOrSelf(aClass1, myClass, true));
1302     }
1303
1304     @Override
1305     public boolean value(PsiClass aClass) {
1306       return myConditionCache.value(aClass);
1307     }
1308   }
1309
1310   @Nullable
1311   public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(final PsiElement @NotNull ... elements) {
1312     return createTypeParameterListWithUsedTypeParameters(null, elements);
1313   }
1314
1315   @Nullable
1316   public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@Nullable final PsiTypeParameterList fromList,
1317                                                                                    final PsiElement @NotNull ... elements) {
1318     return createTypeParameterListWithUsedTypeParameters(fromList, Conditions.alwaysTrue(), elements);
1319   }
1320
1321   @Nullable
1322   public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@Nullable final PsiTypeParameterList fromList,
1323                                                                                    Condition<? super PsiTypeParameter> filter,
1324                                                                                    final PsiElement @NotNull ... elements) {
1325     if (elements.length == 0) return null;
1326     final Set<PsiTypeParameter> used = new HashSet<>();
1327     for (final PsiElement element : elements) {
1328       if (element == null) continue;
1329       collectTypeParameters(used, element, filter);  //pull up extends cls class with type params
1330
1331     }
1332
1333     collectTypeParametersInDependencies(filter, used);
1334
1335     if (fromList != null) {
1336       used.retainAll(Arrays.asList(fromList.getTypeParameters()));
1337     }
1338
1339     PsiTypeParameter[] typeParameters = used.toArray(PsiTypeParameter.EMPTY_ARRAY);
1340
1341     Arrays.sort(typeParameters, Comparator.comparingInt(tp -> tp.getTextRange().getStartOffset()));
1342
1343     final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(elements[0].getProject());
1344     try {
1345       final PsiClass aClass = elementFactory.createClassFromText("class A {}", null);
1346       PsiTypeParameterList list = aClass.getTypeParameterList();
1347       assert list != null;
1348       for (final PsiTypeParameter typeParameter : typeParameters) {
1349         list.add(typeParameter);
1350       }
1351       return list;
1352     }
1353     catch (IncorrectOperationException e) {
1354       LOG.error(e);
1355       assert false;
1356       return null;
1357     }
1358   }
1359
1360   private static void collectTypeParametersInDependencies(Condition<? super PsiTypeParameter> filter, Set<PsiTypeParameter> used) {
1361     Stack<PsiTypeParameter> toProcess = new Stack<>();
1362     toProcess.addAll(used);
1363     while (!toProcess.isEmpty()) {
1364       PsiTypeParameter parameter = toProcess.pop();
1365       HashSet<PsiTypeParameter> dependencies = new HashSet<>();
1366       collectTypeParameters(dependencies, parameter, param -> filter.value(param) && !used.contains(param));
1367       used.addAll(dependencies);
1368       toProcess.addAll(dependencies);
1369     }
1370   }
1371
1372   public static void collectTypeParameters(final Set<? super PsiTypeParameter> used, final PsiElement element) {
1373     collectTypeParameters(used, element, Conditions.alwaysTrue());
1374   }
1375   public static void collectTypeParameters(final Set<? super PsiTypeParameter> used, final PsiElement element,
1376                                            final Condition<? super PsiTypeParameter> filter) {
1377     element.accept(new JavaRecursiveElementVisitor() {
1378       @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
1379         super.visitReferenceElement(reference);
1380         if (!reference.isQualified()) {
1381           final PsiElement resolved = reference.resolve();
1382           if (resolved instanceof PsiTypeParameter) {
1383             final PsiTypeParameter typeParameter = (PsiTypeParameter)resolved;
1384             if (PsiTreeUtil.isAncestor(typeParameter.getOwner(), element, false) && filter.value(typeParameter)) {
1385               used.add(typeParameter);
1386             }
1387           }
1388         }
1389       }
1390
1391       @Override
1392       public void visitExpression(final PsiExpression expression) {
1393         super.visitExpression(expression);
1394         final PsiType type = expression.getType();
1395         if (type != null) {
1396           final PsiTypesUtil.TypeParameterSearcher searcher = new PsiTypesUtil.TypeParameterSearcher();
1397           type.accept(searcher);
1398           for (PsiTypeParameter typeParam : searcher.getTypeParameters()) {
1399             if (PsiTreeUtil.isAncestor(typeParam.getOwner(), element, false) && filter.value(typeParam)){
1400               used.add(typeParam);
1401             }
1402           }
1403         }
1404       }
1405     });
1406   }
1407 }