update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / MethodReturnBooleanFix.java
blobaf27c62c16335e567d08eea89f019cfe2fab868d
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.codeInsight.daemon.impl.quickfix;
18 import com.intellij.codeInsight.CodeInsightUtilBase;
19 import com.intellij.codeInsight.daemon.QuickFixBundle;
20 import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
21 import com.intellij.codeInsight.intention.IntentionAction;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.command.undo.UndoUtil;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.editor.Editor;
26 import com.intellij.openapi.editor.ScrollType;
27 import com.intellij.openapi.fileEditor.FileEditorManager;
28 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.ui.Messages;
31 import com.intellij.openapi.util.Comparing;
32 import com.intellij.openapi.util.TextRange;
33 import com.intellij.psi.*;
34 import com.intellij.psi.controlFlow.AnalysisCanceledException;
35 import com.intellij.psi.controlFlow.ControlFlow;
36 import com.intellij.psi.controlFlow.ControlFlowUtil;
37 import com.intellij.psi.util.PsiFormatUtil;
38 import com.intellij.psi.util.TypeConversionUtil;
39 import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
40 import com.intellij.refactoring.changeSignature.OverriderUsageInfo;
41 import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
42 import com.intellij.usageView.UsageInfo;
43 import com.intellij.util.IncorrectOperationException;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.Iterator;
50 import java.util.List;
52 public class MethodReturnBooleanFix implements IntentionAction {
53 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.MethodReturnBooleanFix");
55 private final PsiMethod myMethod;
56 private final PsiType myReturnType;
58 public MethodReturnBooleanFix(final PsiMethod method, final PsiType returnType) {
59 myMethod = method;
60 myReturnType = returnType;
63 @NotNull
64 public String getText() {
65 return QuickFixBundle.message("fix.return.type.text",
66 myMethod.getName(),
67 myReturnType.getCanonicalText());
70 @NotNull
71 public String getFamilyName() {
72 return QuickFixBundle.message("fix.return.type.family");
75 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
76 return myMethod != null
77 && myMethod.isValid()
78 && myMethod.getManager().isInProject(myMethod)
79 && myReturnType != null
80 && myReturnType.isValid()
81 && !TypeConversionUtil.isNullType(myReturnType)
82 && myMethod.getReturnType() != null
83 && !Comparing.equal(myReturnType, myMethod.getReturnType());
86 public boolean startInWriteAction() {
87 return true;
90 public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) {
91 if (!CodeInsightUtilBase.prepareFileForWrite(myMethod.getContainingFile())) return;
93 final List<PsiMethod> affectedMethods = changeReturnType(myMethod, myReturnType);
95 PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
96 final SourceMethodSelector returnSelector = new SourceMethodSelector(myMethod);
97 final ReturnStatementAdder adder = new ReturnStatementAdder(factory, myReturnType, returnSelector);
99 for (PsiMethod method : affectedMethods) {
100 adder.addReturnForMethod(file, method);
103 final PsiReturnStatement latestReturn = returnSelector.getReturnStatement();
104 if (latestReturn != null) {
105 selectReturnValueInEditor(latestReturn, getEditorForMethod(project, editor, latestReturn.getContainingFile()));
109 private static class SourceMethodSelector implements GeneratedReturnSelector {
110 private final PsiMethod mySourceMethod;
111 private PsiReturnStatement myReturnStatement;
113 private SourceMethodSelector(final PsiMethod sourceMethod) {
114 mySourceMethod = sourceMethod;
117 public void accept(final PsiReturnStatement statement, final PsiMethod method) {
118 if ((mySourceMethod.equals(method)) && (statement != null)) {
119 myReturnStatement = statement;
123 public PsiReturnStatement getReturnStatement() {
124 return myReturnStatement;
129 * selects which of generated / corrected return statements to be selected in editor after operation
130 * only latest return statements inside methods are passed
132 private interface GeneratedReturnSelector {
133 void accept(final PsiReturnStatement statement, final PsiMethod method);
136 // to clearly separate data
137 private static class ReturnStatementAdder {
138 private final PsiElementFactory factory;
139 private final PsiType myTargetType;
140 private final GeneratedReturnSelector mySelector;
142 private ReturnStatementAdder(@NotNull final PsiElementFactory factory, @NotNull final PsiType targetType,
143 @NotNull final GeneratedReturnSelector selector) {
144 this.factory = factory;
145 myTargetType = targetType;
146 mySelector = selector;
149 public void addReturnForMethod(final PsiFile file, final PsiMethod method) {
150 final PsiModifierList modifiers = method.getModifierList();
151 if ((modifiers.hasModifierProperty(PsiModifier.ABSTRACT)) || (method.getBody() == null)) {
152 return;
155 try {
156 final ConvertReturnStatementsVisitor visitor = new ConvertReturnStatementsVisitor(factory, method, myTargetType);
158 final ControlFlow controlFlow = HighlightControlFlowUtil.getControlFlowNoConstantEvaluate(method.getBody());
159 if (ControlFlowUtil.checkReturns(controlFlow, visitor)) {
160 // extra return statement not needed
161 // get latest modified return statement and select...
162 mySelector.accept(visitor.getLatestReturn(), method);
163 return;
166 mySelector.accept(visitor.createReturnInLastStatement(), method);
168 catch (AnalysisCanceledException e) {
169 LOG.error(e);
170 return;
172 catch (IncorrectOperationException e) {
173 LOG.error(e);
176 if (method.getContainingFile() != file) {
177 UndoUtil.markPsiFileForUndo(file);
182 private Editor getEditorForMethod(@NotNull final Project project, final Editor editor, final PsiFile file) {
183 if (myMethod.getContainingFile() != file) {
184 OpenFileDescriptor descriptor = new OpenFileDescriptor(project, myMethod.getContainingFile().getVirtualFile());
185 return FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
187 return editor;
190 @Nullable
191 private static PsiMethod[] getChangeRoots(final PsiMethod method) {
192 final PsiMethod[] methods = method.findDeepestSuperMethods();
194 if (methods.length > 0) {
195 if (ApplicationManager.getApplication().isUnitTestMode()) {
196 return methods;
198 final String methodName = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE |
199 PsiFormatUtil.SHOW_PARAMETERS | PsiFormatUtil.TYPE_AFTER, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.TYPE_AFTER);
200 final int result = Messages.showYesNoCancelDialog(QuickFixBundle.message("quickfix.retun.type.void.to.boolean.inherited.warning.text",
201 method.getContainingClass().getName() + "." + methodName),
202 QuickFixBundle.message("quickfix.retun.type.void.to.boolean.inherited.warning.title"), Messages.getQuestionIcon());
203 if (2 == result) {
204 // cancel
205 return null;
206 } else if (0 == result) {
207 return methods;
210 // no - only base
211 return new PsiMethod[] {method};
214 private static List<PsiMethod> changeReturnType(final PsiMethod method, final PsiType returnType) {
215 final PsiMethod[] methods = getChangeRoots(method);
216 if (methods == null) {
217 // canceled
218 return Collections.emptyList();
221 final MethodSignatureChangeVisitor methodSignatureChangeVisitor = new MethodSignatureChangeVisitor();
222 for (PsiMethod targetMethod : methods) {
223 methodSignatureChangeVisitor.addBase(targetMethod);
224 ChangeSignatureProcessor processor = new UsagesAwareChangeSignatureProcessor(method.getProject(), targetMethod,
225 false, null,
226 method.getName(),
227 returnType,
228 RemoveUnusedParameterFix.getNewParametersInfo(method, null),
229 methodSignatureChangeVisitor);
230 processor.run();
233 return methodSignatureChangeVisitor.getAffectedMethods();
236 private static class MethodSignatureChangeVisitor implements UsageVisitor {
237 private final List<PsiMethod> myAffectedMethods;
239 private MethodSignatureChangeVisitor() {
240 myAffectedMethods = new ArrayList<PsiMethod>();
243 public void addBase(final PsiMethod baseMethod) {
244 myAffectedMethods.add(baseMethod);
247 public void visit(final UsageInfo usage) {
248 if (usage instanceof OverriderUsageInfo) {
249 myAffectedMethods.add(((OverriderUsageInfo) usage).getElement());
253 public List<PsiMethod> getAffectedMethods() {
254 return myAffectedMethods;
257 public void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos) {
258 for (Iterator<UsageInfo> usageInfoIterator = covariantOverriderInfos.iterator(); usageInfoIterator.hasNext();) {
259 final UsageInfo info = usageInfoIterator.next();
260 if (info instanceof OverriderUsageInfo) {
261 final OverriderUsageInfo overrideUsage = (OverriderUsageInfo) info;
262 if (myAffectedMethods.contains(overrideUsage.getElement())) {
263 usageInfoIterator.remove();
270 private interface UsageVisitor {
271 void visit(final UsageInfo usage);
272 void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos);
275 private static class UsagesAwareChangeSignatureProcessor extends ChangeSignatureProcessor {
276 private final UsageVisitor myUsageVisitor;
278 private UsagesAwareChangeSignatureProcessor(final Project project, final PsiMethod method, final boolean generateDelegate,
279 @Modifier final String newVisibility, final String newName, final PsiType newType,
280 @NotNull final ParameterInfoImpl[] parameterInfo, final UsageVisitor usageVisitor) {
281 super(project, method, generateDelegate, newVisibility, newName, newType, parameterInfo);
282 myUsageVisitor = usageVisitor;
285 protected void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos) {
286 myUsageVisitor.preprocessCovariantOverriders(covariantOverriderInfos);
289 protected void performRefactoring(final UsageInfo[] usages) {
290 super.performRefactoring(usages);
292 for (UsageInfo usage : usages) {
293 myUsageVisitor.visit(usage);
298 private static void selectReturnValueInEditor(final PsiReturnStatement returnStatement, final Editor editor) {
299 TextRange range = returnStatement.getReturnValue().getTextRange();
300 int offset = range.getStartOffset();
302 editor.getCaretModel().moveToOffset(offset);
303 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
304 editor.getSelectionModel().setSelection(range.getEndOffset(), range.getStartOffset());