update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / intention / impl / InvertIfConditionAction.java
blobdbdb6975e1e0d7cd92be13fc3dbf7a9c68c230c7
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.
18 * Created by IntelliJ IDEA.
19 * User: mike
20 * Date: Aug 22, 2002
21 * Time: 2:55:23 PM
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com.intellij.codeInsight.intention.impl;
27 import com.intellij.codeInsight.CodeInsightBundle;
28 import com.intellij.codeInsight.CodeInsightServicesUtil;
29 import com.intellij.codeInsight.CodeInsightUtilBase;
30 import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.editor.Editor;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.util.TextRange;
35 import com.intellij.psi.*;
36 import com.intellij.psi.codeStyle.CodeStyleManager;
37 import com.intellij.psi.controlFlow.*;
38 import com.intellij.psi.util.PsiTreeUtil;
39 import com.intellij.util.IncorrectOperationException;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
43 import java.util.List;
45 public class InvertIfConditionAction extends PsiElementBaseIntentionAction {
46 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.InvertIfConditionAction");
48 public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable PsiElement element) {
49 if (element == null) return false;
51 int offset = editor.getCaretModel().getOffset();
52 final PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, PsiIfStatement.class);
53 if (ifStatement == null) return false;
54 final PsiExpression condition = ifStatement.getCondition();
55 if (condition == null) return false;
56 if (ifStatement.getThenBranch() == null) return false;
57 if (element instanceof PsiKeyword) {
58 PsiKeyword keyword = (PsiKeyword) element;
59 if ((keyword.getTokenType() == JavaTokenType.IF_KEYWORD || keyword.getTokenType() == JavaTokenType.ELSE_KEYWORD)
60 && keyword.getParent() == ifStatement) {
61 return true;
64 final TextRange condTextRange = condition.getTextRange();
65 if (condTextRange == null) return false;
66 if (!condTextRange.contains(offset)) return false;
67 PsiElement block = findCodeBlock(ifStatement);
68 return block != null;
71 @NotNull
72 public String getText() {
73 return getFamilyName();
76 @NotNull
77 public String getFamilyName() {
78 return CodeInsightBundle.message("intention.invert.if.condition");
81 public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
82 if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
84 PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(file.findElementAt(
85 editor.getCaretModel().getOffset()), PsiIfStatement.class);
87 LOG.assertTrue(ifStatement != null);
88 PsiElement block = findCodeBlock(ifStatement);
90 ControlFlow controlFlow = buildControlFlow(block);
92 PsiExpression condition = (PsiExpression) ifStatement.getCondition().copy();
94 setupBranches(ifStatement, controlFlow);
95 if (condition != null) {
96 ifStatement.getCondition().replace(CodeInsightServicesUtil.invertCondition(condition));
99 formatIf(ifStatement);
102 private static void formatIf(PsiIfStatement ifStatement) throws IncorrectOperationException {
103 final Project project = ifStatement.getProject();
104 PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
106 PsiElement thenBranch = ifStatement.getThenBranch().copy();
107 PsiElement elseBranch = ifStatement.getElseBranch() != null ? ifStatement.getElseBranch().copy() : null;
108 PsiElement condition = ifStatement.getCondition().copy();
110 final CodeStyleManager codeStyle = CodeStyleManager.getInstance(project);
112 PsiBlockStatement codeBlock = (PsiBlockStatement)factory.createStatementFromText("{}", null);
113 codeBlock = (PsiBlockStatement)codeStyle.reformat(codeBlock);
115 ifStatement.getThenBranch().replace(codeBlock);
116 if (elseBranch != null) {
117 ifStatement.getElseBranch().replace(codeBlock);
119 ifStatement.getCondition().replace(factory.createExpressionFromText("true", null));
120 ifStatement = (PsiIfStatement)codeStyle.reformat(ifStatement);
122 if (!(thenBranch instanceof PsiBlockStatement)) {
123 PsiBlockStatement codeBlock1 = (PsiBlockStatement)ifStatement.getThenBranch().replace(codeBlock);
124 codeBlock1 = (PsiBlockStatement)codeStyle.reformat(codeBlock1);
125 codeBlock1.getCodeBlock().add(thenBranch);
127 else {
128 ifStatement.getThenBranch().replace(thenBranch);
131 if (elseBranch != null) {
132 if (!(elseBranch instanceof PsiBlockStatement)) {
133 PsiBlockStatement codeBlock1 = (PsiBlockStatement)ifStatement.getElseBranch().replace(codeBlock);
134 codeBlock1 = (PsiBlockStatement)codeStyle.reformat(codeBlock1);
135 codeBlock1.getCodeBlock().add(elseBranch);
137 else {
138 elseBranch = ifStatement.getElseBranch().replace(elseBranch);
140 if (emptyBlock(((PsiBlockStatement)elseBranch).getCodeBlock())) {
141 ifStatement.getElseBranch().delete();
146 ifStatement.getCondition().replace(condition);
149 private static boolean emptyBlock (PsiCodeBlock block) {
150 PsiElement[] children = block.getChildren();
151 for (PsiElement child : children) {
152 if (child instanceof PsiComment) return false;
153 if (!(child instanceof PsiWhiteSpace) && !(child instanceof PsiJavaToken)) return false;
155 return true;
158 private static PsiElement findCodeBlock(PsiIfStatement ifStatement) {
159 PsiElement e = PsiTreeUtil.getParentOfType(ifStatement, PsiMethod.class, PsiClassInitializer.class);
160 if (e instanceof PsiMethod) return ((PsiMethod) e).getBody();
161 if (e instanceof PsiClassInitializer) return ((PsiClassInitializer) e).getBody();
162 return null;
165 private static PsiElement findNearestCodeBlock(PsiIfStatement ifStatement) {
166 return PsiTreeUtil.getParentOfType(ifStatement, PsiCodeBlock.class);
169 private static ControlFlow buildControlFlow(PsiElement element) {
170 try {
171 //return new ControlFlowAnalyzer(element, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false, false).buildControlFlow();
172 return ControlFlowFactory.getInstance(element.getProject()).getControlFlow(element, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false);
174 catch (AnalysisCanceledException e) {
175 return ControlFlow.EMPTY;
179 private static void setupBranches(PsiIfStatement ifStatement, ControlFlow flow) throws IncorrectOperationException {
180 PsiElementFactory factory = JavaPsiFacade.getInstance(ifStatement.getProject()).getElementFactory();
181 Project project = ifStatement.getProject();
183 PsiStatement thenBranch = ifStatement.getThenBranch();
184 PsiStatement elseBranch = ifStatement.getElseBranch();
186 if (elseBranch != null) {
187 elseBranch = (PsiStatement) elseBranch.copy();
188 setElseBranch(ifStatement, thenBranch, flow);
189 ifStatement.getThenBranch().replace(elseBranch);
190 return;
193 final CodeStyleManager codeStyle = CodeStyleManager.getInstance(project);
194 if (flow.getSize() == 0) {
195 ifStatement.setElseBranch(thenBranch);
196 PsiStatement statement = factory.createStatementFromText("{}", null);
197 statement = (PsiStatement) codeStyle.reformat(statement);
198 statement = (PsiStatement) ifStatement.getThenBranch().replace(statement);
199 codeStyle.reformat(statement);
200 return;
203 int endOffset = calcEndOffset(flow, ifStatement);
205 LOG.assertTrue(endOffset >= 0);
207 if (endOffset >= flow.getSize()) {
208 PsiStatement statement = factory.createStatementFromText("return;", null);
209 statement = (PsiStatement) codeStyle.reformat(statement);
210 if (thenBranch instanceof PsiBlockStatement) {
211 PsiStatement[] statements = ((PsiBlockStatement) thenBranch).getCodeBlock().getStatements();
212 int len = statements.length;
213 if (len > 0) {
214 if (statements[len - 1] instanceof PsiReturnStatement) len--;
215 if (len > 0) {
216 PsiElement firstElement = statements [0];
217 while (firstElement.getPrevSibling() instanceof PsiWhiteSpace || firstElement.getPrevSibling() instanceof PsiComment) {
218 firstElement = firstElement.getPrevSibling();
220 ifStatement.getParent().addRangeAfter(firstElement, statements[len - 1], ifStatement);
223 } else {
224 if (!(thenBranch instanceof PsiReturnStatement)) {
225 addAfter(ifStatement, thenBranch);
228 ifStatement.getThenBranch().replace(statement);
229 return;
231 PsiElement element = flow.getElement(endOffset);
232 while (element != null && !(element instanceof PsiStatement)) element = element.getParent();
234 if (element != null && element.getParent() instanceof PsiForStatement) {
235 PsiForStatement forStatement = (PsiForStatement) element.getParent();
236 if (forStatement.getUpdate() == element) {
237 PsiStatement statement = factory.createStatementFromText("continue;", null);
238 statement = (PsiStatement) codeStyle.reformat(statement);
239 addAfter(ifStatement, thenBranch);
240 ifStatement.getThenBranch().replace(statement);
241 return;
244 if (element instanceof PsiWhileStatement && flow.getStartOffset(element) == endOffset ||
245 element instanceof PsiForeachStatement && flow.getStartOffset(element) + 1 == endOffset // Foreach doesn't loop on it's first instruction
246 // but rather on second. It only accesses collection initially.
248 PsiStatement statement = factory.createStatementFromText("continue;", null);
249 statement = (PsiStatement) codeStyle.reformat(statement);
250 addAfter(ifStatement, thenBranch);
251 ifStatement.getThenBranch().replace(statement);
252 return;
255 if (element instanceof PsiReturnStatement) {
256 PsiReturnStatement returnStatement = (PsiReturnStatement) element;
257 addAfter(ifStatement, thenBranch);
258 ifStatement.getThenBranch().replace(returnStatement.copy());
260 ControlFlow flow2 = buildControlFlow(findCodeBlock(ifStatement));
261 if (!ControlFlowUtil.isInstructionReachable(flow2, flow2.getStartOffset(returnStatement), 0)) returnStatement.delete();
262 return;
265 boolean nextUnreachable = flow.getEndOffset(ifStatement) == flow.getSize();
266 if (!nextUnreachable) {
267 PsiElement nearestCodeBlock = findNearestCodeBlock(ifStatement);
268 if (nearestCodeBlock != null) {
269 ControlFlow flow2 = buildControlFlow(nearestCodeBlock);
270 nextUnreachable = !ControlFlowUtil.isInstructionReachable(flow2, flow2.getEndOffset(ifStatement), getThenOffset(flow2, ifStatement));
273 if (nextUnreachable) {
274 setElseBranch(ifStatement, thenBranch, flow);
276 PsiElement first = ifStatement.getNextSibling();
277 // while (first instanceof PsiWhiteSpace) first = first.getNextSibling();
278 if (first != null) {
279 PsiElement last = first;
280 PsiElement next = last.getNextSibling();
281 while (next != null && !(next instanceof PsiSwitchLabelStatement)) {
282 last = next;
283 next = next.getNextSibling();
285 while (first != last && (last instanceof PsiWhiteSpace ||
286 last instanceof PsiJavaToken && ((PsiJavaToken) last).getTokenType() == JavaTokenType.RBRACE))
287 last = last.getPrevSibling();
290 PsiBlockStatement codeBlock = (PsiBlockStatement) factory.createStatementFromText("{}", null);
291 codeBlock.getCodeBlock().addRange(first, last);
292 first.getParent().deleteChildRange(first, last);
293 ifStatement.getThenBranch().replace(codeBlock);
295 codeStyle.reformat(ifStatement);
296 return;
299 setElseBranch(ifStatement, thenBranch, flow);
300 PsiStatement statement = factory.createStatementFromText("{}", null);
301 statement = (PsiStatement) codeStyle.reformat(statement);
302 statement = (PsiStatement) ifStatement.getThenBranch().replace(statement);
303 codeStyle.reformat(statement);
306 private static void setElseBranch(PsiIfStatement ifStatement, PsiStatement thenBranch, ControlFlow flow) throws IncorrectOperationException {
307 if (flow.getEndOffset(ifStatement) == flow.getEndOffset(thenBranch)) {
308 if (thenBranch instanceof PsiContinueStatement) {
309 PsiStatement elseBranch = ifStatement.getElseBranch();
310 if (elseBranch != null) {
311 elseBranch.delete();
313 return;
315 else if (thenBranch instanceof PsiBlockStatement) {
316 PsiStatement[] statements = ((PsiBlockStatement) thenBranch).getCodeBlock().getStatements();
317 if (statements.length > 0 && statements[statements.length - 1] instanceof PsiContinueStatement) {
318 statements[statements.length - 1].delete();
322 ifStatement.setElseBranch(thenBranch);
325 private static void addAfter(PsiIfStatement ifStatement, PsiStatement thenBranch) throws IncorrectOperationException {
326 if (thenBranch instanceof PsiBlockStatement) {
327 PsiBlockStatement blockStatement = (PsiBlockStatement) thenBranch;
328 PsiStatement[] statements = blockStatement.getCodeBlock().getStatements();
329 if (statements.length > 0) {
330 ifStatement.getParent().addRangeAfter(statements[0], statements[statements.length - 1], ifStatement);
332 } else {
333 ifStatement.getParent().addAfter(thenBranch, ifStatement);
337 private static int getThenOffset(ControlFlow controlFlow, PsiIfStatement ifStatement) {
338 PsiStatement thenBranch = ifStatement.getThenBranch();
340 for (int i = 0; i < controlFlow.getSize(); i++) {
341 if (PsiTreeUtil.isAncestor(thenBranch, controlFlow.getElement(i), false)) return i;
343 return -1;
346 private static int calcEndOffset(ControlFlow controlFlow, PsiIfStatement ifStatement) {
347 int endOffset = -1;
349 List<Instruction> instructions = controlFlow.getInstructions();
350 for (int i = 0; i < instructions.size(); i++) {
351 Instruction instruction = instructions.get(i);
352 if (controlFlow.getElement(i) != ifStatement) continue;
354 if (instruction instanceof GoToInstruction) {
355 GoToInstruction goToInstruction = (GoToInstruction)instruction;
356 if (goToInstruction.role != BranchingInstruction.Role.END) continue;
358 endOffset = goToInstruction.offset;
359 break;
361 else if (instruction instanceof ConditionalGoToInstruction) {
362 ConditionalGoToInstruction goToInstruction = (ConditionalGoToInstruction)instruction;
363 if (goToInstruction.role != BranchingInstruction.Role.END) continue;
365 endOffset = goToInstruction.offset;
366 break;
369 if (endOffset == -1) {
370 endOffset = controlFlow.getSize();
372 while (endOffset < instructions.size() && instructions.get(endOffset) instanceof GoToInstruction && !((GoToInstruction) instructions.get(endOffset)).isReturn) {
373 endOffset = ((BranchingInstruction)instructions.get(endOffset)).offset;
376 return endOffset;