update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / inline / InlineLocalHandler.java
blob7ad18b79a276eeb7c149493636b13470af0da391
2 /*
3 * Copyright 2000-2009 JetBrains s.r.o.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package com.intellij.refactoring.inline;
19 import com.intellij.codeInsight.highlighting.HighlightManager;
20 import com.intellij.codeInsight.intention.QuickFixFactory;
21 import com.intellij.codeInsight.TargetElementUtilBase;
22 import com.intellij.lang.refactoring.InlineActionHandler;
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.command.CommandProcessor;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.editor.colors.EditorColors;
28 import com.intellij.openapi.editor.colors.EditorColorsManager;
29 import com.intellij.openapi.editor.markup.TextAttributes;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.wm.WindowManager;
32 import com.intellij.psi.*;
33 import com.intellij.psi.controlFlow.DefUseUtil;
34 import com.intellij.psi.search.GlobalSearchScope;
35 import com.intellij.psi.search.searches.ReferencesSearch;
36 import com.intellij.psi.util.PsiTreeUtil;
37 import com.intellij.psi.util.PsiUtil;
38 import com.intellij.refactoring.HelpID;
39 import com.intellij.refactoring.RefactoringBundle;
40 import com.intellij.refactoring.util.CommonRefactoringUtil;
41 import com.intellij.refactoring.util.InlineUtil;
42 import com.intellij.refactoring.util.RefactoringMessageDialog;
43 import com.intellij.util.ArrayUtil;
44 import com.intellij.util.IncorrectOperationException;
45 import com.intellij.util.Processor;
46 import com.intellij.util.Query;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
50 import java.util.ArrayList;
51 import java.util.Collections;
52 import java.util.List;
54 public class InlineLocalHandler extends JavaInlineActionHandler {
55 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inline.InlineLocalHandler");
57 private static final String REFACTORING_NAME = RefactoringBundle.message("inline.variable.title");
59 public boolean canInlineElement(PsiElement element) {
60 return element instanceof PsiLocalVariable;
63 public void inlineElement(Project project, Editor editor, PsiElement element) {
64 final PsiReference psiReference = TargetElementUtilBase.findReference(editor);
65 final PsiReferenceExpression refExpr = psiReference instanceof PsiReferenceExpression ? ((PsiReferenceExpression)psiReference) : null;
66 invoke(project, editor, (PsiLocalVariable) element, refExpr);
69 /**
70 * should be called in AtomicAction
72 public static void invoke(@NotNull final Project project, final Editor editor, final PsiLocalVariable local, PsiReferenceExpression refExpr) {
73 if (!CommonRefactoringUtil.checkReadOnlyStatus(project, local)) return;
75 final HighlightManager highlightManager = HighlightManager.getInstance(project);
77 final String localName = local.getName();
79 final Query<PsiReference> query = ReferencesSearch.search(local, GlobalSearchScope.allScope(project), false);
80 if (query.findFirst() == null){
81 LOG.assertTrue(refExpr == null);
82 String message = RefactoringBundle.message("variable.is.never.used", localName);
83 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
84 return;
87 final PsiClass containingClass = PsiTreeUtil.getParentOfType(local, PsiClass.class);
88 final List<PsiClass> innerClassesWithUsages = new ArrayList<PsiClass>();
89 final List<PsiElement> innerClassUsages = new ArrayList<PsiElement>();
90 query.forEach(new Processor<PsiReference>() {
91 public boolean process(final PsiReference psiReference) {
92 final PsiElement element = psiReference.getElement();
93 final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
94 if (psiClass != containingClass) {
95 innerClassesWithUsages.add(psiClass);
96 innerClassUsages.add(element);
98 return true;
102 final PsiCodeBlock containerBlock = PsiTreeUtil.getParentOfType(local, PsiCodeBlock.class);
103 LOG.assertTrue(containerBlock != null);
105 final PsiExpression defToInline = innerClassesWithUsages.isEmpty()
106 ? getDefToInline(local, refExpr, containerBlock)
107 : getDefToInline(local, innerClassesWithUsages.get(0), containerBlock);
108 if (defToInline == null){
109 final String key = refExpr == null ? "variable.has.no.initializer" : "variable.has.no.dominating.definition";
110 String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message(key, localName));
111 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
112 return;
115 final List<PsiElement> refsToInlineList = new ArrayList<PsiElement>();
116 Collections.addAll(refsToInlineList, DefUseUtil.getRefs(containerBlock, local, defToInline));
117 for (PsiElement innerClassUsage : innerClassUsages) {
118 if (!refsToInlineList.contains(innerClassUsage)) {
119 refsToInlineList.add(innerClassUsage);
122 if (refsToInlineList.size() == 0) {
123 String message = RefactoringBundle.message("variable.is.never.used.before.modification", localName);
124 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
125 return;
127 final PsiElement[] refsToInline = refsToInlineList.toArray(new PsiElement[refsToInlineList.size()]);
129 EditorColorsManager manager = EditorColorsManager.getInstance();
130 final TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
131 final TextAttributes writeAttributes = manager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
132 if (refExpr != null && PsiUtil.isAccessedForReading(refExpr) && ArrayUtil.find(refsToInline, refExpr) < 0) {
133 final PsiElement[] defs = DefUseUtil.getDefs(containerBlock, local, refExpr);
134 LOG.assertTrue(defs.length > 0);
135 highlightManager.addOccurrenceHighlights(editor, defs, attributes, true, null);
136 String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("variable.is.accessed.for.writing", localName));
137 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
138 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
139 return;
142 PsiFile workingFile = local.getContainingFile();
143 for (PsiElement ref : refsToInline) {
144 final PsiFile otherFile = ref.getContainingFile();
145 if (!otherFile.equals(workingFile)) {
146 String message = RefactoringBundle.message("variable.is.referenced.in.multiple.files", localName);
147 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
148 return;
152 for (final PsiElement ref : refsToInline) {
153 final PsiElement[] defs = DefUseUtil.getDefs(containerBlock, local, ref);
154 boolean isSameDefinition = true;
155 for (PsiElement def : defs) {
156 isSameDefinition &= isSameDefinition(def, defToInline);
158 if (!isSameDefinition) {
159 highlightManager.addOccurrenceHighlights(editor, defs, writeAttributes, true, null);
160 highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{ref}, attributes, true, null);
161 String message =
162 RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("variable.is.accessed.for.writing.and.used.with.inlined", localName));
163 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
164 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
165 return;
169 if (checkRefsInAugmentedAssignment(refsToInline, project, editor, localName)) {
170 return;
173 if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) {
174 // TODO : check if initializer uses fieldNames that possibly will be hidden by other
175 // locals with the same names after inlining
176 highlightManager.addOccurrenceHighlights(
177 editor,
178 refsToInline,
179 attributes, true, null
181 int occurrencesCount = refsToInline.length;
182 String occurencesString = RefactoringBundle.message("occurences.string", occurrencesCount);
183 final String promptKey = isInliningVariableInitializer(defToInline)
184 ? "inline.local.variable.prompt" : "inline.local.variable.definition.prompt";
185 final String question = RefactoringBundle.message(promptKey, localName) + " " + occurencesString;
186 RefactoringMessageDialog dialog = new RefactoringMessageDialog(
187 REFACTORING_NAME,
188 question,
189 HelpID.INLINE_VARIABLE,
190 "OptionPane.questionIcon",
191 true,
192 project);
193 dialog.show();
194 if (!dialog.isOK()){
195 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
196 return;
200 final Runnable runnable = new Runnable() {
201 public void run() {
202 try{
203 PsiExpression[] exprs = new PsiExpression[refsToInline.length];
204 for(int idx = 0; idx < refsToInline.length; idx++){
205 PsiJavaCodeReferenceElement refElement = (PsiJavaCodeReferenceElement)refsToInline[idx];
206 exprs[idx] = InlineUtil.inlineVariable(local, defToInline, refElement);
209 if (!isInliningVariableInitializer(defToInline)) {
210 defToInline.getParent().delete();
211 } else {
212 defToInline.delete();
215 if (ReferencesSearch.search(local).findFirst() == null) {
216 QuickFixFactory.getInstance().createRemoveUnusedVariableFix(local).invoke(project, editor, local.getContainingFile());
219 if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) {
220 highlightManager.addOccurrenceHighlights(editor, exprs, attributes, true, null);
221 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
224 for (final PsiExpression expr : exprs) {
225 InlineUtil.tryToInlineArrayCreationForVarargs(expr);
228 catch (IncorrectOperationException e){
229 LOG.error(e);
234 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
235 public void run() {
236 ApplicationManager.getApplication().runWriteAction(runnable);
238 }, RefactoringBundle.message("inline.command", localName), null);
241 private static boolean checkRefsInAugmentedAssignment(final PsiElement[] refsToInline, final Project project, final Editor editor,
242 final String localName) {
243 for(PsiElement element: refsToInline) {
244 if (element.getParent() instanceof PsiAssignmentExpression) {
245 PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression) element.getParent();
246 if (element == assignmentExpression.getLExpression()) {
247 EditorColorsManager manager = EditorColorsManager.getInstance();
248 final TextAttributes writeAttributes = manager.getGlobalScheme().getAttributes(EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES);
249 HighlightManager.getInstance(project).addOccurrenceHighlights(editor, new PsiElement[]{element}, writeAttributes, true, null);
251 String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("variable.is.accessed.for.writing", localName));
252 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INLINE_VARIABLE);
253 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
254 return true;
258 return false;
261 private static boolean isSameDefinition(final PsiElement def, final PsiExpression defToInline) {
262 if (def instanceof PsiLocalVariable) return defToInline.equals(((PsiLocalVariable)def).getInitializer());
263 final PsiElement parent = def.getParent();
264 return parent instanceof PsiAssignmentExpression && defToInline.equals(((PsiAssignmentExpression)parent).getRExpression());
267 private static boolean isInliningVariableInitializer(final PsiExpression defToInline) {
268 return defToInline.getParent() instanceof PsiVariable;
271 @Nullable
272 private static PsiExpression getDefToInline(final PsiLocalVariable local,
273 final PsiElement refExpr,
274 final PsiCodeBlock block) {
275 if (refExpr != null) {
276 PsiElement def;
277 if (refExpr instanceof PsiReferenceExpression && PsiUtil.isAccessedForWriting((PsiExpression) refExpr)) {
278 def = refExpr;
280 else {
281 final PsiElement[] defs = DefUseUtil.getDefs(block, local, refExpr);
282 if (defs.length == 1) {
283 def = defs[0];
285 else {
286 return null;
290 if (def instanceof PsiReferenceExpression && def.getParent() instanceof PsiAssignmentExpression) {
291 final PsiExpression rExpr = ((PsiAssignmentExpression)def.getParent()).getRExpression();
292 if (rExpr != null) return rExpr;
295 return local.getInitializer();