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
) {
60 myReturnType
= returnType
;
64 public String
getText() {
65 return QuickFixBundle
.message("fix.return.type.text",
67 myReturnType
.getCanonicalText());
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
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() {
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)) {
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
);
166 mySelector
.accept(visitor
.createReturnInLastStatement(), method
);
168 catch (AnalysisCanceledException e
) {
172 catch (IncorrectOperationException 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);
191 private static PsiMethod
[] getChangeRoots(final PsiMethod method
) {
192 final PsiMethod
[] methods
= method
.findDeepestSuperMethods();
194 if (methods
.length
> 0) {
195 if (ApplicationManager
.getApplication().isUnitTestMode()) {
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());
206 } else if (0 == result
) {
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) {
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
,
228 RemoveUnusedParameterFix
.getNewParametersInfo(method
, null),
229 methodSignatureChangeVisitor
);
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());