update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / util / RefactoringUtil.java
blobbf5f069f3bbd908d39000e555c870b261ed2df06
1 /*
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;
62 import java.util.*;
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) {
88 return Boolean.TRUE;
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;
102 return Boolean.TRUE;
105 public Boolean visitWildcardType(PsiWildcardType wildcardType) {
106 if (wildcardType.getBound() != null) return wildcardType.getBound().accept(this);
107 return Boolean.TRUE;
109 }).booleanValue();
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());
123 else {
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) {
138 int index = 0;
139 while (true) {
140 final String name = index > 0 ? baseName + index : baseName;
141 index++;
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 {
149 try {
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) {
163 continue;
166 return name;
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();
182 @Nullable
183 public static String suggestNewOverriderName(String oldOverriderName, String oldBaseName, String newBaseName) {
184 if (oldOverriderName.equals(oldBaseName)) {
185 return newBaseName;
187 int i;
188 if (oldOverriderName.startsWith(oldBaseName)) {
189 i = 0;
191 else {
192 i = StringUtil.indexOfIgnoreCase(oldOverriderName, oldBaseName, 0);
194 if (i >= 0) {
195 String newOverriderName = oldOverriderName.substring(0, i);
196 if (Character.isUpperCase(oldOverriderName.charAt(i))) {
197 newOverriderName += StringUtil.capitalize(newBaseName);
199 else {
200 newOverriderName += newBaseName;
202 final int j = i + oldBaseName.length();
203 if (j < oldOverriderName.length()) {
204 newOverriderName += oldOverriderName.substring(j);
207 return newOverriderName;
209 return null;
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) {
219 return true;
224 return false;
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());
234 return replaceIn;
237 public static PsiElement getVariableScope(PsiLocalVariable localVar) {
238 if (!(localVar instanceof ImplicitVariable)) {
239 return localVar.getParent().getParent();
241 else {
242 return ((ImplicitVariable)localVar).getDeclarationScope();
246 public static PsiReturnStatement[] findReturnStatements(PsiMethod method) {
247 ArrayList<PsiReturnStatement> vector = new ArrayList<PsiReturnStatement>();
248 PsiCodeBlock body = method.getBody();
249 if (body != null) {
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;
270 while (true) {
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;
295 while (true) {
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;
309 * @param expression
310 * @return loop body if expression is part of some loop's condition or for loop's increment part
311 * null otherwise
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) {
321 return forStatement;
323 else {
324 return null;
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()) {
330 return forStatement;
332 else {
333 return null;
336 if (outermost.getParent() instanceof PsiWhileStatement) {
337 return outermost.getParent();
339 if (outermost.getParent() instanceof PsiDoWhileStatement) {
340 return outermost.getParent();
342 return null;
345 public static PsiClass getThisClass(PsiElement place) {
346 PsiElement parent = place.getContext();
347 if (parent == null) return null;
348 PsiElement prev = null;
349 while (true) {
350 if (parent instanceof PsiClass) {
351 if (!(parent instanceof PsiAnonymousClass && ((PsiAnonymousClass)parent).getArgumentList() == prev)) {
352 return (PsiClass)parent;
355 prev = 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;
367 return null;
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;
409 else {
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();
425 ref.delete();
426 return refCopy;
429 return null;
432 public static PsiJavaCodeReferenceElement findReferenceToClass(PsiReferenceList refList, PsiClass aClass) {
433 PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
434 for (PsiJavaCodeReferenceElement ref : refs) {
435 if (ref.isReferenceTo(aClass)) {
436 return ref;
439 return null;
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;
450 return null;
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();
460 if (type == null) {
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();
469 return null;
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();
480 else {
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;
504 else {
505 return false;
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)) {
525 continue;
527 PsiElement anchor1 = getParentExpressionAnchorElement(occurrence);
528 if (anchor1 == null) return null;
530 if (anchor == null) {
531 anchor = anchor1;
533 else {
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;
540 else {
541 if (commonParent instanceof PsiStatement) {
542 anchor = commonParent;
544 else {
545 PsiElement parent = firstAnchor;
546 while (!parent.getParent().equals(commonParent)) {
547 parent = parent.getParent();
549 final PsiElement newAnchor = getParentExpressionAnchorElement(parent);
550 if (newAnchor != null) {
551 anchor = newAnchor;
553 else {
554 anchor = parent;
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;
573 break;
577 if (isInDifferentCases) {
578 anchor = switchStatement;
583 return anchor;
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) {
591 return true;
593 else if (parent instanceof PsiAnonymousClass) {
594 return element.equals(((PsiAnonymousClass)parent).getBaseClassReference());
596 return false;
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);
609 return null;
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();
624 else {
625 LOG.assertTrue(false);
626 return null;
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) {
642 highlightManager
643 .addRangeHighlight(editor, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(), attributes, true, highlighters);
645 else {
646 final TextRange textRange = occurrence.getTextRange();
647 highlightManager.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, highlighters);
651 return 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();
668 if (expr1 != null) {
669 expr = expr1;
672 PsiDeclarationStatement decl = factory.createVariableDeclarationStatement(id, expr.getType(), expr);
673 if (declareFinal) {
674 PsiUtil.setModifierProperty(((PsiLocalVariable)decl.getDeclaredElements()[0]), PsiModifier.FINAL, true);
676 anchorStatement.getParent().addBefore(decl, anchorStatement);
678 return id;
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);
716 return result;
719 public static PsiExpression convertInitializerToNormalExpression(PsiExpression expression, PsiType forcedReturnType)
720 throws IncorrectOperationException {
721 if (expression instanceof PsiArrayInitializerExpression) {
722 return createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)expression, forcedReturnType);
724 return expression;
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;
735 else {
736 initializerType = getTypeByExpression(initializer);
739 if (initializerType == null) {
740 return initializer;
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);
750 return result;
753 public static void abstractizeMethod(PsiClass targetClass, PsiMethod method) throws IncorrectOperationException {
754 PsiCodeBlock body = method.getBody();
755 if (body != null) {
756 body.delete();
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;
775 return false;
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;
784 return expression;
787 public static PsiExpression outermostParenthesizedExpression(PsiExpression expression) {
788 while (expression.getParent() instanceof PsiParenthesizedExpression) {
789 expression = (PsiParenthesizedExpression)expression.getParent();
791 return expression;
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();
806 if (testForSuper) {
807 if ("super".equals(methodExpression.getText())) return true;
809 if (testForThis) {
810 if ("this".equals(methodExpression.getText())) return true;
813 return false;
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);
829 else {
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;
840 return null;
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.
858 * <p/>
859 * Note that <code>graph.getTargets()</code> is not neccesrily a subset of <code>graph.getVertex()</code>
861 * @param graph
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();
869 boolean anyChanged;
870 do {
871 anyChanged = false;
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);
879 anyChanged = true;
880 break;
884 else {
885 result.add(currentVertex);
890 while (anyChanged);
891 return result;
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);
922 return result;
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;
929 return false;
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);
951 found = true;
952 break;
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());
968 else {
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) {
977 paramTag.delete();
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)) {
986 return directory;
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;
1000 return current;
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 '.'
1013 return result;
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()) == '.';
1023 @Nullable
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)) {
1028 return directory;
1031 String qNameToCreate;
1032 try {
1033 qNameToCreate = qNameToCreateInSourceRoot(aPackage, sourceRoot);
1035 catch (IncorrectOperationException e) {
1036 return null;
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) {
1044 return null;
1046 current = subdirectory;
1048 return current;
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);
1064 if (value) {
1065 myTrueSet.add(object);
1066 return true;
1068 return false;
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) {
1079 myClass = 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);
1092 @Nullable
1093 public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@NotNull final PsiElement... elements) {
1094 return createTypeParameterListWithUsedTypeParameters(null, elements);
1097 @Nullable
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);
1118 @Override
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();
1143 try {
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);
1150 return list;
1152 catch (IncorrectOperationException e) {
1153 LOG.error(e);
1154 assert false;
1155 return null;